Compare commits

...

106 Commits

Author SHA1 Message Date
Electron Bot
1ebd9761fb Bump v2.0.13 2018-10-31 16:12:36 -07:00
trop[bot]
67c7554b80 docs: add return type for subscribeNotification (#15501) 2018-10-31 09:48:25 -07:00
trop[bot]
d158cce9a3 doc: clarify menu item properties not available top-level (#15460) 2018-10-29 16:23:48 -07:00
trop[bot]
5a73ec2ec8 fix: honor dialog.showMessageBox()'s Icon argument on Linux (backport: 2-0-x) (#15341)
* Don't call gtk_widget_show_all() on popup dialog.

Fixes #15317.

Notes: Fixed incorrect display of some GtkMessageDialog icons.

The issue is caused because GtkMessageDialog contains an icon widget
which is not shown when there's no associated icon. Our call to
`gtk_widget_show_all()` overrides this, showing the uninitialized
icon widget.

This PR fixes the issue by calling `gtk_widget_show()` where needed
and removing use of `gtk_widget_show_all()` in the message dialog.

* use gtk_dialog_set_default_response() for default

* fix: support icons on gtk+ messageboxes.
2018-10-23 12:00:50 -07:00
Michelle Tilley
a03da04578 fix: correct reversed logic in NativeWindowMac::SetEnabled (#15322)
Backport of #15257
2018-10-23 23:22:07 +11:00
trop[bot]
d45c29087b docs: we don't emit an event object for session-created (#15306) 2018-10-21 10:17:37 -07:00
trop[bot]
00200c707e chore: make macOS release builds higher priority to skip the queue (#15283)
Release builds should be run before branch builds on our limited macOS
infra.

Refs: https://docs.microsoft.com/en-us/rest/api/vsts/build/builds/queue?view=vsts-rest-4.1#queuepriority
2018-10-19 09:12:38 -07:00
trop[bot]
b228166900 fix: make release-artifact-cleanup executable (backport: 2-0-x) (#15270)
* fix: make release-artifact-cleanup executable

* fix misc issues in cleanup script
2018-10-18 20:27:45 -07:00
Electron Bot
d281859cf5 Bump v2.0.12 2018-10-18 17:04:22 -07:00
Samuel Attard
e00d217cfb Revert "build: enable PIE when compiling Linux builds, fix #14961. (#15148)"
This reverts commit 2f2761fa47.
2018-10-18 14:12:33 +11:00
Samuel Attard
00f40e84db Revert "chore: enable BIND_NOW when compiling Linux builds"
This reverts commit 9f1e674f30.
2018-10-18 14:12:27 +11:00
Samuel Attard
dde5b3abe6 Revert "Bump v2.0.12"
This reverts commit 97bffde34c.
2018-10-18 14:12:00 +11:00
Electron Bot
97bffde34c Bump v2.0.12 2018-10-17 19:33:51 -07:00
Samuel Attard
a6a1cc3bbb Revert "Bump v2.0.12"
This reverts commit 22e71497ec.
2018-10-18 13:27:04 +11:00
Electron Bot
22e71497ec Bump v2.0.12 2018-10-17 10:29:11 -07:00
Roller Bot
a32529b8fa chore: bump libcc submodule to ccdb085454b0a387ee96e0f81a7ca9a8ce07a710 (#15200) 2018-10-18 00:54:36 +11:00
Cheng Zhao
732f053acd Merge pull request #15207 from electron/window-open-warn-leak-2-0-x
chore: warn memory leak when using nativeWindowOpen with nodeIntegration (2-0-x)
2018-10-17 11:54:32 +09:00
Cheng Zhao
90fc709194 fix: print warning after DOM is created 2018-10-17 10:49:08 +09:00
Cheng Zhao
1d2b926962 chore: warn memory leak when using nativeWindowOpen with nodeIntegration 2018-10-17 10:47:45 +09:00
Cheng Zhao
fbc7b7f8ad fix: do not enable node integration in child window if not enabled (#15109) 2018-10-16 18:10:58 +09:00
Kevin Lynagh
4279f2f3f8 fix: simpleFullscreen window should be on top of other OS X menu bars. (#15182)
If an app has no menu bar (because `app.dock.hide()` has been called),
OS X will still render the menu bar of the previously-focused app.

This commit ensures simpleFullscreen windows will be drawn on top of
that menu bar by setting their level to NSPopUpMenuWindowLevel while
simpleFullscreen mode is active.

Ref: #11468
2018-10-16 11:42:13 +11:00
Shelley Vohr
b8eec43b31 fix: natively implement LoginItem functions (backport: 2-0-x) (#15139)
* fix: natively implement LoginItem functions

* equal => deepEqual
2018-10-15 12:55:09 -07:00
trop[bot]
ce30d6c11f fix: Memory > Profiles > Load in DevTools (#15154) 2018-10-14 15:15:47 -07:00
#DeleteGithub
9f1e674f30 chore: enable BIND_NOW when compiling Linux builds
We've hardened Linux builds by enabling PIE and RELRO,
and should continue to try hardening Linux builds by
enabling BIND_NOW. With both RELRO and BIND_NOW enabled,
we can stop all GOT overwrite attacks. The same hardening
option has been enabled in official Chrome/Chromium
builds since more than five years ago.

This helps to improve the security of a whole range of
applications built upon Electron, including sensetive ones
such as Signal-Desktop.

Signed-off-by: Tom Li <tomli@tomli.me>
2018-10-14 11:13:37 -07:00
#DeleteGithub
2f2761fa47 build: enable PIE when compiling Linux builds, fix #14961. (#15148)
PIE allows an application to utilize the full benefits of ASLR
to prevent itself from exploitations, but it was disabled for
all released versions of Electron (3.0 and prior).

Currently, PIE is already enabled since 9294fac but enabling it
for all released version is still an ongoing work (#14961). This
patch backports PIE to the 2.0.x branch, which is an especially
important branch, since security is an urgency for many existing
programs including Signal-Desktop. Enabling it can help protect
many high-risk users.

Signed-off-by: Tom Li <tomli@tomli.me>
2018-10-14 12:47:45 +11:00
Charles Kerr
411f2be579 Check dbus response for null before using. (#15088)
Fixes #14958.

Manual backport of #15030. Drops some the BindOnce improvements
due to compile issues with older version of Chromium.
2018-10-11 22:01:58 -07:00
Charles Kerr
28a160fc1f fix: SetSize() should honor minSize (#13994) (#15052)
Manual backport of #14931 to fix #13994.
2018-10-10 16:18:04 -05:00
Troy
2663d3e10d Backport of #14648 (#15025) 2018-10-08 20:18:02 -05:00
trop[bot]
e55d7d6f1e fix: support ASAR in fs.copyFile (#14952) 2018-10-04 09:23:07 +10:00
Cheng Zhao
092796f5f4 fix: use white background for non-OSR renderer by default (#14933) 2018-10-02 20:08:49 -07:00
Electron Bot
3f6d86c872 Bump v2.0.11 2018-10-01 09:47:39 -07:00
Samuel Attard
bdc9faa083 Revert "Bump v2.0.11"
This reverts commit 3e35f77da4.
2018-10-02 02:42:49 +10:00
Electron Bot
3e35f77da4 Bump v2.0.11 2018-10-01 08:38:40 -07:00
Roller Bot
464f144a2b chore: bump libcc submodule to e271f9ba29ff73167bbc4a7e125657caccb32960 (#14839) 2018-09-28 10:30:16 -07:00
trop[bot]
8f92396ed4 fix: enable key accelerator flags for Windows and Linux (backport: 2-0-x) (#14858)
* Fix Accelerator Flags for Windows and Linux

* Lint fix

* Lint whitespace
2018-09-28 10:01:09 -07:00
Shelley Vohr
4e41a79404 fix: get background color from GtkMenuBar#menubar (#14813) 2018-09-27 17:44:58 +10:00
trop[bot]
d3c153e85a docs: correct key code reqs for accel doc (#14825) 2018-09-27 12:49:58 +10:00
Shelley Vohr
503b488153 fix: inconsistent titleBarStyle on transparent fullscreen (#14792) 2018-09-25 09:58:32 -04:00
trop[bot]
005a65da79 docs: clarify allowable background colors for BrowserWindow transparency (backport: 2-0-x) (#14780)
* docs: clarify allowable colors for transparency

* fix alpha value specification
2018-09-24 09:39:53 -07:00
trop[bot]
36da7dcd5f docs: specify BrowserWindow features passable to window.open (#14701) 2018-09-20 16:56:11 +10:00
trop[bot]
0e5f734a1a chore: fix await in cleanup script (#14694) 2018-09-19 10:10:20 -07:00
John Kleinschmidt
3aeaf10e64 ci: Skip VSTS testing builds on older branches (#14668)
Also, don't send slack notifications for actual releases
2018-09-19 09:55:48 -07:00
Shelley Vohr
d70942190c fix: quit properly in simpleFullScreen mode (#14671) 2018-09-19 08:31:08 -07:00
Electron Bot
ac400e5cb9 Bump v2.0.10 2018-09-19 02:18:01 -07:00
Samuel Attard
333d5fb5d2 Revert "Bump v2.0.10"
This reverts commit f2c12ccbf5.
2018-09-19 19:10:43 +10:00
Electron Bot
f2c12ccbf5 Bump v2.0.10 2018-09-18 06:45:51 -07:00
trop[bot]
79af1ea6f1 chore: dont pass --stable through to bump-version.py (#14667)
I want to clean up this "stable" and "beta" code smell eventually but
for now this will unblock the 2.0.x releases.
2018-09-18 23:41:20 +10:00
Roller Bot
1ed7875b0b chore: bump libcc submodule to cbd04c0dccc7655cd42f02baee3a622d5170ac08 (#14654) 2018-09-18 17:37:00 +10:00
John Kleinschmidt
67a690e536 Merge pull request #14593 from electron/ignore-gn-debug-builds-2-0-x
ci: don't run gn debug build on older branches (2-0-x)
2018-09-13 09:24:02 -04:00
John Kleinschmidt
5f3bedd1e0 ci: don't run gn debug build on older branches (#14584)
* ci: don't run gn debug build on older branches

Older branches that build using gyp do not run both a debug and testing build.

* Actually skip the build if debug

(cherry picked from commit f924a16055)
2018-09-12 17:10:05 -04:00
Electron Bot
670a875792 Bump v2.0.9 2018-09-10 08:21:03 -07:00
trop[bot]
faf82245dc Fix documentation tyop (#14477) 2018-09-06 10:59:35 -07:00
Shelley Vohr
066eeb5b5f chore: add release-artifact-cleanup script (#14448) 2018-09-04 11:36:57 -07:00
trop[bot]
77ec86b894 docs: added session-created event documentation (backport: 2-0-x) (#14439)
* Added session-created event documentation

Emitted at 1c0ea0286e/lib/browser/api/session.js (L21)

* Removed trailing spaces
2018-09-04 09:26:58 +10:00
trop[bot]
bf82dc7896 fix: crash when tray is destroyed (#14366)
Release the view of status item before destroying it,
gives chance to perform cleanup on the view.
2018-08-28 23:14:08 -07:00
Charles Kerr
b4c5a30469 fix: dont parse arguments after a -- in the inspector (#14297) (#14334) 2018-08-27 14:39:38 -05:00
trop[bot]
7ed6e2b909 fix: don't crash on tray.setContextMenu(null) (#14330) 2018-08-27 12:51:09 -05:00
Wenjun Che
8edd18be90 fix: issue 10962, crash when open devtool (#13808)
* fix: crash when opening devtool (#10962)

* fix: fixed linting issues
2018-08-24 14:36:47 -07:00
Shelley Vohr
3bc9ae7f5b deps: update node for two backports (#14298) 2018-08-24 14:50:55 -05:00
Cheng Zhao
760c2327b4 fix: do not bubble up resize event from webview (#14272) 2018-08-23 15:42:46 +09:00
Cheng Zhao
fcb5069d24 Revert "fix: do not bubble up resize event from webview"
This reverts commit 221ab080a1.

It was an accidental push.
2018-08-23 09:31:10 +09:00
Cheng Zhao
221ab080a1 fix: do not bubble up resize event from webview 2018-08-23 09:29:08 +09:00
Electron Bot
addf069f26 Bump v2.0.8 2018-08-22 11:10:51 -07:00
Samuel Attard
80221e52d9 fix: inheritance of webPreferences sub properties 2018-08-22 12:20:04 -05:00
trop[bot]
3403b4a4de ci: add better logging around request failures for releasing (#14244) 2018-08-21 13:31:29 -07:00
trop[bot]
a1ac930f4d chore: retry for the meta dumper a few times (#14242) 2018-08-21 13:30:53 -07:00
Electron Bot
ed8396c6f5 Bump v2.0.8-nightly.20180820 2018-08-20 16:54:02 -07:00
Samuel Attard
a457d8823a Revert "Bump v2.0.8-nightly.20180820"
This reverts commit 2d44dcb8be.
2018-08-20 16:44:17 -07:00
Electron Bot
2d44dcb8be Bump v2.0.8-nightly.20180820 2018-08-20 13:33:19 -07:00
Samuel Attard
85da731867 fix: support installing nightlies on <= 2.0.x (#14224) 2018-08-20 11:18:45 -07:00
trop[bot]
3af5fdb831 chore: remove autorelease check logic (#14221) 2018-08-20 10:56:40 -07:00
trop[bot]
f1b197acbb chore: add option to return next version from prepare-release (backport: 2-0-x) (#14216)
* chore: add option to return next version from prepare-release

* shuffle logic
2018-08-20 08:35:28 -07:00
Electron Bot
d483c81887 Bump v2.0.8-nightly.20180819 2018-08-19 16:42:46 -07:00
Samuel Attard
3ef980ae2b chore: use metadumper service to create index.json file (#14158) (#14210) 2018-08-19 16:40:49 -07:00
Samuel Attard
36e68b46df Revert "Bump v2.0.8-nightly.20180819"
This reverts commit 148d0d8d29.
2018-08-19 15:38:56 -07:00
Samuel Attard
f611dfdb5c chore: stop auto releasing from windows CI 2018-08-19 15:38:47 -07:00
Electron Bot
148d0d8d29 Bump v2.0.8-nightly.20180819 2018-08-19 14:48:54 -07:00
trop[bot]
79a1382126 chore: match the bump commit exactly, reverts should not count (#14208) 2018-08-19 14:45:49 -07:00
Samuel Attard
b0d5ba1996 Revert "Bump v2.0.8-nightly.20180819"
This reverts commit 48a4b58cc1.
2018-08-19 14:24:11 -07:00
Samuel Attard
088dc70dae chore: disable tests on release builds, this is already done on master/3-0-x (#14206) 2018-08-19 14:22:57 -07:00
trop[bot]
ad686cca68 fix: #14160 (#14204) 2018-08-19 14:13:56 -07:00
Electron Bot
48a4b58cc1 Bump v2.0.8-nightly.20180819 2018-08-19 13:18:10 -07:00
Samuel Attard
c1ef824e4c chore: fix upload script for nightly support (#14203) 2018-08-19 13:03:15 -07:00
Samuel Attard
a2eb532720 Revert "Bump v2.0.8-nightly.20180819"
This reverts commit 0cb53ceb9d.
2018-08-19 12:57:00 -07:00
Electron Bot
0cb53ceb9d Bump v2.0.8-nightly.20180819 2018-08-19 12:33:38 -07:00
Samuel Attard
afe4e610ae chore: always target master for nightly releases (#14190) (#14202) 2018-08-19 12:28:21 -07:00
Shelley Vohr
74d90fbb33 chore: backport release script updates to 2-0-x (#14191)
* chore: alter release scripts to enable sudowoodo

* add example .env file

* chore: only prepare release if last commit not a bump (#14193)
2018-08-18 19:39:06 -07:00
trop[bot]
f3bd8f6133 docs: fix electron.d.ts typings (#14137) 2018-08-16 11:11:31 -07:00
John Kleinschmidt
863c511fbf Merge pull request #13988 from K900/update-lld
fix: update clang/lld version to fix #13972
2018-08-16 10:29:21 -07:00
trop[bot]
03d8689ded fix: add a hidden option to disable remote dereferencing (#14112) 2018-08-15 08:43:41 -07:00
John Kleinschmidt
ff2bc1d705 Merge pull request #14084 from electron/appveyor-rename-2-0-x
ci: rename appveyor-override to appveyor (2-0-x)
2018-08-14 09:56:17 -07:00
John Kleinschmidt
a1df8befde Rename appveyor-override to appveyor 2018-08-14 09:44:15 -07:00
trop[bot]
75909685f7 Fix flaky sandbox tests (#14055)
Windows apparently has issues running opening two windows at once which is causing flakiness on the mixed sandbox tests.
2018-08-13 15:14:21 -07:00
trop[bot]
2e0a015168 fix: make asarStatsToFsStats nherit from fs.stats (#14040) 2018-08-12 21:13:43 -07:00
K900
740b2797c5 vendor: update clang/lld version to fix #13972
This seems like some sort of a weird linker bug, so just update the
toolchain a bit.
2018-08-10 14:55:13 +03:00
Charles Kerr
5f372f7ecc Bump v2.0.7 2018-08-08 16:23:52 -05:00
Jeremy Apthorp
e4f4a1d9f9 chore: roll libcc (#13974)
Picks up electron/libchromiumcontent#637
2018-08-08 09:59:16 -07:00
John Kleinschmidt
bdc255fa9e Merge pull request #13966 from electron/2-0-x-backport-12809
fix: Prevent menu update while it's open (backport: 2-0-x)
2018-08-07 10:05:44 -04:00
John Kleinschmidt
e8e542b3e8 Merge pull request #13960 from electron/miniak/fix-promisify-2.0
fix: some APIs modified for ASAR support cannot be util.promisify'ed (backport: 2-0-x)
2018-08-07 10:04:56 -04:00
Zhuo Lu
dd89aa6c77 Memory safety 2018-08-06 23:50:00 -07:00
Zhuo Lu
79caff69f5 Fix code style 2018-08-06 23:50:00 -07:00
Zhuo Lu
67b10135b6 Update application menu on default runloop
Menu change should be prevented while the menu is open
2018-08-06 23:49:57 -07:00
Milan Burda
6a86831b7c fix: some APIs modified for ASAR support cannot be util.promisify'ed (#13845) 2018-08-07 00:20:54 +02:00
Birunthan Mohanathas
dffd17ab70 fix: Improve --enable-features/--disable-features handling (2.0.x) (#13921)
As it turns out, we can reinitialize the feature list directly after the
user JS script has been executed instead of much later. This allows
modifications to `--enable-features`/`--disable-features` to work with a
greater set of features.

This backports #13920 to `2-0-x`.
2018-08-03 11:09:53 +10:00
Birunthan Mohanathas
cf98934dd9 build: Bump libcc to latest (2.0.x) (#13919)
This picks up electron/libchromiumcontent#627.
2018-08-03 10:54:39 +10:00
trop[bot]
2fff138740 fix: handle SIGINT and SIGTERM from the Electron CLI helper (#13888)
Fixes #12840
2018-08-01 13:25:37 +10:00
70 changed files with 1774 additions and 386 deletions

7
.env.example Normal file
View File

@@ -0,0 +1,7 @@
# These env vars are only necessary for creating Electron releases.
# See docs/development/releasing.md
APPVEYOR_TOKEN=
CIRCLE_TOKEN=
ELECTRON_GITHUB_TOKEN=
VSTS_TOKEN=

View File

@@ -2,7 +2,9 @@ build_cloud: electron-16
image: electron-16-vs2015
build_script:
- ps: >-
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
if($env:SKIP_GYP_BUILD -eq "true") {
Write-warning "Skipping debug build for older branch"; Exit-AppveyorBuild
} elseif(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild
} else {
Add-Path "$env:ProgramFiles (x86)\Windows Kits\10\Debuggers\x64"
@@ -55,4 +57,13 @@ artifacts:
- path: test-results.xml
name: test-results.xml
deploy_script:
- ps: "if (Test-Path Env:\\ELECTRON_RELEASE) {\n if (Test-Path Env:\\RUN_RELEASE_BUILD) {\n Write-Output \"Uploading Electron release distribution to s3\"\n & python script\\upload.py --upload_to_s3\n } else {\n Write-Output \"Uploading Electron release distribution to github releases\"\n & python script\\upload.py\n if (Test-Path Env:\\AUTO_RELEASE) {\n node script\\release.js --validateRelease --automaticRelease\n if ($? -eq 'True') {\n echo 'Release is ready to go; now running release'\n node script\\release.js --automaticRelease\n if ($? -eq 'True') { \n echo 'Release successful, now publishing to npm' \n $npmfile = \"$HOME\\.npmrc\"\n \"//registry.npmjs.org/:_authToken=$env:ELECTRON_NPM_TOKEN\" > $npmfile\n npm run publish-to-npm\n }\n } else {\n echo 'Release is not complete, skipping publish for now.'\n }\n }\n }\n} else {\n Write-Output \"Skipping upload distribution because build is not for release\"\n}"
- ps: >-
if (Test-Path Env:\ELECTRON_RELEASE) {
if (Test-Path Env:\RUN_RELEASE_BUILD) {
Write-Output "Uploading Electron release distribution to s3"
& python script\upload.py --upload_to_s3
} else {
Write-Output "Uploading Electron release distribution to github releases"
& python script\upload.py
}
}

View File

@@ -18,6 +18,12 @@
using content::BrowserThread;
namespace {
static scoped_nsobject<NSMenu> applicationMenu_;
} // namespace
namespace atom {
namespace api {
@@ -135,7 +141,18 @@ void Menu::SetApplicationMenu(Menu* base_menu) {
base::scoped_nsobject<AtomMenuController> menu_controller(
[[AtomMenuController alloc] initWithModel:menu->model_.get()
useDefaultAccelerator:YES]);
[NSApp setMainMenu:[menu_controller menu]];
NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
[currentRunLoop cancelPerformSelector:@selector(setMainMenu:)
target:NSApp
argument:applicationMenu_];
applicationMenu_.reset([[menu_controller menu] retain]);
[[NSRunLoop currentRunLoop]
performSelector:@selector(setMainMenu:)
target:NSApp
argument:applicationMenu_
order:0
modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
// Ensure the menu_controller_ is destroyed after main menu is set.
menu_controller.swap(menu->menu_controller_);

View File

@@ -206,7 +206,7 @@ void Tray::PopUpContextMenu(mate::Arguments* args) {
void Tray::SetContextMenu(v8::Isolate* isolate, mate::Handle<Menu> menu) {
menu_.Reset(isolate, menu.ToV8());
tray_icon_->SetContextMenu(menu->model());
tray_icon_->SetContextMenu(menu.IsEmpty() ? nullptr : menu->model());
}
gfx::Rect Tray::GetBounds() {

View File

@@ -310,7 +310,8 @@ WebContents::WebContents(v8::Isolate* isolate,
type_(type),
request_id_(0),
background_throttling_(true),
enable_devtools_(true) {
enable_devtools_(true),
is_dom_ready_(false) {
const mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);
if (type == REMOTE) {
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
@@ -799,8 +800,10 @@ void WebContents::DidChangeThemeColor(SkColor theme_color) {
void WebContents::DocumentLoadedInFrame(
content::RenderFrameHost* render_frame_host) {
if (!render_frame_host->GetParent())
if (!render_frame_host->GetParent()) {
is_dom_ready_ = true;
Emit("dom-ready");
}
}
void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
@@ -822,6 +825,7 @@ void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host,
}
void WebContents::DidStartLoading() {
is_dom_ready_ = false;
Emit("did-start-loading");
}
@@ -1343,6 +1347,10 @@ bool WebContents::IsAudioMuted() {
return web_contents()->IsAudioMuted();
}
bool WebContents::IsDOMReady() const {
return is_dom_ready_;
}
void WebContents::Print(mate::Arguments* args) {
PrintSettings settings = { false, false, base::string16() };
if (args->Length() >= 1 && !args->GetNext(&settings)) {
@@ -1934,6 +1942,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
&WebContents::SetIgnoreMenuShortcuts)
.SetMethod("setAudioMuted", &WebContents::SetAudioMuted)
.SetMethod("isAudioMuted", &WebContents::IsAudioMuted)
.SetMethod("isDomReady", &WebContents::IsDOMReady)
.SetMethod("undo", &WebContents::Undo)
.SetMethod("redo", &WebContents::Redo)
.SetMethod("cut", &WebContents::Cut)

View File

@@ -125,6 +125,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
void SetIgnoreMenuShortcuts(bool ignore);
void SetAudioMuted(bool muted);
bool IsAudioMuted();
bool IsDOMReady() const;
void Print(mate::Arguments* args);
std::vector<printing::PrinterBasicInfo> GetPrinterList();
void SetEmbedder(const WebContents* embedder);
@@ -430,6 +431,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
// Whether to enable devtools.
bool enable_devtools_;
// Whether page's document is ready.
bool is_dom_ready_;
DISALLOW_COPY_AND_ASSIGN(WebContents);
};

View File

@@ -450,9 +450,11 @@ gfx::Rect Window::GetContentBounds() {
}
void Window::SetSize(int width, int height, mate::Arguments* args) {
gfx::Size size = window_->GetMinimumSize();
size.SetToMax(gfx::Size(width, height));
bool animate = false;
args->GetNext(&animate);
window_->SetSize(gfx::Size(width, height), animate);
window_->SetSize(size, animate);
}
std::vector<int> Window::GetSize() {

View File

@@ -151,6 +151,14 @@ void AtomBrowserMainParts::PostEarlyInitialization() {
// Wrap the uv loop with global env.
node_bindings_->set_uv_env(env);
// We already initialized the feature list in
// brightray::BrowserMainParts::PreEarlyInitialization(), but
// the user JS script would not have had a chance to alter the command-line
// switches at that point. Lets reinitialize it here to pick up the
// command-line changes.
base::FeatureList::ClearInstanceForTesting();
brightray::BrowserMainParts::InitializeFeatureList();
}
int AtomBrowserMainParts::PreCreateThreads() {

View File

@@ -215,6 +215,65 @@ Browser::LoginItemSettings Browser::GetLoginItemSettings(
return settings;
}
// copied from GetLoginItemForApp in base/mac/mac_util.mm
LSSharedFileListItemRef GetLoginItemForApp() {
base::ScopedCFTypeRef<LSSharedFileListRef> login_items(
LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
if (!login_items.get()) {
LOG(ERROR) << "Couldn't get a Login Items list.";
return NULL;
}
base::scoped_nsobject<NSArray> login_items_array(
base::mac::CFToNSCast(LSSharedFileListCopySnapshot(login_items, NULL)));
NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]];
for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
LSSharedFileListItemRef item =
reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
CFURLRef item_url_ref = NULL;
if (LSSharedFileListItemResolve(item, 0, &item_url_ref, NULL) == noErr &&
item_url_ref) {
base::ScopedCFTypeRef<CFURLRef> item_url(item_url_ref);
if (CFEqual(item_url, url)) {
CFRetain(item);
return item;
}
}
}
return NULL;
}
void RemoveFromLoginItems() {
base::ScopedCFTypeRef<LSSharedFileListRef> list(
LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
if (!list) {
LOG(ERROR) << "Unable to access shared file list";
return;
}
if (GetLoginItemForApp() != NULL) {
base::scoped_nsobject<NSArray> login_items_array(
base::mac::CFToNSCast(LSSharedFileListCopySnapshot(list, NULL)));
if (!login_items_array) {
LOG(ERROR) << "No items in list of auto-loaded apps";
return;
}
for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
LSSharedFileListItemRef item =
reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
CFURLRef url_ref = NULL;
if (LSSharedFileListItemResolve(item, 0, &url_ref, NULL) == noErr &&
item) {
base::ScopedCFTypeRef<CFURLRef> url(url_ref);
if ([[base::mac::CFToNSCast(url.get()) path]
hasPrefix:[[NSBundle mainBundle] bundlePath]])
LSSharedFileListItemRemove(list, item);
}
}
}
}
void Browser::SetLoginItemSettings(LoginItemSettings settings) {
#if defined(MAS_BUILD)
platform_util::SetLoginItemEnabled(settings.open_at_login);
@@ -222,7 +281,7 @@ void Browser::SetLoginItemSettings(LoginItemSettings settings) {
if (settings.open_at_login)
base::mac::AddToLoginItems(settings.open_as_hidden);
else
base::mac::RemoveFromLoginItems();
RemoveFromLoginItems();
#endif
}

View File

@@ -6,6 +6,7 @@
#include <unistd.h>
#include <uv.h>
#include <iostream>
#include <utility>
#include "base/bind.h"
#include "device/bluetooth/dbus/dbus_thread_manager_linux.h"
@@ -23,7 +24,7 @@ std::string get_executable_basename() {
if (!uv_exepath(buf, &buf_size)) {
rv = strrchr(static_cast<const char*>(buf), '/') + 1;
}
return std::move(rv);
return rv;
}
} // namespace
@@ -32,39 +33,37 @@ namespace atom {
PowerObserverLinux::PowerObserverLinux()
: lock_owner_name_(get_executable_basename()), weak_ptr_factory_(this) {
auto dbus_thread_manager = bluez::DBusThreadManagerLinux::Get();
if (dbus_thread_manager) {
bus_ = dbus_thread_manager->GetSystemBus();
if (bus_) {
logind_ = bus_->GetObjectProxy(kLogindServiceName,
dbus::ObjectPath(kLogindObjectPath));
logind_->WaitForServiceToBeAvailable(
base::Bind(&PowerObserverLinux::OnLoginServiceAvailable,
weak_ptr_factory_.GetWeakPtr()));
} else {
LOG(WARNING) << "Failed to get system bus connection";
}
} else {
LOG(WARNING) << "DBusThreadManagerLinux instance isn't available";
auto* bus = bluez::DBusThreadManagerLinux::Get()->GetSystemBus();
if (!bus) {
LOG(WARNING) << "Failed to get system bus connection";
return;
}
// set up the logind proxy
const auto weakThis = weak_ptr_factory_.GetWeakPtr();
logind_ = bus->GetObjectProxy(kLogindServiceName,
dbus::ObjectPath(kLogindObjectPath));
logind_->ConnectToSignal(
kLogindManagerInterface, "PrepareForShutdown",
base::BindRepeating(&PowerObserverLinux::OnPrepareForShutdown, weakThis),
base::BindRepeating(&PowerObserverLinux::OnSignalConnected, weakThis));
logind_->ConnectToSignal(
kLogindManagerInterface, "PrepareForSleep",
base::BindRepeating(&PowerObserverLinux::OnPrepareForSleep, weakThis),
base::BindRepeating(&PowerObserverLinux::OnSignalConnected, weakThis));
logind_->WaitForServiceToBeAvailable(base::BindRepeating(
&PowerObserverLinux::OnLoginServiceAvailable, weakThis));
}
PowerObserverLinux::~PowerObserverLinux() = default;
void PowerObserverLinux::OnLoginServiceAvailable(bool service_available) {
if (!service_available) {
LOG(WARNING) << kLogindServiceName << " not available";
return;
}
// Connect to PrepareForShutdown/PrepareForSleep signals
logind_->ConnectToSignal(kLogindManagerInterface, "PrepareForShutdown",
base::Bind(&PowerObserverLinux::OnPrepareForShutdown,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&PowerObserverLinux::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
logind_->ConnectToSignal(kLogindManagerInterface, "PrepareForSleep",
base::Bind(&PowerObserverLinux::OnPrepareForSleep,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&PowerObserverLinux::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
// Take sleep inhibit lock
BlockSleep();
}
@@ -120,8 +119,10 @@ void PowerObserverLinux::SetShutdownHandler(base::Callback<bool()> handler) {
void PowerObserverLinux::OnInhibitResponse(base::ScopedFD* scoped_fd,
dbus::Response* response) {
dbus::MessageReader reader(response);
reader.PopFileDescriptor(scoped_fd);
if (response != nullptr) {
dbus::MessageReader reader(response);
reader.PopFileDescriptor(scoped_fd);
}
}
void PowerObserverLinux::OnPrepareForSleep(dbus::Signal* signal) {
@@ -156,7 +157,7 @@ void PowerObserverLinux::OnPrepareForShutdown(dbus::Signal* signal) {
}
}
void PowerObserverLinux::OnSignalConnected(const std::string& interface,
void PowerObserverLinux::OnSignalConnected(const std::string& /*interface*/,
const std::string& signal,
bool success) {
LOG_IF(WARNING, !success) << "Failed to connect to " << signal;

View File

@@ -19,6 +19,7 @@ namespace atom {
class PowerObserverLinux : public base::PowerObserver {
public:
PowerObserverLinux();
~PowerObserverLinux() override;
protected:
void BlockSleep();
@@ -39,7 +40,6 @@ class PowerObserverLinux : public base::PowerObserver {
base::Callback<bool()> should_shutdown_;
scoped_refptr<dbus::Bus> bus_;
scoped_refptr<dbus::ObjectProxy> logind_;
std::string lock_owner_name_;
base::ScopedFD sleep_lock_;

View File

@@ -213,6 +213,7 @@ class NativeWindowMac : public NativeWindow,
bool was_maximizable_;
bool was_movable_;
NSRect original_frame_;
NSInteger original_level_;
NSUInteger simple_fullscreen_mask_;
base::scoped_nsobject<NSColor> background_color_before_vibrancy_;

View File

@@ -380,6 +380,7 @@ bool ScopedDisableResize::disable_resize_ = false;
// Set window style to hide the toolbar, otherwise the toolbar will show in
// fullscreen mode.
[window setTitlebarAppearsTransparent:NO];
shell_->SetStyleMask(true, NSFullSizeContentViewWindowMask);
}
}
@@ -397,6 +398,7 @@ bool ScopedDisableResize::disable_resize_ = false;
// Turn off the style for toolbar.
if (base::mac::IsAtLeastOS10_10() &&
shell_->title_bar_style() == atom::NativeWindowMac::HIDDEN_INSET) {
[window setTitlebarAppearsTransparent:YES];
shell_->SetStyleMask(false, NSFullSizeContentViewWindowMask);
}
}
@@ -728,10 +730,20 @@ enum {
// Custom window button methods
- (BOOL)windowShouldClose:(id)sender { return YES; }
- (void)performClose:(id)sender {
if (shell_->title_bar_style() == atom::NativeWindowMac::CUSTOM_BUTTONS_ON_HOVER)
if (shell_->title_bar_style() ==
atom::NativeWindowMac::CUSTOM_BUTTONS_ON_HOVER) {
[[self delegate] windowShouldClose:self];
else
} else if (shell_->IsSimpleFullScreen()) {
if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) {
if(![[self delegate] windowShouldClose:self]) return;
} else if([self respondsToSelector:@selector(windowShouldClose:)]) {
if(![self windowShouldClose:self]) return;
}
[self close];
} else
[super performClose:sender];
}
@@ -1045,6 +1057,10 @@ NativeWindowMac::NativeWindowMac(
RegisterInputEventObserver(
web_contents->GetWebContents()->GetRenderViewHost());
original_frame_ = [window_ frame];
original_level_ = [window_ level];
}
NativeWindowMac::~NativeWindowMac() {
@@ -1139,12 +1155,12 @@ bool NativeWindowMac::IsEnabled() {
void NativeWindowMac::SetEnabled(bool enable) {
if (enable) {
[window_ endSheet: [window_ attachedSheet]];
} else {
[window_ beginSheet: window_ completionHandler:^(NSModalResponse returnCode) {
NSLog(@"modal enabled");
return;
}];
} else {
[window_ endSheet: [window_ attachedSheet]];
}
}
@@ -1411,8 +1427,9 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
if (simple_fullscreen && !is_simple_fullscreen_) {
is_simple_fullscreen_ = true;
// Take note of the current window size
// Take note of the current window size and level
original_frame_ = [window frame];
original_level_ = [window level];
simple_fullscreen_options_ = [NSApp currentSystemPresentationOptions];
simple_fullscreen_mask_ = [window styleMask];
@@ -1428,6 +1445,13 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
NSRect fullscreenFrame = [window.screen frame];
// If our app has dock hidden, set the window level higher so another app's
// menu bar doesn't appear on top of our fullscreen app.
if ([[NSRunningApplication currentApplication] activationPolicy] !=
NSApplicationActivationPolicyRegular) {
window.level = NSPopUpMenuWindowLevel;
}
if ( !fullscreen_window_title() ) {
// Hide the titlebar
SetStyleMask(false, NSTitledWindowMask);
@@ -1462,6 +1486,7 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
}
[window setFrame:original_frame_ display: YES animate: YES];
window.level = original_level_;
[NSApp setPresentationOptions:simple_fullscreen_options_];

View File

@@ -608,6 +608,7 @@ void NativeWindowViews::SetResizable(bool resizable) {
// both the minimum and maximum size to the window size to achieve it.
if (resizable) {
SetContentSizeConstraints(old_size_constraints_);
SetMaximizable(maximizable_);
} else {
old_size_constraints_ = GetContentSizeConstraints();
resizable_ = false;

View File

@@ -4,6 +4,8 @@
#include "atom/browser/node_debugger.h"
#include <string>
#include "base/command_line.h"
#include "base/strings/utf_string_conversions.h"
#include "libplatform/libplatform.h"
@@ -28,10 +30,15 @@ void NodeDebugger::Start(node::NodePlatform* platform) {
node::DebugOptions options;
for (auto& arg : base::CommandLine::ForCurrentProcess()->argv()) {
#if defined(OS_WIN)
options.ParseOption("Electron", base::UTF16ToUTF8(arg));
const std::string nice_arg = base::UTF16ToUTF8(arg);
#else
options.ParseOption("Electron", arg);
const std::string& nice_arg = arg;
#endif
// Stop handling arguments after a "--" to be consistent with Chromium
if (nice_arg == "--")
break;
options.ParseOption("Electron", nice_arg);
}
if (options.inspector_enabled()) {

View File

@@ -17,9 +17,9 @@
<key>CFBundleIconFile</key>
<string>electron.icns</string>
<key>CFBundleVersion</key>
<string>2.0.6</string>
<string>2.0.13</string>
<key>CFBundleShortVersionString</key>
<string>2.0.6</string>
<string>2.0.13</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>

View File

@@ -56,8 +56,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,0,6,0
PRODUCTVERSION 2,0,6,0
FILEVERSION 2,0,13,0
PRODUCTVERSION 2,0,13,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -74,12 +74,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "GitHub, Inc."
VALUE "FileDescription", "Electron"
VALUE "FileVersion", "2.0.6"
VALUE "FileVersion", "2.0.13"
VALUE "InternalName", "electron.exe"
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
VALUE "OriginalFilename", "electron.exe"
VALUE "ProductName", "Electron"
VALUE "ProductVersion", "2.0.6"
VALUE "ProductVersion", "2.0.13"
VALUE "SquirrelAwareVersion", "1"
END
END

View File

@@ -91,7 +91,9 @@ bool TriggerAcceleratorTableCommand(AcceleratorTable* table,
if (base::ContainsKey(*table, accelerator)) {
const accelerator_util::MenuItem& item = (*table)[accelerator];
if (item.model->IsEnabledAt(item.position)) {
item.model->ActivatedAt(item.position);
const auto event_flags =
accelerator.MaskOutKeyEventFlags(accelerator.modifiers());
item.model->ActivatedAt(item.position, event_flags);
return true;
}
}

View File

@@ -39,7 +39,8 @@ class GtkMessageBox : public NativeWindowObserver {
const std::string& message,
const std::string& detail,
const std::string& checkbox_label,
bool checkbox_checked)
bool checkbox_checked,
const gfx::ImageSkia& icon)
: cancel_id_(cancel_id),
checkbox_checked_(false),
parent_(static_cast<NativeWindow*>(parent_window)) {
@@ -56,6 +57,21 @@ class GtkMessageBox : public NativeWindowObserver {
if (!title.empty())
gtk_window_set_title(GTK_WINDOW(dialog_), title.c_str());
if (!icon.isNull()) {
// No easy way to obtain this programmatically, but GTK+'s docs
// define GTK_ICON_SIZE_DIALOG to be 48 pixels
static constexpr int pixel_width = 48;
static constexpr int pixel_height = 48;
GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(*icon.bitmap());
GdkPixbuf* scaled_pixbuf = gdk_pixbuf_scale_simple(
pixbuf, pixel_width, pixel_height, GDK_INTERP_BILINEAR);
GtkWidget* w = gtk_image_new_from_pixbuf(scaled_pixbuf);
gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog_), w);
gtk_widget_show(w);
g_clear_pointer(&scaled_pixbuf, gdk_pixbuf_unref);
g_clear_pointer(&pixbuf, gdk_pixbuf_unref);
}
if (!checkbox_label.empty()) {
GtkWidget* message_area =
gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog_));
@@ -66,15 +82,15 @@ class GtkMessageBox : public NativeWindowObserver {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button),
checkbox_checked);
gtk_container_add(GTK_CONTAINER(message_area), check_button);
gtk_widget_show(check_button);
}
// Add buttons.
GtkDialog* dialog = GTK_DIALOG(dialog_);
for (size_t i = 0; i < buttons.size(); ++i) {
GtkWidget* button = gtk_dialog_add_button(
GTK_DIALOG(dialog_), TranslateToStock(i, buttons[i]), i);
if (static_cast<int>(i) == default_id)
gtk_widget_grab_focus(button);
gtk_dialog_add_button(dialog, TranslateToStock(i, buttons[i]), i);
}
gtk_dialog_set_default_response(dialog, default_id);
// Parent window.
if (parent_) {
@@ -122,7 +138,7 @@ class GtkMessageBox : public NativeWindowObserver {
}
void Show() {
gtk_widget_show_all(dialog_);
gtk_widget_show(dialog_);
// We need to call gtk_window_present after making the widgets visible to
// make sure window gets correctly raised and gets focus.
int time = ui::X11EventSource::GetInstance()->GetTimestamp();
@@ -195,9 +211,9 @@ int ShowMessageBox(NativeWindow* parent,
const std::string& title,
const std::string& message,
const std::string& detail,
const gfx::ImageSkia& /*icon*/) {
const gfx::ImageSkia& icon) {
return GtkMessageBox(parent, type, buttons, default_id, cancel_id, title,
message, detail, "", false)
message, detail, "", false, icon)
.RunSynchronous();
}
@@ -212,10 +228,10 @@ void ShowMessageBox(NativeWindow* parent,
const std::string& detail,
const std::string& checkbox_label,
bool checkbox_checked,
const gfx::ImageSkia& /*icon*/,
const gfx::ImageSkia& icon,
const MessageBoxCallback& callback) {
(new GtkMessageBox(parent, type, buttons, default_id, cancel_id, title,
message, detail, checkbox_label, checkbox_checked))
message, detail, checkbox_label, checkbox_checked, icon))
->RunAsynchronous(callback);
}
@@ -223,7 +239,8 @@ void ShowErrorBox(const base::string16& title, const base::string16& content) {
if (Browser::Get()->is_ready()) {
GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, {"OK"}, -1, 0, "Error",
base::UTF16ToUTF8(title).c_str(),
base::UTF16ToUTF8(content).c_str(), "", false)
base::UTF16ToUTF8(content).c_str(), "", false,
gfx::ImageSkia())
.RunSynchronous();
} else {
fprintf(stderr, ANSI_TEXT_BOLD ANSI_BACKGROUND_GRAY ANSI_FOREGROUND_RED

View File

@@ -96,11 +96,6 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
NSArray* ns_buttons = [alert buttons];
int button_count = static_cast<int>([ns_buttons count]);
// Bind cancel id button to escape key if there is more than one button
if (button_count > 1 && cancel_id >= 0 && cancel_id < button_count) {
[[ns_buttons objectAtIndex:cancel_id] setKeyEquivalent:@"\e"];
}
if (default_id >= 0 && default_id < button_count) {
// Focus the button at default_id if the user opted to do so.
// The first button added gets set as the default selected.
@@ -109,6 +104,11 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
[[ns_buttons objectAtIndex:default_id] setKeyEquivalent:@"\r"];
}
// Bind cancel id button to escape key if there is more than one button
if (button_count > 1 && cancel_id >= 0 && cancel_id < button_count) {
[[ns_buttons objectAtIndex:cancel_id] setKeyEquivalent:@"\e"];
}
if (!checkbox_label.empty()) {
alert.showsSuppressionButton = YES;
alert.suppressionButton.title = base::SysUTF8ToNSString(checkbox_label);

View File

@@ -40,9 +40,16 @@ const CGFloat kVerticalTitleMargin = 2;
@implementation StatusItemView
- (void)dealloc {
trayIcon_ = nil;
menuController_ = nil;
[super dealloc];
}
- (id)initWithImage:(NSImage*)image icon:(atom::TrayIconCocoa*)icon {
image_.reset([image copy]);
trayIcon_ = icon;
menuController_ = nil;
highlight_mode_ = atom::TrayIcon::HighlightMode::SELECTION;
forceHighlight_ = NO;
inMouseEventSequence_ = NO;
@@ -85,6 +92,7 @@ const CGFloat kVerticalTitleMargin = 2;
trackingArea_.reset();
}
[[NSStatusBar systemStatusBar] removeStatusItem:statusItem_];
[statusItem_ setView:nil];
statusItem_.reset();
}
@@ -454,11 +462,18 @@ void TrayIconCocoa::SetContextMenu(AtomMenuModel* menu_model) {
// Substribe to MenuClosed event.
if (menu_model_)
menu_model_->RemoveObserver(this);
menu_model->AddObserver(this);
// Create native menu.
menu_.reset([[AtomMenuController alloc] initWithModel:menu_model
useDefaultAccelerator:NO]);
menu_model_ = menu_model;
if (menu_model) {
menu_model->AddObserver(this);
// Create native menu.
menu_.reset([[AtomMenuController alloc] initWithModel:menu_model
useDefaultAccelerator:NO]);
} else {
menu_.reset();
}
[status_item_view_ setMenuController:menu_.get()];
}

View File

@@ -4,12 +4,19 @@
#include "atom/browser/ui/views/menu_bar.h"
#include <memory>
#include <string>
#include "atom/browser/ui/views/menu_delegate.h"
#include "atom/browser/ui/views/submenu_button.h"
#include "ui/base/models/menu_model.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"
#if defined(USE_X11)
#include "chrome/browser/ui/libgtkui/gtk_util.h"
#endif
#if defined(OS_WIN)
#include "ui/gfx/color_utils.h"
#endif
@@ -130,13 +137,17 @@ void MenuBar::RefreshColorCache(const ui::NativeTheme* theme) {
if (!theme)
theme = ui::NativeTheme::GetInstanceForNativeUi();
if (theme) {
background_color_ =
theme->GetSystemColor(ui::NativeTheme::kColorId_MenuBackgroundColor);
#if defined(USE_X11)
const std::string menubar_selector = "GtkMenuBar#menubar";
background_color_ = libgtkui::GetBgColor(menubar_selector);
enabled_color_ = theme->GetSystemColor(
ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor);
disabled_color_ = theme->GetSystemColor(
ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor);
#else
background_color_ =
theme->GetSystemColor(ui::NativeTheme::kColorId_MenuBackgroundColor);
#endif
}
#if defined(OS_WIN)

View File

@@ -201,8 +201,13 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
// --background-color.
std::string color;
if (web_preferences.GetString(options::kBackgroundColor, &color))
if (web_preferences.GetString(options::kBackgroundColor, &color)) {
command_line->AppendSwitchASCII(switches::kBackgroundColor, color);
} else if (!(web_preferences.GetBoolean("offscreen", &b) && b)) {
// For non-OSR WebContents, we expect to have white background, see
// https://github.com/electron/electron/issues/13764 for more.
command_line->AppendSwitchASCII(switches::kBackgroundColor, "#fff");
}
// --guest-instance-id, which is used to identify guest WebContents.
int guest_instance_id = 0;
@@ -331,6 +336,11 @@ void WebContentsPreferences::OverrideWebkitPrefs(
std::string encoding;
if (self->web_preferences_.GetString("defaultEncoding", &encoding))
prefs->default_encoding = encoding;
bool node_integration = false;
self->web_preferences_.GetBoolean(options::kNodeIntegration,
&node_integration);
prefs->node_integration = node_integration;
}
bool WebContentsPreferences::GetInteger(const std::string& attributeName,

View File

@@ -7,7 +7,7 @@
#define ATOM_MAJOR_VERSION 2
#define ATOM_MINOR_VERSION 0
#define ATOM_PATCH_VERSION 6
#define ATOM_PATCH_VERSION 13
// #define ATOM_PRE_RELEASE_VERSION
#ifndef ATOM_STRINGIFY

View File

@@ -168,6 +168,7 @@ void NodeBindings::Initialize() {
// Init node.
// (we assume node::Init would not modify the parameters under embedded mode).
// NOTE: If you change this line, please ping @codebytere or @MarshallOfSound
node::Init(nullptr, nullptr, nullptr, nullptr);
#if defined(OS_WIN)

View File

@@ -17,6 +17,7 @@
#include "atom/renderer/atom_render_frame_observer.h"
#include "atom/renderer/web_worker_observer.h"
#include "base/command_line.h"
#include "content/public/common/web_preferences.h"
#include "content/public/renderer/render_frame.h"
#include "native_mate/dictionary.h"
#include "third_party/WebKit/public/web/WebDocument.h"
@@ -89,6 +90,15 @@ void AtomRendererClient::DidCreateScriptContext(
if (!render_frame->IsMainFrame() && !IsDevToolsExtension(render_frame))
return;
// Don't allow node integration if this is a child window and it does not have
// node integration enabled. Otherwise we would have memory leak in the child
// window since we don't clean up node environments.
//
// TODO(zcbenz): We shouldn't allow node integration even for the top frame.
if (!render_frame->GetWebkitPreferences().node_integration &&
render_frame->GetWebFrame()->Opener())
return;
// Prepare the node bindings.
if (!node_integration_initialized_) {
node_integration_initialized_ = true;

View File

@@ -266,16 +266,6 @@ void BrowserMainParts::PreMainMessageLoopStart() {
}
void BrowserMainParts::PreMainMessageLoopRun() {
// We already initialized feature list in PreEarlyInitialization(), but
// the user JS script would not have had a chance to alter the command-line
// switches at that point. Lets reinitialize it here to pick up the
// command-line changes. Note that some Chromium code (e.g.
// gpu_process_host.cc) queries the feature list between
// PreEarlyInitialization() and here so the user script may not have
// control over all features. Better than nothing though!
base::FeatureList::ClearInstanceForTesting();
InitializeFeatureList();
content::WebUIControllerFactory::RegisterFactory(
WebUIControllerFactory::GetInstance());

View File

@@ -46,14 +46,14 @@ class BrowserMainParts : public content::BrowserMainParts {
int PreCreateThreads() override;
void PostDestroyThreads() override;
void InitializeFeatureList();
private:
#if defined(OS_MACOSX)
void InitializeMainNib();
void OverrideAppLogsPath();
#endif
void InitializeFeatureList();
std::unique_ptr<IOThread> io_thread_;
#if defined(TOOLKIT_VIEWS)

View File

@@ -698,6 +698,8 @@ void InspectableWebContentsImpl::WebContentsDestroyed() {
for (const auto& pair : pending_requests_)
delete pair.first;
pending_requests_.clear();
if (view_ && view_->GetDelegate())
view_->GetDelegate()->DevToolsClosed();
}

View File

@@ -2,7 +2,7 @@
> Define keyboard shortcuts.
Accelerators are Strings that can contain multiple modifiers and key codes,
Accelerators are Strings that can contain multiple modifiers and a single key code,
combined by the `+` character, and are used to define keyboard shortcuts
throughout your application.

View File

@@ -365,6 +365,22 @@ assistive technologies, such as screen readers, are enabled or disabled.
See https://www.chromium.org/developers/design-documents/accessibility for more
details.
### Event: 'session-created'
Returns:
* `session` [Session](session.md)
Emitted when Electron has created a new `session`.
```javascript
const {app} = require('electron')
app.on('session-created', (event, session) => {
console.log(session)
})
```
## Methods
The `app` object has the following methods:

View File

@@ -202,7 +202,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
than screen. Default is `false`.
* `backgroundColor` String (optional) - Window's background color as a hexadecimal value,
like `#66CD00` or `#FFF` or `#80FFFFFF` (alpha is supported). Default is
`#FFF` (white).
`#FFF` (white). If `transparent` is set to `true`, only values with transparent (`#00-------`) or opaque (`#FF-----`) alpha values are respected.
* `hasShadow` Boolean (optional) - Whether window should have a shadow. This is only
implemented on macOS. Default is `true`.
* `opacity` Number (optional) - Set the initial opacity of the window, between 0.0 (fully
@@ -361,7 +361,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
script. You can use the `will-attach-webview` event on [webContents](web-contents.md)
to strip away the `preload` script and to validate or alter the
`<webview>`'s initial settings.
* `additionArguments` String[] (optional) - A list of strings that will be appended
* `additionalArguments` String[] (optional) - A list of strings that will be appended
to `process.argv` in the renderer process of this app. Useful for passing small
bits of data down to renderer process preload scripts.
@@ -870,7 +870,9 @@ Disable or enable the window.
* `height` Integer
* `animate` Boolean (optional) _macOS_
Resizes the window to `width` and `height`.
Resizes the window to `width` and `height`. If `width` or `height`
are below any set minimum size constraints the window will snap to
its minimum size.
#### `win.getSize()`

View File

@@ -95,6 +95,8 @@ When specifying a `role` on macOS, `label` and `accelerator` are the only
options that will affect the menu item. All other options will be ignored.
Lowercase `role`, e.g. `toggledevtools`, is still supported.
**Nota Bene:** The `enabled` and `visibility` properties are not available for top-level menu items in the tray on MacOS.
### Instance Properties
The following properties are available on instances of `MenuItem`:

View File

@@ -67,6 +67,8 @@ that contains the user information dictionary sent along with the notification.
* `callback` Function
* `event` String
* `userInfo` Object
Returns `Number` - The ID of this subscription
Subscribes to native notifications of macOS, `callback` will be called with
`callback(event, userInfo)` when the corresponding `event` happens. The
@@ -96,6 +98,8 @@ Removes the subscriber with `id`.
* `callback` Function
* `event` String
* `userInfo` Object
Returns `Number` - The ID of this subscription
Same as `subscribeNotification`, but uses `NSNotificationCenter` for local defaults.
This is necessary for events such as `NSUserDefaultsDidChangeNotification`.

View File

@@ -262,7 +262,7 @@ The `position` is only available on Windows, and it is (0, 0) by default.
#### `tray.setContextMenu(menu)`
* `menu` Menu
* `menu` Menu | null
Sets the context menu for this icon.

View File

@@ -173,6 +173,7 @@ Set the security origin of the isolated world.
Returns `Object`:
* `images` [MemoryUsageDetails](structures/memory-usage-details.md)
* `scripts` [MemoryUsageDetails](structures/memory-usage-details.md)
* `cssStyleSheets` [MemoryUsageDetails](structures/memory-usage-details.md)
* `xslStyleSheets` [MemoryUsageDetails](structures/memory-usage-details.md)
* `fonts` [MemoryUsageDetails](structures/memory-usage-details.md)

View File

@@ -24,7 +24,12 @@ Returns [`BrowserWindowProxy`](browser-window-proxy.md) - Creates a new window
and returns an instance of `BrowserWindowProxy` class.
The `features` string follows the format of standard browser, but each feature
has to be a field of `BrowserWindow`'s options.
has to be a field of `BrowserWindow`'s options. These are the features you can set via `features` string: `zoomFactor`, `nodeIntegration`, `preload`, `javascript`, `contextIsolation`, `webviewTag`.
For example:
```js
window.open('https://github.com', '_blank', 'nodeIntegration=no')
```
**Notes:**

View File

@@ -4,7 +4,7 @@
'product_name%': 'Electron',
'company_name%': 'GitHub, Inc',
'company_abbr%': 'github',
'version%': '2.0.6',
'version%': '2.0.13',
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
},
'includes': [

View File

@@ -49,6 +49,21 @@ BrowserWindow.prototype._init = function () {
return
}
if (webContents.getLastWebPreferences().nodeIntegration === true) {
const message =
'Enabling Node.js integration in child windows opened with the ' +
'"nativeWindowOpen" option will cause memory leaks, please turn off ' +
'the "nodeIntegration" option.\\n' +
'See https://github.com/electron/electron/pull/15076 for more.'
// console is only available after DOM is created.
const printWarning = () => this.webContents.executeJavaScript(`console.warn('${message}')`)
if (this.webContents.isDomReady()) {
printWarning()
} else {
this.webContents.once('dom-ready', printWarning)
}
}
let {url, frameName} = urlFrameName
v8Util.deleteHiddenValue(webContents, 'url-framename')
const options = {

View File

@@ -27,11 +27,11 @@ const mergeOptions = function (child, parent, visited) {
for (const key in parent) {
if (key === 'isBrowserView') continue
if (!hasProp.call(parent, key)) continue
if (key in child) continue
if (key in child && key !== 'webPreferences') continue
const value = parent[key]
if (typeof value === 'object') {
child[key] = mergeOptions({}, value, visited)
child[key] = mergeOptions(child[key] || {}, value, visited)
} else {
child[key] = value
}

View File

@@ -83,6 +83,8 @@ class ObjectsRegistry {
// Private: Dereference the object from store.
dereference (id) {
if (process.env.ELECTRON_DISABLE_REMOTE_DEREFERENCING) return
let pointer = this.storage[id]
if (pointer == null) {
return

View File

@@ -3,6 +3,7 @@
const {Buffer} = require('buffer')
const childProcess = require('child_process')
const path = require('path')
const util = require('util')
const hasProp = {}.hasOwnProperty
@@ -66,47 +67,40 @@
let nextInode = 0
const uid = process.getuid != null ? process.getuid() : 0
const gid = process.getgid != null ? process.getgid() : 0
const fakeTime = new Date()
const msec = (date) => (date || fakeTime).getTime()
const asarStatsToFsStats = function (stats) {
return {
dev: 1,
ino: ++nextInode,
mode: 33188,
nlink: 1,
uid: uid,
gid: gid,
rdev: 0,
atime: stats.atime || fakeTime,
birthtime: stats.birthtime || fakeTime,
mtime: stats.mtime || fakeTime,
ctime: stats.ctime || fakeTime,
size: stats.size,
isFile: function () {
return stats.isFile
},
isDirectory: function () {
return stats.isDirectory
},
isSymbolicLink: function () {
return stats.isLink
},
isBlockDevice: function () {
return false
},
isCharacterDevice: function () {
return false
},
isFIFO: function () {
return false
},
isSocket: function () {
return false
}
const {Stats, constants} = require('fs')
let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR
if (stats.isFile) {
mode ^= constants.S_IFREG
} else if (stats.isDirectory) {
mode ^= constants.S_IFDIR
} else if (stats.isLink) {
mode ^= constants.S_IFLNK
}
return new Stats(
1, // dev
mode, // mode
1, // nlink
uid,
gid,
0, // rdev
undefined, // blksize
++nextInode, // ino
stats.size,
undefined, // blocks,
msec(stats.atime), // atim_msec
msec(stats.mtime), // mtim_msec
msec(stats.ctime), // ctim_msec
msec(stats.birthtime) // birthtim_msec
)
}
// Create a ENOENT error.
@@ -217,6 +211,28 @@
arguments[arg] = newPath
return old.apply(this, arguments)
}
if (old[util.promisify.custom]) {
module[name][util.promisify.custom] = function () {
const p = arguments[arg]
const [isAsar, asarPath, filePath] = splitPath(p)
if (!isAsar) {
return old[util.promisify.custom].apply(this, arguments)
}
const archive = getOrCreateArchive(asarPath)
if (!archive) {
return new Promise(() => invalidArchiveError(asarPath))
}
const newPath = archive.copyFileOut(filePath)
if (!newPath) {
return new Promise(() => notFoundError(asarPath, filePath))
}
arguments[arg] = newPath
return old[util.promisify.custom].apply(this, arguments)
}
}
}
// Override fs APIs.
@@ -373,6 +389,18 @@
})
}
fs.exists[util.promisify.custom] = function (p) {
const [isAsar, asarPath, filePath] = splitPath(p)
if (!isAsar) {
return exists[util.promisify.custom](p)
}
const archive = getOrCreateArchive(asarPath)
if (!archive) {
return new Promise(() => invalidArchiveError(asarPath))
}
return Promise.resolve(archive.stat(filePath) !== false)
}
const {existsSync} = fs
fs.existsSync = function (p) {
const [isAsar, asarPath, filePath] = splitPath(p)
@@ -680,18 +708,28 @@
// called by `childProcess.{exec,execSync}`, causing
// Electron to consider the full command as a single path
// to an archive.
['exec', 'execSync'].forEach(function (functionName) {
const old = childProcess[functionName]
childProcess[functionName] = function () {
const {exec, execSync} = childProcess
childProcess.exec = invokeWithNoAsar(exec)
childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom])
childProcess.execSync = invokeWithNoAsar(execSync)
function invokeWithNoAsar (func) {
return function () {
const processNoAsarOriginalValue = process.noAsar
process.noAsar = true
try {
return old.apply(this, arguments)
return func.apply(this, arguments)
} finally {
process.noAsar = processNoAsarOriginalValue
}
}
})
}
// Strictly implementing the flags of fs.copyFile is hard, just do a simple
// implementation for now. Doing 2 copies won't spend much time more as OS
// has filesystem caching.
overrideAPI(fs, 'copyFile')
overrideAPISync(fs, 'copyFileSync')
overrideAPI(fs, 'open')
overrideAPI(childProcess, 'execFile')

View File

@@ -3,8 +3,7 @@ window.onload = function () {
window.InspectorFrontendHost.showContextMenuAtPoint = createMenu
// Use dialog API to override file chooser dialog.
// Note: It will be moved to UI after Chrome 57.
window.Bindings.createFileSelectorElement = createFileSelectorElement
window.UI.createFileSelectorElement = createFileSelectorElement
}
window.confirm = function (message, title) {

View File

@@ -145,9 +145,7 @@ class WebViewImpl {
onElementResize (newSize) {
// Dispatch the 'resize' event.
const resizeEvent = new Event('resize', {
bubbles: true
})
const resizeEvent = new Event('resize')
// Using client size values, because when a webview is transformed `newSize`
// is incorrect

View File

@@ -8,3 +8,14 @@ var child = proc.spawn(electron, process.argv.slice(2), {stdio: 'inherit'})
child.on('close', function (code) {
process.exit(code)
})
const handleTerminationSignal = function (signal) {
process.on(signal, function signalHandler () {
if (!child.killed) {
child.kill(signal)
}
})
}
handleTerminationSignal('SIGINT')
handleTerminationSignal('SIGTERM')

View File

@@ -21,6 +21,12 @@ if (installedVersion === version && fs.existsSync(path.join(__dirname, platformP
process.exit(0)
}
var mirror
if (version.indexOf('nightly') !== -1) {
mirror = 'https://github.com/electron/nightlies/releases/download/v'
}
// downloads if not cached
download({
cache: process.env.electron_config_cache,
@@ -29,7 +35,8 @@ download({
arch: process.env.npm_config_arch,
strictSSL: process.env.npm_config_strict_ssl === 'true',
force: process.env.force_no_cache === 'true',
quiet: process.env.npm_config_loglevel === 'silent' || process.env.CI
quiet: process.env.npm_config_loglevel === 'silent' || process.env.CI,
mirror
}, extractFile)
// unzips and makes path.txt point at the correct executable

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "2.0.6",
"version": "2.0.13",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

View File

@@ -5,12 +5,12 @@ import re
import sys
import argparse
from lib.util import execute, get_electron_version, parse_version, scoped_cwd
from lib.util import execute, get_electron_version, parse_version, scoped_cwd, \
is_nightly, is_beta, is_stable, get_next_nightly, get_next_beta, \
get_next_stable_from_pre, get_next_stable_from_stable, clean_parse_version
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
def main():
parser = argparse.ArgumentParser(
@@ -34,14 +34,7 @@ def main():
action='store',
default=None,
dest='bump',
help='increment [major | minor | patch | beta]'
)
parser.add_argument(
'--stable',
action='store_true',
default= False,
dest='stable',
help='promote to stable (i.e. remove `-beta.x` suffix)'
help='increment [stable | beta | nightly]'
)
parser.add_argument(
'--dry-run',
@@ -52,36 +45,56 @@ def main():
)
args = parser.parse_args()
curr_version = get_electron_version()
if args.bump not in ['stable', 'beta', 'nightly']:
raise Exception('bump must be set to either stable, beta or nightly')
if is_nightly(curr_version):
if args.bump == 'nightly':
version = get_next_nightly(curr_version)
elif args.bump == 'beta':
version = get_next_beta(curr_version)
elif args.bump == 'stable':
version = get_next_stable_from_pre(curr_version)
else:
not_reached()
elif is_beta(curr_version):
if args.bump == 'nightly':
version = get_next_nightly(curr_version)
elif args.bump == 'beta':
version = get_next_beta(curr_version)
elif args.bump == 'stable':
version = get_next_stable_from_pre(curr_version)
else:
not_reached()
elif is_stable(curr_version):
if args.bump == 'nightly':
version = get_next_nightly(curr_version)
elif args.bump == 'beta':
raise Exception("You can\'t bump to a beta from stable")
elif args.bump == 'stable':
version = get_next_stable_from_stable(curr_version)
else:
not_reached()
else:
raise Exception("Invalid current version: " + curr_version)
if args.new_version == None and args.bump == None and args.stable == False:
parser.print_help()
return 1
increments = ['major', 'minor', 'patch', 'beta']
curr_version = get_electron_version()
versions = parse_version(re.sub('-beta', '', curr_version))
if args.bump in increments:
versions = increase_version(versions, increments.index(args.bump))
if versions[3] == '0':
# beta starts at 1
versions = increase_version(versions, increments.index('beta'))
if args.stable == True:
versions[3] = '0'
if args.new_version != None:
versions = parse_version(re.sub('-beta', '', args.new_version))
version = '.'.join(versions[:3])
suffix = '' if versions[3] == '0' else '-beta.' + versions[3]
versions = clean_parse_version(version)
suffix = ''
if '-' in version:
suffix = '-' + version.split('-')[1]
versions[3] = parse_version(version)[3]
version = version.split('-')[0]
if args.dry_run:
print 'new version number would be: {0}\n'.format(version + suffix)
return 0
with scoped_cwd(SOURCE_ROOT):
update_electron_gyp(version, suffix)
update_win_rc(version, versions)
@@ -92,6 +105,9 @@ def main():
print 'Bumped to version: {0}'.format(version + suffix)
def not_reached():
raise Exception('Unreachable code was reached')
def increase_version(versions, index):
for i in range(index + 1, 4):
versions[i] = '0'
@@ -100,7 +116,8 @@ def increase_version(versions, index):
def update_electron_gyp(version, suffix):
pattern = re.compile(" *'version%' *: *'[0-9.]+(-beta[0-9.]*)?'")
pattern = re.compile(" *'version%' *: *'[0-9.]+(-beta[0-9.]*)?(-dev)?"
+ "(-nightly[0-9.]*)?'")
with open('electron.gyp', 'r') as f:
lines = f.readlines()
@@ -192,7 +209,14 @@ def update_package_json(version, suffix):
def tag_version(version, suffix):
execute(['git', 'commit', '-a', '-m', 'Bump v{0}'.format(version + suffix)])
execute([
'git',
'commit',
'-a',
'-m',
'Bump v{0}'.format(version + suffix),
'-n'
])
if __name__ == '__main__':

View File

@@ -1,3 +1,5 @@
require('dotenv-safe').load()
const assert = require('assert')
const request = require('request')
const buildAppVeyorURL = 'https://windows-ci.electronjs.org/api/builds'
@@ -32,8 +34,13 @@ async function makeRequest (requestOptions, parseResponse) {
resolve(body)
}
} else {
console.error('Error occurred while requesting:', requestOptions.url)
if (parseResponse) {
console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions)
try {
console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions)
} catch (err) {
console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions)
}
} else {
console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions)
}
@@ -44,7 +51,6 @@ async function makeRequest (requestOptions, parseResponse) {
}
async function circleCIcall (buildUrl, targetBranch, job, options) {
assert(process.env.CIRCLE_TOKEN, 'CIRCLE_TOKEN not found in environment')
console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`)
let buildRequest = {
'build_parameters': {
@@ -77,7 +83,6 @@ async function circleCIcall (buildUrl, targetBranch, job, options) {
}
function buildAppVeyor (targetBranch, options) {
assert(process.env.APPVEYOR_TOKEN, 'APPVEYOR_TOKEN not found in environment')
const validJobs = Object.keys(appVeyorJobs)
if (options.job) {
assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`)
@@ -139,7 +144,6 @@ async function buildVSTS (targetBranch, options) {
assert(vstsJobs.includes(options.job), `Unknown VSTS CI job name: ${options.job}. Valid values are: ${vstsJobs}.`)
}
console.log(`Triggering VSTS to run build on branch: ${targetBranch} with release flag.`)
assert(process.env.VSTS_TOKEN, 'VSTS_TOKEN not found in environment')
let environmentVariables = {}
if (!options.ghRelease) {
@@ -175,7 +179,8 @@ async function buildVSTS (targetBranch, options) {
async function callVSTSBuild (build, targetBranch, environmentVariables) {
let buildBody = {
definition: build,
sourceBranch: targetBranch
sourceBranch: targetBranch,
priority: 'high'
}
if (Object.keys(environmentVariables).length !== 0) {
buildBody.parameters = JSON.stringify(environmentVariables)

View File

@@ -1,3 +1,5 @@
if (!process.env.CI) require('dotenv-safe').load()
const GitHub = require('github')
const github = new GitHub()
@@ -12,7 +14,7 @@ async function findRelease () {
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
let releases = await github.repos.getReleases({
owner: 'electron',
repo: 'electron'
repo: version.indexOf('nightly') > 0 ? 'nightlies' : 'electron'
})
let targetRelease = releases.data.find(release => {
return release.tag_name === version

View File

@@ -0,0 +1,29 @@
const { GitProcess } = require('dugite')
const path = require('path')
const semver = require('semver')
const gitDir = path.resolve(__dirname, '..')
async function determineNextMajorForMaster () {
let branchNames
let result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]-[0-9]-x'], gitDir)
if (result.exitCode === 0) {
branchNames = result.stdout.trim().split('\n')
const filtered = branchNames.map(b => b.replace('origin/', ''))
return getNextReleaseBranch(filtered)
} else {
throw new Error('Release branches could not be fetched.')
}
}
function getNextReleaseBranch (branches) {
const converted = branches.map(b => b.replace(/-/g, '.').replace('x', '0'))
const next = converted.reduce((v1, v2) => {
return semver.gt(v1, v2) ? v1 : v2
})
return parseInt(next.split('.')[0], 10)
}
determineNextMajorForMaster().then(console.info).catch((err) => {
console.error(err)
process.exit(1)
})

View File

@@ -2,6 +2,7 @@
import atexit
import contextlib
import datetime
import errno
import platform
import re
@@ -87,7 +88,7 @@ def download(text, url, path):
downloaded_size = 0
block_size = 128
ci = os.environ.get('CI') == '1'
ci = os.environ.get('CI') is not None
while True:
buf = web_file.read(block_size)
@@ -287,3 +288,67 @@ def update_node_modules(dirname, env=None):
pass
else:
execute_stdout(args, env)
def clean_parse_version(v):
return parse_version(v.split("-")[0])
def is_stable(v):
return len(v.split(".")) == 3
def is_beta(v):
return 'beta' in v
def is_nightly(v):
return 'nightly' in v
def get_nightly_date():
return datetime.datetime.today().strftime('%Y%m%d')
def get_last_major():
return execute(['node', 'script/get-last-major-for-master.js'])
def get_next_nightly(v):
pv = clean_parse_version(v)
major = pv[0]; minor = pv[1]; patch = pv[2]
if (is_stable(v)):
patch = str(int(pv[2]) + 1)
if execute(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) == "master":
major = str(get_last_major() + 1)
minor = '0'
patch = '0'
pre = 'nightly.' + get_nightly_date()
return make_version(major, minor, patch, pre)
def non_empty(thing):
return thing.strip() != ''
def get_next_beta(v):
pv = clean_parse_version(v)
tag_pattern = 'v' + pv[0] + '.' + pv[1] + '.' + pv[2] + '-beta.*'
tag_list = filter(
non_empty,
execute(['git', 'tag', '--list', '-l', tag_pattern]).strip().split('\n')
)
if len(tag_list) == 0:
return make_version(pv[0] , pv[1], pv[2], 'beta.1')
lv = parse_version(tag_list[-1])
return make_version(pv[0] , pv[1], pv[2], 'beta.' + str(int(lv[3]) + 1))
def get_next_stable_from_pre(v):
pv = clean_parse_version(v)
major = pv[0]; minor = pv[1]; patch = pv[2]
return make_version(major, minor, patch)
def get_next_stable_from_stable(v):
pv = clean_parse_version(v)
major = pv[0]; minor = pv[1]; patch = pv[2]
return make_version(major, minor, str(int(patch) + 1))
def make_version(major, minor, patch, pre = None):
if pre is None:
return major + '.' + minor + '.' + patch
return major + "." + minor + "." + patch + '-' + pre

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env node
if (!process.env.CI) require('dotenv-safe').load()
require('colors')
const args = require('minimist')(process.argv.slice(2), {
boolean: ['automaticRelease', 'notesOnly', 'stable']
})
const assert = require('assert')
const ciReleaseBuild = require('./ci-release-build')
const { execSync } = require('child_process')
const fail = '\u2717'.red
@@ -15,13 +15,13 @@ const path = require('path')
const pkg = require('../package.json')
const readline = require('readline')
const versionType = args._[0]
const targetRepo = versionType === 'nightly' ? 'nightlies' : 'electron'
// TODO (future) automatically determine version based on conventional commits
// via conventional-recommended-bump
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
if (!versionType && !args.notesOnly) {
console.log(`Usage: prepare-release versionType [major | minor | patch | beta]` +
console.log(`Usage: prepare-release versionType [stable | beta | nightly]` +
` (--stable) (--notesOnly) (--automaticRelease) (--branch)`)
process.exit(1)
}
@@ -30,13 +30,12 @@ const github = new GitHub()
const gitDir = path.resolve(__dirname, '..')
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
function getNewVersion (dryRun) {
console.log(`Bumping for new "${versionType}" version.`)
let bumpScript = path.join(__dirname, 'bump-version.py')
let scriptArgs = [bumpScript, `--bump ${versionType}`]
if (args.stable) {
scriptArgs.push('--stable')
async function getNewVersion (dryRun) {
if (!dryRun) {
console.log(`Bumping for new "${versionType}" version.`)
}
let bumpScript = path.join(__dirname, 'bump-version.py')
let scriptArgs = [bumpScript, '--bump', versionType]
if (dryRun) {
scriptArgs.push('--dry-run')
}
@@ -50,6 +49,7 @@ function getNewVersion (dryRun) {
return newVersion
} catch (err) {
console.log(`${fail} Could not bump version, error was:`, err)
throw err
}
}
@@ -71,10 +71,13 @@ async function getCurrentBranch (gitDir) {
}
async function getReleaseNotes (currentBranch) {
if (versionType === 'nightly') {
return 'Nightlies do not get release notes, please compare tags for info'
}
console.log(`Generating release notes for ${currentBranch}.`)
let githubOpts = {
owner: 'electron',
repo: 'electron',
repo: targetRepo,
base: `v${pkg.version}`,
head: currentBranch
}
@@ -136,11 +139,11 @@ async function getReleaseNotes (currentBranch) {
async function createRelease (branchToTarget, isBeta) {
let releaseNotes = await getReleaseNotes(branchToTarget)
let newVersion = getNewVersion()
let newVersion = await getNewVersion()
await tagRelease(newVersion)
const githubOpts = {
owner: 'electron',
repo: 'electron'
repo: targetRepo
}
console.log(`Checking for existing draft release.`)
let releases = await github.repos.getReleases(githubOpts)
@@ -158,17 +161,24 @@ async function createRelease (branchToTarget, isBeta) {
githubOpts.draft = true
githubOpts.name = `electron ${newVersion}`
if (isBeta) {
githubOpts.body = `Note: This is a beta release. Please file new issues ` +
`for any bugs you find in it.\n \n This release is published to npm ` +
`under the beta tag and can be installed via npm install electron@beta, ` +
`or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes}`
if (newVersion.indexOf('nightly') > 0) {
githubOpts.body = `Note: This is a nightly release. Please file new issues ` +
`for any bugs you find in it.\n \n This release is published to npm ` +
`under the nightly tag and can be installed via npm install electron@nightly, ` +
`or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes}`
} else {
githubOpts.body = `Note: This is a beta release. Please file new issues ` +
`for any bugs you find in it.\n \n This release is published to npm ` +
`under the beta tag and can be installed via npm install electron@beta, ` +
`or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes}`
}
githubOpts.name = `${githubOpts.name}`
githubOpts.prerelease = true
} else {
githubOpts.body = releaseNotes
}
githubOpts.tag_name = newVersion
githubOpts.target_commitish = branchToTarget
githubOpts.target_commitish = newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget
await github.repos.createRelease(githubOpts)
.catch(err => {
console.log(`${fail} Error creating new release: `, err)
@@ -209,7 +219,7 @@ async function tagRelease (version) {
}
async function verifyNewVersion () {
let newVersion = getNewVersion(true)
let newVersion = await getNewVersion(true)
let response
if (args.automaticRelease) {
response = 'y'
@@ -237,26 +247,34 @@ async function promptForVersion (version) {
})
}
// function to determine if there have been commits to master since the last release
async function changesToRelease () {
let lastCommitWasRelease = new RegExp(`^Bump v[0-9.]*(-beta[0-9.]*)?(-nightly[0-9.]*)?$`, 'g')
let lastCommit = await GitProcess.exec(['log', '-n', '1', `--pretty=format:'%s'`], gitDir)
return !lastCommitWasRelease.test(lastCommit.stdout)
}
async function prepareRelease (isBeta, notesOnly) {
if (args.automaticRelease && (pkg.version.indexOf('beta') === -1 ||
versionType !== 'beta')) {
console.log(`${fail} Automatic release is only supported for beta releases`)
process.exit(1)
}
let currentBranch
if (args.branch) {
currentBranch = args.branch
if (args.dryRun) {
let newVersion = await getNewVersion(true)
console.log(newVersion)
} else {
currentBranch = await getCurrentBranch(gitDir)
}
if (notesOnly) {
let releaseNotes = await getReleaseNotes(currentBranch)
console.log(`Draft release notes are: \n${releaseNotes}`)
} else {
await verifyNewVersion()
await createRelease(currentBranch, isBeta)
await pushRelease(currentBranch)
await runReleaseBuilds(currentBranch)
const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(gitDir)
if (notesOnly) {
let releaseNotes = await getReleaseNotes(currentBranch)
console.log(`Draft release notes are: \n${releaseNotes}`)
} else {
const changes = await changesToRelease(currentBranch)
if (changes) {
await verifyNewVersion()
await createRelease(currentBranch, isBeta)
await pushRelease(currentBranch)
await runReleaseBuilds(currentBranch)
} else {
console.log(`There are no new changes to this branch since the last release, aborting release.`)
process.exit(1)
}
}
}
}

View File

@@ -3,10 +3,15 @@ const fs = require('fs')
const path = require('path')
const childProcess = require('child_process')
const GitHubApi = require('github')
const {GitProcess} = require('dugite')
const request = require('request')
const assert = require('assert')
const rootPackageJson = require('../package.json')
if (!process.env.ELECTRON_NPM_OTP) {
console.error('Please set ELECTRON_NPM_OTP')
process.exit(1)
}
const github = new GitHubApi({
// debug: true,
headers: { 'User-Agent': 'electron-npm-publisher' },
@@ -68,7 +73,7 @@ new Promise((resolve, reject) => {
return github.repos.getReleases({
owner: 'electron',
repo: 'electron'
repo: rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron'
})
})
.then((releases) => {
@@ -103,8 +108,17 @@ new Promise((resolve, reject) => {
})
})
})
.then((release) => {
npmTag = release.prerelease ? 'beta' : 'latest'
.then(async (release) => {
if (release.tag_name.indexOf('nightly') > 0) {
const currentBranch = await getCurrentBranch()
if (currentBranch === 'master') {
npmTag = 'nightly'
} else {
npmTag = `nightly-${currentBranch}`
}
} else {
npmTag = release.prerelease ? 'beta' : 'latest'
}
})
.then(() => childProcess.execSync('npm pack', { cwd: tempDir }))
.then(() => {
@@ -115,13 +129,29 @@ new Promise((resolve, reject) => {
env: Object.assign({}, process.env, { electron_config_cache: tempDir }),
cwd: tempDir
})
const checkVersion = childProcess.execSync(`${path.join(tempDir, 'node_modules', '.bin', 'electron')} -v`)
assert.ok((`v${rootPackageJson.version}`.indexOf(checkVersion.toString().trim()) === 0), `Version is correct`)
resolve(tarballPath)
})
})
.then((tarballPath) => childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag}`))
.then((tarballPath) => childProcess.execSync(`npm publish ${tarballPath} --tag ${npmTag} --otp=${process.env.ELECTRON_NPM_OTP}`))
.catch((err) => {
console.error(`Error: ${err}`)
process.exit(1)
})
async function getCurrentBranch () {
const gitDir = path.resolve(__dirname, '..')
console.log(`Determining current git branch`)
let gitArgs = ['rev-parse', '--abbrev-ref', 'HEAD']
let branchDetails = await GitProcess.exec(gitArgs, gitDir)
if (branchDetails.exitCode === 0) {
let currentBranch = branchDetails.stdout.trim()
console.log(`Successfully determined current git branch is ` +
`${currentBranch}`)
return currentBranch
} else {
let error = GitProcess.parseError(branchDetails.stderr)
console.log(`Could not get details for the current branch,
error was ${branchDetails.stderr}`, error)
process.exit(1)
}
}

View File

@@ -0,0 +1,108 @@
#!/usr/bin/env node
if (!process.env.CI) require('dotenv-safe').load()
require('colors')
const args = require('minimist')(process.argv.slice(2), {
string: ['tag']
})
const { execSync } = require('child_process')
const { GitProcess } = require('dugite')
const GitHub = require('github')
const path = require('path')
const github = new GitHub()
const gitDir = path.resolve(__dirname, '..')
github.authenticate({
type: 'token',
token: process.env.ELECTRON_GITHUB_TOKEN
})
function getLastBumpCommit (tag) {
const data = execSync(`git log -n1 --grep "Bump ${tag}" --format='format:{"hash": "%H", "message": "%s"}'`).toString()
return JSON.parse(data)
}
async function getCurrentBranch (gitDir) {
const gitArgs = ['rev-parse', '--abbrev-ref', 'HEAD']
const branchDetails = await GitProcess.exec(gitArgs, gitDir)
if (branchDetails.exitCode === 0) {
return branchDetails.stdout.trim()
}
const error = GitProcess.parseError(branchDetails.stderr)
console.error(`Couldn't get current branch: `, error)
process.exit(1)
}
async function revertBumpCommit (tag) {
const branch = await getCurrentBranch()
const commitToRevert = getLastBumpCommit(tag).hash
await GitProcess.exec(['revert', commitToRevert], gitDir)
const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], gitDir)
if (pushDetails.exitCode === 0) {
console.log(`Successfully reverted release commit.`)
} else {
const error = GitProcess.parseError(pushDetails.stderr)
console.error(`Failed to push release commit: `, error)
process.exit(1)
}
}
async function deleteDraft (tag, targetRepo) {
try {
const result = await github.repos.getReleaseByTag({
owner: 'electron',
repo: targetRepo,
tag
})
if (!result.draft) {
console.log(`Published releases cannot be deleted.`)
process.exit(1)
} else {
await github.repos.deleteRelease({
owner: 'electron',
repo: targetRepo,
release_id: result.id
})
}
console.log(`Successfully deleted draft with tag ${tag} from ${targetRepo}`)
} catch (err) {
console.error(`Couldn't delete draft with tag ${tag} from ${targetRepo}: `, err)
process.exit(1)
}
}
async function deleteTag (tag, targetRepo) {
try {
await github.gitdata.deleteReference({
owner: 'electron',
repo: targetRepo,
ref: tag
})
console.log(`Successfully deleted tag ${tag} from ${targetRepo}`)
} catch (err) {
console.log(`Couldn't delete tag ${tag} from ${targetRepo}: `, err)
process.exit(1)
}
}
async function cleanReleaseArtifacts () {
const tag = args.tag
const isNightly = args.tag.includes('nightly')
if (isNightly) {
await deleteDraft(tag, 'nightlies')
await deleteTag(tag, 'nightlies')
} else {
await deleteDraft(tag, 'electron')
}
await deleteTag(tag, 'electron')
await revertBumpCommit(tag)
console.log('Failed release artifact cleanup complete')
}
cleanReleaseArtifacts()

View File

@@ -0,0 +1,478 @@
const { GitProcess } = require('dugite')
const Entities = require('html-entities').AllHtmlEntities
const fetch = require('node-fetch')
const fs = require('fs')
const GitHub = require('github')
const path = require('path')
const semver = require('semver')
const CACHE_DIR = path.resolve(__dirname, '.cache')
// Fill this with tags to ignore if you are generating release notes for older
// versions
//
// E.g. ['v3.0.0-beta.1'] to generate the release notes for 3.0.0-beta.1 :) from
// the current 3-0-x branch
const EXCLUDE_TAGS = []
const entities = new Entities()
const github = new GitHub()
const gitDir = path.resolve(__dirname, '..', '..')
github.authenticate({ type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN })
let currentBranch
const semanticMap = new Map()
for (const line of fs.readFileSync(path.resolve(__dirname, 'legacy-pr-semantic-map.csv'), 'utf8').split('\n')) {
if (!line) continue
const bits = line.split(',')
if (bits.length !== 2) continue
semanticMap.set(bits[0], bits[1])
}
const getCurrentBranch = async () => {
if (currentBranch) return currentBranch
const gitArgs = ['rev-parse', '--abbrev-ref', 'HEAD']
const branchDetails = await GitProcess.exec(gitArgs, gitDir)
if (branchDetails.exitCode === 0) {
currentBranch = branchDetails.stdout.trim()
return currentBranch
}
throw GitProcess.parseError(branchDetails.stderr)
}
const getBranchOffPoint = async (branchName) => {
const gitArgs = ['merge-base', branchName, 'master']
const commitDetails = await GitProcess.exec(gitArgs, gitDir)
if (commitDetails.exitCode === 0) {
return commitDetails.stdout.trim()
}
throw GitProcess.parseError(commitDetails.stderr)
}
const getTagsOnBranch = async (branchName) => {
const gitArgs = ['tag', '--merged', branchName]
const tagDetails = await GitProcess.exec(gitArgs, gitDir)
if (tagDetails.exitCode === 0) {
return tagDetails.stdout.trim().split('\n').filter(tag => !EXCLUDE_TAGS.includes(tag))
}
throw GitProcess.parseError(tagDetails.stderr)
}
const memLastKnownRelease = new Map()
const getLastKnownReleaseOnBranch = async (branchName) => {
if (memLastKnownRelease.has(branchName)) {
return memLastKnownRelease.get(branchName)
}
const tags = await getTagsOnBranch(branchName)
if (!tags.length) {
throw new Error(`Branch ${branchName} has no tags, we have no idea what the last release was`)
}
const branchOffPointTags = await getTagsOnBranch(await getBranchOffPoint(branchName))
if (branchOffPointTags.length >= tags.length) {
// No release on this branch
return null
}
memLastKnownRelease.set(branchName, tags[tags.length - 1])
// Latest tag is the latest release
return tags[tags.length - 1]
}
const getBranches = async () => {
const gitArgs = ['branch', '--remote']
const branchDetails = await GitProcess.exec(gitArgs, gitDir)
if (branchDetails.exitCode === 0) {
return branchDetails.stdout.trim().split('\n').map(b => b.trim()).filter(branch => branch !== 'origin/HEAD -> origin/master')
}
throw GitProcess.parseError(branchDetails.stderr)
}
const semverify = (v) => v.replace(/^origin\//, '').replace('x', '0').replace(/-/g, '.')
const getLastReleaseBranch = async () => {
const current = await getCurrentBranch()
const allBranches = await getBranches()
const releaseBranches = allBranches
.filter(branch => /^origin\/[0-9]+-[0-9]+-x$/.test(branch))
.filter(branch => branch !== current && branch !== `origin/${current}`)
let latest = null
for (const b of releaseBranches) {
if (latest === null) latest = b
if (semver.gt(semverify(b), semverify(latest))) {
latest = b
}
}
return latest
}
const commitBeforeTag = async (commit, tag) => {
const gitArgs = ['tag', '--contains', commit]
const tagDetails = await GitProcess.exec(gitArgs, gitDir)
if (tagDetails.exitCode === 0) {
return tagDetails.stdout.split('\n').includes(tag)
}
throw GitProcess.parseError(tagDetails.stderr)
}
const getCommitsMergedIntoCurrentBranchSincePoint = async (point) => {
return getCommitsBetween(point, 'HEAD')
}
const getCommitsBetween = async (point1, point2) => {
const gitArgs = ['rev-list', `${point1}..${point2}`]
const commitsDetails = await GitProcess.exec(gitArgs, gitDir)
if (commitsDetails.exitCode !== 0) {
throw GitProcess.parseError(commitsDetails.stderr)
}
return commitsDetails.stdout.trim().split('\n')
}
const TITLE_PREFIX = 'Merged Pull Request: '
const getCommitDetails = async (commitHash) => {
const commitInfo = await (await fetch(`https://github.com/electron/electron/branch_commits/${commitHash}`)).text()
const bits = commitInfo.split('</a>)')[0].split('>')
const prIdent = bits[bits.length - 1].trim()
if (!prIdent || commitInfo.indexOf('href="/electron/electron/pull') === -1) {
console.warn(`WARNING: Could not track commit "${commitHash}" to a pull request, it may have been committed directly to the branch`)
return null
}
const title = commitInfo.split('title="')[1].split('"')[0]
if (!title.startsWith(TITLE_PREFIX)) {
console.warn(`WARNING: Unknown PR title for commit "${commitHash}" in PR "${prIdent}"`)
return null
}
return {
mergedFrom: prIdent,
prTitle: entities.decode(title.substr(TITLE_PREFIX.length))
}
}
const doWork = async (items, fn, concurrent = 5) => {
const results = []
const toUse = [].concat(items)
let i = 1
const doBit = async () => {
if (toUse.length === 0) return
console.log(`Running ${i}/${items.length}`)
i += 1
const item = toUse.pop()
const index = toUse.length
results[index] = await fn(item)
await doBit()
}
const bits = []
for (let i = 0; i < concurrent; i += 1) {
bits.push(doBit())
}
await Promise.all(bits)
return results
}
const notes = new Map()
const NoteType = {
FIX: 'fix',
FEATURE: 'feature',
BREAKING_CHANGE: 'breaking-change',
DOCUMENTATION: 'doc',
OTHER: 'other',
UNKNOWN: 'unknown'
}
class Note {
constructor (trueTitle, prNumber, ignoreIfInVersion) {
// Self bindings
this.guessType = this.guessType.bind(this)
this.fetchPrInfo = this.fetchPrInfo.bind(this)
this._getPr = this._getPr.bind(this)
if (!trueTitle.trim()) console.error(prNumber)
this._ignoreIfInVersion = ignoreIfInVersion
this.reverted = false
if (notes.has(trueTitle)) {
console.warn(`Duplicate PR trueTitle: "${trueTitle}", "${prNumber}" this might cause weird reversions (this would be RARE)`)
}
// Memoize
notes.set(trueTitle, this)
this.originalTitle = trueTitle
this.title = trueTitle
this.prNumber = prNumber
this.stripColon = true
if (this.guessType() !== NoteType.UNKNOWN && this.stripColon) {
this.title = trueTitle.split(':').slice(1).join(':').trim()
}
}
guessType () {
if (this.originalTitle.startsWith('fix:') ||
this.originalTitle.startsWith('Fix:')) return NoteType.FIX
if (this.originalTitle.startsWith('feat:')) return NoteType.FEATURE
if (this.originalTitle.startsWith('spec:') ||
this.originalTitle.startsWith('build:') ||
this.originalTitle.startsWith('test:') ||
this.originalTitle.startsWith('chore:') ||
this.originalTitle.startsWith('deps:') ||
this.originalTitle.startsWith('refactor:') ||
this.originalTitle.startsWith('tools:') ||
this.originalTitle.startsWith('vendor:') ||
this.originalTitle.startsWith('perf:') ||
this.originalTitle.startsWith('style:') ||
this.originalTitle.startsWith('ci')) return NoteType.OTHER
if (this.originalTitle.startsWith('doc:') ||
this.originalTitle.startsWith('docs:')) return NoteType.DOCUMENTATION
this.stripColon = false
if (this.pr && this.pr.data.labels.find(label => label.name === 'semver/breaking-change')) {
return NoteType.BREAKING_CHANGE
}
// FIXME: Backported features will not be picked up by this
if (this.pr && this.pr.data.labels.find(label => label.name === 'semver/nonbreaking-feature')) {
return NoteType.FEATURE
}
const n = this.prNumber.replace('#', '')
if (semanticMap.has(n)) {
switch (semanticMap.get(n)) {
case 'feat':
return NoteType.FEATURE
case 'fix':
return NoteType.FIX
case 'breaking-change':
return NoteType.BREAKING_CHANGE
case 'doc':
return NoteType.DOCUMENTATION
case 'build':
case 'vendor':
case 'refactor':
case 'spec':
return NoteType.OTHER
default:
throw new Error(`Unknown semantic mapping: ${semanticMap.get(n)}`)
}
}
return NoteType.UNKNOWN
}
async _getPr (n) {
const cachePath = path.resolve(CACHE_DIR, n)
if (fs.existsSync(cachePath)) {
return JSON.parse(fs.readFileSync(cachePath, 'utf8'))
} else {
try {
const pr = await github.pullRequests.get({
number: n,
owner: 'electron',
repo: 'electron'
})
fs.writeFileSync(cachePath, JSON.stringify({ data: pr.data }))
return pr
} catch (err) {
console.info('#### FAILED:', `#${n}`)
throw err
}
}
}
async fetchPrInfo () {
if (this.pr) return
const n = this.prNumber.replace('#', '')
this.pr = await this._getPr(n)
if (this.pr.data.labels.find(label => label.name === `merged/${this._ignoreIfInVersion.replace('origin/', '')}`)) {
// This means we probably backported this PR, let's try figure out what
// the corresponding backport PR would be by searching through comments
// for trop
let comments
const cacheCommentsPath = path.resolve(CACHE_DIR, `${n}-comments`)
if (fs.existsSync(cacheCommentsPath)) {
comments = JSON.parse(fs.readFileSync(cacheCommentsPath, 'utf8'))
} else {
comments = await github.issues.getComments({
number: n,
owner: 'electron',
repo: 'electron',
per_page: 100
})
fs.writeFileSync(cacheCommentsPath, JSON.stringify({ data: comments.data }))
}
const tropComment = comments.data.find(
c => (
new RegExp(`We have automatically backported this PR to "${this._ignoreIfInVersion.replace('origin/', '')}", please check out #[0-9]+`)
).test(c.body)
)
if (tropComment) {
const commentBits = tropComment.body.split('#')
const tropPrNumber = commentBits[commentBits.length - 1]
const tropPr = await this._getPr(tropPrNumber)
if (tropPr.data.merged && tropPr.data.merge_commit_sha) {
if (await commitBeforeTag(tropPr.data.merge_commit_sha, await getLastKnownReleaseOnBranch(this._ignoreIfInVersion))) {
this.reverted = true
console.log('PR', this.prNumber, 'was backported to a previous version, ignoring from notes')
}
}
}
}
}
}
Note.findByTrueTitle = (trueTitle) => notes.get(trueTitle)
class ReleaseNotes {
constructor (ignoreIfInVersion) {
this._ignoreIfInVersion = ignoreIfInVersion
this._handledPrs = new Set()
this._revertedPrs = new Set()
this.other = []
this.docs = []
this.fixes = []
this.features = []
this.breakingChanges = []
this.unknown = []
}
async parseCommits (commitHashes) {
await doWork(commitHashes, async (commit) => {
const info = await getCommitDetails(commit)
if (!info) return
// Only handle each PR once
if (this._handledPrs.has(info.mergedFrom)) return
this._handledPrs.add(info.mergedFrom)
// Strip the trop backport prefix
const trueTitle = info.prTitle.replace(/^Backport \([0-9]+-[0-9]+-x\) - /, '')
if (this._revertedPrs.has(trueTitle)) return
// Handle PRs that revert other PRs
if (trueTitle.startsWith('Revert "')) {
const revertedTrueTitle = trueTitle.substr(8, trueTitle.length - 9)
this._revertedPrs.add(revertedTrueTitle)
const existingNote = Note.findByTrueTitle(revertedTrueTitle)
if (existingNote) {
existingNote.reverted = true
}
return
}
// Add a note for this PR
const note = new Note(trueTitle, info.mergedFrom, this._ignoreIfInVersion)
try {
await note.fetchPrInfo()
} catch (err) {
console.error(commit, info)
throw err
}
switch (note.guessType()) {
case NoteType.FIX:
this.fixes.push(note)
break
case NoteType.FEATURE:
this.features.push(note)
break
case NoteType.BREAKING_CHANGE:
this.breakingChanges.push(note)
break
case NoteType.OTHER:
this.other.push(note)
break
case NoteType.DOCUMENTATION:
this.docs.push(note)
break
case NoteType.UNKNOWN:
default:
this.unknown.push(note)
break
}
}, 20)
}
list (notes) {
if (notes.length === 0) {
return '_There are no items in this section this release_'
}
return notes
.filter(note => !note.reverted)
.sort((a, b) => a.title.toLowerCase().localeCompare(b.title.toLowerCase()))
.map((note) => `* ${note.title.trim()} ${note.prNumber}`).join('\n')
}
render () {
return `
# Release Notes
## Breaking Changes
${this.list(this.breakingChanges)}
## Features
${this.list(this.features)}
## Fixes
${this.list(this.fixes)}
## Other Changes (E.g. Internal refactors or build system updates)
${this.list(this.other)}
## Documentation Updates
Some documentation updates, fixes and reworks: ${
this.docs.length === 0
? '_None in this release_'
: this.docs.sort((a, b) => a.prNumber.localeCompare(b.prNumber)).map(note => note.prNumber).join(', ')
}
${this.unknown.filter(n => !n.reverted).length > 0
? `## Unknown (fix these before publishing release)
${this.list(this.unknown)}
` : ''}`
}
}
async function main () {
if (!fs.existsSync(CACHE_DIR)) {
fs.mkdirSync(CACHE_DIR)
}
const lastReleaseBranch = await getLastReleaseBranch()
const notes = new ReleaseNotes(lastReleaseBranch)
const lastKnownReleaseInCurrentStream = await getLastKnownReleaseOnBranch(await getCurrentBranch())
const currentBranchOff = await getBranchOffPoint(await getCurrentBranch())
const commits = await getCommitsMergedIntoCurrentBranchSincePoint(
lastKnownReleaseInCurrentStream || currentBranchOff
)
if (!lastKnownReleaseInCurrentStream) {
// This means we are the first release in our stream
// FIXME: This will not work for minor releases!!!!
const lastReleaseBranch = await getLastReleaseBranch()
const lastBranchOff = await getBranchOffPoint(lastReleaseBranch)
commits.push(...await getCommitsBetween(lastBranchOff, currentBranchOff))
}
await notes.parseCommits(commits)
console.log(notes.render())
const badNotes = notes.unknown.filter(n => !n.reverted).length
if (badNotes > 0) {
throw new Error(`You have ${badNotes.length} unknown release notes, please fix them before releasing`)
}
}
if (process.mainModule === module) {
main().catch((err) => {
console.error('Error Occurred:', err)
process.exit(1)
})
}

View File

@@ -0,0 +1,193 @@
12884,fix
12093,feat
12595,doc
12674,doc
12577,doc
12084,doc
12103,doc
12948,build
12496,feat
13133,build
12651,build
12767,doc
12238,build
12646,build
12373,doc
12723,feat
12202,doc
12504,doc
12669,doc
13044,feat
12746,spec
12617,doc
12532,feat
12619,feat
12118,build
12921,build
13281,doc
12059,feat
12131,doc
12123,doc
12080,build
12904,fix
12562,fix
12122,spec
12817,spec
12254,fix
12999,vendor
13248,vendor
12104,build
12477,feat
12648,refactor
12649,refactor
12650,refactor
12673,refactor
12305,refactor
12168,refactor
12627,refactor
12446,doc
12304,refactor
12615,breaking-change
12135,feat
12155,doc
12975,fix
12501,fix
13065,fix
13089,build
12786,doc
12736,doc
11966,doc
12885,fix
12984,refactor
12187,build
12535,refactor
12538,feat
12190,fix
12139,fix
11328,fix
12828,feat
12614,feat
12546,feat
12647,refactor
12987,build
12900,doc
12389,doc
12387,doc
12232,doc
12742,build
12043,fix
12741,fix
12995,fix
12395,fix
12003,build
12216,fix
12132,fix
12062,fix
12968,doc
12422,doc
12149,doc
13339,build
12044,fix
12327,fix
12180,fix
12263,spec
12153,spec
13055,feat
12113,doc
12067,doc
12882,build
13029,build
13067,doc
12196,build
12797,doc
12013,fix
12507,fix
11607,feat
12837,build
11613,feat
12015,spec
12058,doc
12403,spec
12192,feat
12204,doc
13294,doc
12542,doc
12826,refactor
12781,doc
12157,fix
12319,fix
12188,build
12399,doc
12145,doc
12661,refactor
8953,fix
12037,fix
12186,spec
12397,fix
12040,doc
12886,refactor
12008,refactor
12716,refactor
12750,refactor
12787,refactor
12858,refactor
12140,refactor
12503,refactor
12514,refactor
12584,refactor
12596,refactor
12637,refactor
12660,refactor
12696,refactor
12877,refactor
13030,refactor
12916,build
12896,build
13039,breaking-change
11927,build
12847,doc
12852,doc
12194,fix
12870,doc
12924,fix
12682,doc
12004,refactor
12601,refactor
12998,fix
13105,vendor
12452,doc
12738,fix
12536,refactor
12189,spec
13122,spec
12662,fix
12665,doc
12419,feat
12756,doc
12616,refactor
12679,breaking-change
12000,doc
12372,build
12805,build
12348,fix
12315,doc
12072,doc
12912,doc
12982,fix
12105,doc
12917,spec
12400,doc
12101,feat
12642,build
13058,fix
12913,vendor
13298,vendor
13042,build
11230,feat
11459,feat
12476,vendor
11937,doc
12328,build
12539,refactor
12127,build
12537,build
1 12884 fix
2 12093 feat
3 12595 doc
4 12674 doc
5 12577 doc
6 12084 doc
7 12103 doc
8 12948 build
9 12496 feat
10 13133 build
11 12651 build
12 12767 doc
13 12238 build
14 12646 build
15 12373 doc
16 12723 feat
17 12202 doc
18 12504 doc
19 12669 doc
20 13044 feat
21 12746 spec
22 12617 doc
23 12532 feat
24 12619 feat
25 12118 build
26 12921 build
27 13281 doc
28 12059 feat
29 12131 doc
30 12123 doc
31 12080 build
32 12904 fix
33 12562 fix
34 12122 spec
35 12817 spec
36 12254 fix
37 12999 vendor
38 13248 vendor
39 12104 build
40 12477 feat
41 12648 refactor
42 12649 refactor
43 12650 refactor
44 12673 refactor
45 12305 refactor
46 12168 refactor
47 12627 refactor
48 12446 doc
49 12304 refactor
50 12615 breaking-change
51 12135 feat
52 12155 doc
53 12975 fix
54 12501 fix
55 13065 fix
56 13089 build
57 12786 doc
58 12736 doc
59 11966 doc
60 12885 fix
61 12984 refactor
62 12187 build
63 12535 refactor
64 12538 feat
65 12190 fix
66 12139 fix
67 11328 fix
68 12828 feat
69 12614 feat
70 12546 feat
71 12647 refactor
72 12987 build
73 12900 doc
74 12389 doc
75 12387 doc
76 12232 doc
77 12742 build
78 12043 fix
79 12741 fix
80 12995 fix
81 12395 fix
82 12003 build
83 12216 fix
84 12132 fix
85 12062 fix
86 12968 doc
87 12422 doc
88 12149 doc
89 13339 build
90 12044 fix
91 12327 fix
92 12180 fix
93 12263 spec
94 12153 spec
95 13055 feat
96 12113 doc
97 12067 doc
98 12882 build
99 13029 build
100 13067 doc
101 12196 build
102 12797 doc
103 12013 fix
104 12507 fix
105 11607 feat
106 12837 build
107 11613 feat
108 12015 spec
109 12058 doc
110 12403 spec
111 12192 feat
112 12204 doc
113 13294 doc
114 12542 doc
115 12826 refactor
116 12781 doc
117 12157 fix
118 12319 fix
119 12188 build
120 12399 doc
121 12145 doc
122 12661 refactor
123 8953 fix
124 12037 fix
125 12186 spec
126 12397 fix
127 12040 doc
128 12886 refactor
129 12008 refactor
130 12716 refactor
131 12750 refactor
132 12787 refactor
133 12858 refactor
134 12140 refactor
135 12503 refactor
136 12514 refactor
137 12584 refactor
138 12596 refactor
139 12637 refactor
140 12660 refactor
141 12696 refactor
142 12877 refactor
143 13030 refactor
144 12916 build
145 12896 build
146 13039 breaking-change
147 11927 build
148 12847 doc
149 12852 doc
150 12194 fix
151 12870 doc
152 12924 fix
153 12682 doc
154 12004 refactor
155 12601 refactor
156 12998 fix
157 13105 vendor
158 12452 doc
159 12738 fix
160 12536 refactor
161 12189 spec
162 13122 spec
163 12662 fix
164 12665 doc
165 12419 feat
166 12756 doc
167 12616 refactor
168 12679 breaking-change
169 12000 doc
170 12372 build
171 12805 build
172 12348 fix
173 12315 doc
174 12072 doc
175 12912 doc
176 12982 fix
177 12105 doc
178 12917 spec
179 12400 doc
180 12101 feat
181 12642 build
182 13058 fix
183 12913 vendor
184 13298 vendor
185 13042 build
186 11230 feat
187 11459 feat
188 12476 vendor
189 11937 doc
190 12328 build
191 12539 refactor
192 12127 build
193 12537 build

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env node
if (!process.env.CI) require('dotenv-safe').load()
require('colors')
const args = require('minimist')(process.argv.slice(2))
const assert = require('assert')
const fs = require('fs')
const { execSync } = require('child_process')
const GitHub = require('github')
@@ -16,17 +16,16 @@ const fail = '\u2717'.red
const sumchecker = require('sumchecker')
const temp = require('temp').track()
const { URL } = require('url')
const targetRepo = pkgVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron'
let failureCount = 0
assert(process.env.ELECTRON_GITHUB_TOKEN, 'ELECTRON_GITHUB_TOKEN not found in environment')
const github = new GitHub({
followRedirects: false
})
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
async function getDraftRelease (version, skipValidation) {
let releaseInfo = await github.repos.getReleases({owner: 'electron', repo: 'electron'})
let releaseInfo = await github.repos.getReleases({owner: 'electron', repo: targetRepo})
let drafts
let versionToCheck
if (version) {
@@ -90,15 +89,12 @@ function assetsForVersion (version, validatingRelease) {
`electron-${version}-darwin-x64-dsym.zip`,
`electron-${version}-darwin-x64-symbols.zip`,
`electron-${version}-darwin-x64.zip`,
`electron-${version}-linux-arm-symbols.zip`,
`electron-${version}-linux-arm.zip`,
`electron-${version}-linux-arm64-symbols.zip`,
`electron-${version}-linux-arm64.zip`,
`electron-${version}-linux-armv7l-symbols.zip`,
`electron-${version}-linux-armv7l.zip`,
`electron-${version}-linux-ia32-symbols.zip`,
`electron-${version}-linux-ia32.zip`,
// `electron-${version}-linux-mips64el.zip`,
`electron-${version}-linux-x64-symbols.zip`,
`electron-${version}-linux-x64.zip`,
`electron-${version}-mas-x64-dsym.zip`,
@@ -113,11 +109,9 @@ function assetsForVersion (version, validatingRelease) {
`electron-api.json`,
`electron.d.ts`,
`ffmpeg-${version}-darwin-x64.zip`,
`ffmpeg-${version}-linux-arm.zip`,
`ffmpeg-${version}-linux-arm64.zip`,
`ffmpeg-${version}-linux-armv7l.zip`,
`ffmpeg-${version}-linux-ia32.zip`,
// `ffmpeg-${version}-linux-mips64el.zip`,
`ffmpeg-${version}-linux-x64.zip`,
`ffmpeg-${version}-mas-x64.zip`,
`ffmpeg-${version}-win32-ia32.zip`,
@@ -147,6 +141,8 @@ function s3UrlsForVersion (version) {
}
function checkVersion () {
if (args.skipVersionCheck) return
console.log(`Verifying that app version matches package version ${pkgVersion}.`)
let startScript = path.join(__dirname, 'start.py')
let scriptArgs = ['--version']
@@ -184,11 +180,7 @@ function uploadNodeShasums () {
function uploadIndexJson () {
console.log('Uploading index.json to S3.')
let scriptPath = path.join(__dirname, 'upload-index-json.py')
let scriptArgs = []
if (args.automaticRelease) {
scriptArgs.push('-R')
}
runScript(scriptPath, scriptArgs)
runScript(scriptPath, [pkgVersion])
console.log(`${pass} Done uploading index.json to S3.`)
}
@@ -199,7 +191,7 @@ async function createReleaseShasums (release) {
console.log(`${fileName} already exists on GitHub; deleting before creating new file.`)
await github.repos.deleteAsset({
owner: 'electron',
repo: 'electron',
repo: targetRepo,
id: existingAssets[0].id
}).catch(err => {
console.log(`${fail} Error deleting ${fileName} on GitHub:`, err)
@@ -218,7 +210,7 @@ async function createReleaseShasums (release) {
async function uploadShasumFile (filePath, fileName, release) {
let githubOpts = {
owner: 'electron',
repo: 'electron',
repo: targetRepo,
id: release.id,
filePath,
name: fileName
@@ -253,7 +245,7 @@ function saveShaSumFile (checksums, fileName) {
async function publishRelease (release) {
let githubOpts = {
owner: 'electron',
repo: 'electron',
repo: targetRepo,
id: release.id,
tag_name: release.tag_name,
draft: false
@@ -280,6 +272,7 @@ async function makeRelease (releaseToValidate) {
let draftRelease = await getDraftRelease()
uploadNodeShasums()
uploadIndexJson()
await createReleaseShasums(draftRelease)
// Fetch latest version of release before verifying
draftRelease = await getDraftRelease(pkgVersion, true)
@@ -307,7 +300,7 @@ async function verifyAssets (release) {
let downloadDir = await makeTempDir()
let githubOpts = {
owner: 'electron',
repo: 'electron',
repo: targetRepo,
headers: {
Accept: 'application/octet-stream'
}

View File

@@ -8,10 +8,10 @@
# Do NOT CHANGE this if you don't know what you're doing -- see
# https://code.google.com/p/chromium/wiki/UpdatingClang
# Reverting problematic clang rolls is safe, though.
CLANG_REVISION=307486
CLANG_REVISION=308728
# This is incremented when pushing a new build of Clang at the same revision.
CLANG_SUB_REVISION=1
CLANG_SUB_REVISION=3
PACKAGE_VERSION="${CLANG_REVISION}-${CLANG_SUB_REVISION}"

View File

@@ -2,39 +2,49 @@
import os
import sys
import urllib2
from lib.config import PLATFORM, s3_config
from lib.util import electron_gyp, execute, s3put, scoped_cwd
from lib.config import s3_config
from lib.util import s3put, scoped_cwd, safe_mkdir
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
OUT_DIR = os.path.join(SOURCE_ROOT, 'out', 'D')
PROJECT_NAME = electron_gyp()['project_name%']
PRODUCT_NAME = electron_gyp()['product_name%']
BASE_URL = 'https://electron-metadumper.herokuapp.com/?version='
version = sys.argv[1]
authToken = os.getenv('META_DUMPER_AUTH_HEADER')
def get_content(retry_count = 5):
try:
request = urllib2.Request(
BASE_URL + version,
headers={"Authorization" : authToken}
)
return urllib2.urlopen(
request
).read()
except Exception as e:
if retry_count == 0:
raise e
return get_content(retry_count - 1)
def main():
if not authToken or authToken == "":
raise Exception("Please set META_DUMPER_AUTH_HEADER")
# Upload the index.json.
with scoped_cwd(SOURCE_ROOT):
if len(sys.argv) == 2 and sys.argv[1] == '-R':
config = 'R'
else:
config = 'D'
out_dir = os.path.join(SOURCE_ROOT, 'out', config)
if sys.platform == 'darwin':
electron = os.path.join(out_dir, '{0}.app'.format(PRODUCT_NAME),
'Contents', 'MacOS', PRODUCT_NAME)
elif sys.platform == 'win32':
electron = os.path.join(out_dir, '{0}.exe'.format(PROJECT_NAME))
else:
electron = os.path.join(out_dir, PROJECT_NAME)
index_json = os.path.relpath(os.path.join(out_dir, 'index.json'))
execute([electron,
os.path.join('tools', 'dump-version-info.js'),
index_json])
safe_mkdir(OUT_DIR)
index_json = os.path.relpath(os.path.join(OUT_DIR, 'index.json'))
new_content = get_content()
with open(index_json, "w") as f:
f.write(new_content)
bucket, access_key, secret_key = s3_config()
s3put(bucket, access_key, secret_key, out_dir, 'atom-shell/dist',
s3put(bucket, access_key, secret_key, OUT_DIR, 'atom-shell/dist',
[index_json])

View File

@@ -1,18 +1,23 @@
if (!process.env.CI) require('dotenv-safe').load()
const GitHub = require('github')
const github = new GitHub()
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
if (process.argv.length < 5) {
if (process.argv.length < 6) {
console.log('Usage: upload-to-github filePath fileName releaseId')
process.exit(1)
}
let filePath = process.argv[2]
let fileName = process.argv[3]
let releaseId = process.argv[4]
let releaseVersion = process.argv[5]
const targetRepo = releaseVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron'
let githubOpts = {
owner: 'electron',
repo: 'electron',
repo: targetRepo,
id: releaseId,
filePath: filePath,
name: fileName
@@ -34,7 +39,7 @@ function uploadToGitHub () {
console.log(`${fileName} already exists; will delete before retrying upload.`)
github.repos.deleteAsset({
owner: 'electron',
repo: 'electron',
repo: targetRepo,
id: existingAssets[0].id
}).then(uploadToGitHub).catch(uploadToGitHub)
} else {

View File

@@ -166,7 +166,7 @@ def upload_electron(release, file_path, args):
return
# Upload the file.
upload_io_to_github(release, filename, file_path)
upload_io_to_github(release, filename, file_path, args.version)
# Upload the checksum file.
upload_sha256_checksum(args.version, file_path)
@@ -180,11 +180,12 @@ def upload_electron(release, file_path, args):
upload_electron(release, arm_file_path, args)
def upload_io_to_github(release, filename, filepath):
def upload_io_to_github(release, filename, filepath, version):
print 'Uploading %s to Github' % \
(filename)
script_path = os.path.join(SOURCE_ROOT, 'script', 'upload-to-github.js')
execute(['node', script_path, filepath, filename, str(release['id'])])
execute(['node', script_path, filepath, filename, str(release['id']),
version])
def upload_sha256_checksum(version, file_path, key_prefix=None):

View File

@@ -417,31 +417,13 @@ describe('app module', () => {
app.setLoginItemSettings({openAtLogin: false, path: updateExe, args: processStartArgs})
})
it('returns the login item status of the app', (done) => {
app.setLoginItemSettings({openAtLogin: true})
assert.deepEqual(app.getLoginItemSettings(), {
openAtLogin: true,
openAsHidden: false,
wasOpenedAtLogin: false,
wasOpenedAsHidden: false,
restoreState: false
})
app.setLoginItemSettings({openAtLogin: true, openAsHidden: true})
assert.deepEqual(app.getLoginItemSettings(), {
openAtLogin: true,
openAsHidden: process.platform === 'darwin' && !process.mas, // Only available on macOS
wasOpenedAtLogin: false,
wasOpenedAsHidden: false,
restoreState: false
})
app.setLoginItemSettings({})
it('sets and returns the app as a login item', done => {
app.setLoginItemSettings({ openAtLogin: true })
// Wait because login item settings are not applied immediately in MAS build
const delay = process.mas ? 100 : 0
const delay = process.mas ? 150 : 0
setTimeout(() => {
assert.deepEqual(app.getLoginItemSettings(), {
openAtLogin: false,
openAtLogin: true,
openAsHidden: false,
wasOpenedAtLogin: false,
wasOpenedAsHidden: false,
@@ -451,6 +433,32 @@ describe('app module', () => {
}, delay)
})
it('adds a login item that loads in hidden mode', () => {
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true })
assert.deepEqual(app.getLoginItemSettings(), {
openAtLogin: true,
openAsHidden: process.platform === 'darwin' && !process.mas, // Only available on macOS
wasOpenedAtLogin: false,
wasOpenedAsHidden: false,
restoreState: false
})
})
it('correctly sets and unsets the LoginItem as hidden', function () {
if (process.platform !== 'darwin' || process.mas) this.skip()
assert.equal(app.getLoginItemSettings().openAtLogin, false)
assert.equal(app.getLoginItemSettings().openAsHidden, false)
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: true })
assert.equal(app.getLoginItemSettings().openAtLogin, true)
assert.equal(app.getLoginItemSettings().openAsHidden, true)
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false })
assert.equal(app.getLoginItemSettings().openAtLogin, true)
assert.equal(app.getLoginItemSettings().openAsHidden, false)
})
it('allows you to pass a custom executable and arguments', function () {
if (process.platform !== 'win32') {
// FIXME(alexeykuzmin): Skip the test.

View File

@@ -2349,9 +2349,15 @@ describe('BrowserWindow module', () => {
// Only implemented on windows.
if (process.platform !== 'win32') return
it('is set to false when resizable state is set to false', () => {
it('is reset to its former state', () => {
w.setMaximizable(false)
w.setResizable(false)
w.setResizable(true)
assert.equal(w.isMaximizable(), false)
w.setMaximizable(true)
w.setResizable(false)
w.setResizable(true)
assert.strictEqual(w.isMaximizable(), true)
})
})

25
spec/api-tray-spec.js Normal file
View File

@@ -0,0 +1,25 @@
const {remote} = require('electron')
const {Menu, Tray, nativeImage} = remote
describe('tray module', () => {
describe('tray.setContextMenu', () => {
let tray
beforeEach(() => {
tray = new Tray(nativeImage.createEmpty())
})
afterEach(() => {
tray.destroy()
tray = null
})
it('accepts menu instance', () => {
tray.setContextMenu(new Menu())
})
it('accepts null', () => {
tray.setContextMenu(null)
})
})
})

View File

@@ -2,6 +2,8 @@ const assert = require('assert')
const ChildProcess = require('child_process')
const fs = require('fs')
const path = require('path')
const temp = require('temp').track()
const util = require('util')
const {closeWindow} = require('./window-helpers')
const nativeImage = require('electron').nativeImage
@@ -135,6 +137,44 @@ describe('asar package', function () {
})
})
describe('fs.copyFile', function () {
it('copies a normal file', function (done) {
const p = path.join(fixtures, 'asar', 'a.asar', 'file1')
const dest = temp.path()
fs.copyFile(p, dest, function (err) {
assert.strictEqual(err, null)
assert(fs.readFileSync(p).equals(fs.readFileSync(dest)))
done()
})
})
it('copies a unpacked file', function (done) {
const p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt')
const dest = temp.path()
fs.copyFile(p, dest, function (err) {
assert.strictEqual(err, null)
assert(fs.readFileSync(p).equals(fs.readFileSync(dest)))
done()
})
})
})
describe('fs.copyFileSync', function () {
it('copies a normal file', function () {
const p = path.join(fixtures, 'asar', 'a.asar', 'file1')
const dest = temp.path()
fs.copyFileSync(p, dest)
assert(fs.readFileSync(p).equals(fs.readFileSync(dest)))
})
it('copies a unpacked file', function () {
const p = path.join(fixtures, 'asar', 'unpack.asar', 'a.txt')
const dest = temp.path()
fs.copyFileSync(p, dest)
assert(fs.readFileSync(p).equals(fs.readFileSync(dest)))
})
})
describe('fs.lstatSync', function () {
it('handles path with trailing slash correctly', function () {
var p = path.join(fixtures, 'asar', 'a.asar', 'link2', 'link2', 'file1')
@@ -549,6 +589,60 @@ describe('asar package', function () {
})
})
describe('fs.exists', function () {
it('handles an existing file', function (done) {
var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
// eslint-disable-next-line
fs.exists(p, function (exists) {
assert.equal(exists, true)
done()
})
})
it('handles a non-existent file', function (done) {
var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
// eslint-disable-next-line
fs.exists(p, function (exists) {
assert.equal(exists, false)
done()
})
})
it('promisified version handles an existing file', (done) => {
var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
// eslint-disable-next-line
util.promisify(fs.exists)(p).then(exists => {
assert.equal(exists, true)
done()
})
})
it('promisified version handles a non-existent file', function (done) {
var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
// eslint-disable-next-line
util.promisify(fs.exists)(p).then(exists => {
assert.equal(exists, false)
done()
})
})
})
describe('fs.existsSync', function () {
it('handles an existing file', function () {
var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
assert.doesNotThrow(function () {
assert.equal(fs.existsSync(p), true)
})
})
it('handles a non-existent file', function () {
var p = path.join(fixtures, 'asar', 'a.asar', 'not-exist')
assert.doesNotThrow(function () {
assert.equal(fs.existsSync(p), false)
})
})
})
describe('fs.access', function () {
it('accesses a normal file', function (done) {
var p = path.join(fixtures, 'asar', 'a.asar', 'file1')
@@ -644,6 +738,12 @@ describe('asar package', function () {
done()
})
})
it('can be promisified', () => {
return util.promisify(ChildProcess.exec)('echo ' + echo + ' foo bar').then(({ stdout }) => {
assert.equal(stdout.toString().replace(/\r/g, ''), echo + ' foo bar\n')
})
})
})
describe('child_process.execSync', function () {
@@ -680,6 +780,12 @@ describe('asar package', function () {
var output = execFileSync(echo, ['test'])
assert.equal(String(output), 'test\n')
})
it('can be promisified', () => {
return util.promisify(ChildProcess.execFile)(echo, ['test']).then(({ stdout }) => {
assert.equal(stdout, 'test\n')
})
})
})
describe('internalModuleReadFile', function () {
@@ -700,6 +806,18 @@ describe('asar package', function () {
})
})
describe('util.promisify', function () {
it('can promisify all fs functions', function () {
const originalFs = require('original-fs')
for (const key in originalFs) {
if (originalFs[key][util.promisify.custom] && !fs[key][util.promisify.custom]) {
assert(false, `fs.${key}[util.promisify.custom] missing`)
}
}
})
})
describe('process.noAsar', function () {
var errorName = process.platform === 'win32' ? 'ENOENT' : 'ENOTDIR'

View File

@@ -10,27 +10,32 @@ if (!process.argv.includes('--enable-mixed-sandbox')) {
app.enableMixedSandbox()
}
let sandboxWindow
let noSandboxWindow
let currentWindowSandboxed = false
app.once('ready', () => {
sandboxWindow = new BrowserWindow({
show: false,
webPreferences: {
preload: path.join(__dirname, 'electron-app-mixed-sandbox-preload.js'),
sandbox: true
}
})
sandboxWindow.loadURL('about:blank')
noSandboxWindow = new BrowserWindow({
show: false,
webPreferences: {
preload: path.join(__dirname, 'electron-app-mixed-sandbox-preload.js'),
sandbox: false
}
})
noSandboxWindow.loadURL('about:blank')
function testWindow (isSandboxed, callback) {
currentWindowSandboxed = isSandboxed
let currentWindow = new BrowserWindow({
show: false,
webPreferences: {
preload: path.join(__dirname, 'electron-app-mixed-sandbox-preload.js'),
sandbox: isSandboxed
}
})
currentWindow.loadURL('about:blank')
currentWindow.webContents.once('devtools-opened', () => {
if (isSandboxed) {
argv.sandboxDevtools = true
} else {
argv.noSandboxDevtools = true
}
if (callback) {
callback()
}
finish()
})
currentWindow.webContents.openDevTools()
}
const argv = {
sandbox: null,
@@ -41,6 +46,10 @@ app.once('ready', () => {
let connected = false
testWindow(true, () => {
testWindow()
})
function finish () {
if (connected && argv.sandbox != null && argv.noSandbox != null &&
argv.noSandboxDevtools != null && argv.sandboxDevtools != null) {
@@ -57,22 +66,10 @@ app.once('ready', () => {
finish()
})
noSandboxWindow.webContents.once('devtools-opened', () => {
argv.noSandboxDevtools = true
finish()
})
noSandboxWindow.webContents.openDevTools()
sandboxWindow.webContents.once('devtools-opened', () => {
argv.sandboxDevtools = true
finish()
})
sandboxWindow.webContents.openDevTools()
ipcMain.on('argv', (event, value) => {
if (event.sender === sandboxWindow.webContents) {
if (currentWindowSandboxed) {
argv.sandbox = value
} else if (event.sender === noSandboxWindow.webContents) {
} else {
argv.noSandbox = value
}
finish()

2
vendor/node vendored

View File

@@ -2,29 +2,21 @@ resources:
- repo: self
steps:
- bash: |
if [ "$ELECTRON_RELEASE" == "1" ]; then
echo 'Bootstrapping Electron for release build'
script/bootstrap.py --target_arch=$TARGET_ARCH
else
echo 'Bootstrapping Electron for debug build'
script/bootstrap.py --target_arch=$TARGET_ARCH --dev
fi
echo 'Non release VSTS builds do not run on older branches'
displayName: Skip build on older branch
condition: ne(variables['ELECTRON_RELEASE'], '1')
- bash: |
echo 'Bootstrapping Electron for release build'
script/bootstrap.py --target_arch=$TARGET_ARCH
name: Bootstrap
condition: eq(variables['ELECTRON_RELEASE'], '1')
- bash: |
npm run lint
name: Lint
condition: and(succeeded(), ne(variables['ELECTRON_RELEASE'], '1'))
- bash: |
if [ "$ELECTRON_RELEASE" == "1" ]; then
echo 'Building Electron for release'
script/build.py -c R
else
echo 'Building Electron for debug'
script/build.py -c D
fi
echo 'Building Electron for release'
script/build.py -c R
name: Build
condition: and(succeeded(), eq(variables['ELECTRON_RELEASE'], '1'))
- bash: |
echo 'Creating Electron release distribution'
@@ -43,40 +35,11 @@ steps:
name: Upload_distribution
condition: and(succeeded(), eq(variables['ELECTRON_RELEASE'], '1'))
- bash: |
echo 'Testing Electron build'
mkdir junit
export MOCHA_FILE="junit/test-results.xml"
export MOCHA_REPORTER="mocha-junit-reporter"
if [ "$ELECTRON_RELEASE" == "1" ]; then
script/test.py --ci --rebuild_native_modules -c R
else
script/test.py --ci --rebuild_native_modules
fi
name: Test
- bash: |
echo 'Verifying ffmpeg on build'
if [ "$ELECTRON_RELEASE" == "1" ]; then
script/verify-ffmpeg.py -R
else
script/verify-ffmpeg.py
fi
name: Verify_FFmpeg
- task: PublishTestResults@2
displayName: Publish Test Results
- task: PublishBuildArtifacts@1
displayName: Publish Build Artifacts
inputs:
testResultsFiles: 'test-results.xml'
searchFolder: junit
condition: and(always(), ne(variables['ELECTRON_RELEASE'], '1'))
- task: kasunkodagoda.slack-notification.slack-notification-task.SlackNotification@3
displayName: Post Slack Notification
inputs:
SlackApiToken: '$(slack_token)'
Channel: '#bot-nightly-releases'
Message: '$(Build.DefinitionName)-$(Build.BuildNumber) finished with a $(Agent.JobStatus) status.'
condition: and(always(), eq(variables['Build.Reason'], 'Schedule'))
PathtoPublish: '$(Build.SourcesDirectory)/out'
ArtifactName: out
condition: and(succeeded(), eq(variables['ELECTRON_RELEASE'], '1'))
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3