From 74321dce74259d18163cbbb318aec431bf05c5a1 Mon Sep 17 00:00:00 2001 From: Charlie Hess Date: Mon, 20 Jun 2016 17:48:42 -0700 Subject: [PATCH 001/192] Add systemPreferences.subscribeLocalNotification. (#6150) --- .../api/atom_api_system_preferences.cc | 4 ++ .../browser/api/atom_api_system_preferences.h | 18 ++++-- .../api/atom_api_system_preferences_mac.mm | 61 +++++++++++++------ docs/api/system-preferences.md | 11 ++++ 4 files changed, 71 insertions(+), 23 deletions(-) diff --git a/atom/browser/api/atom_api_system_preferences.cc b/atom/browser/api/atom_api_system_preferences.cc index 2b11aad25278..c3421365f0c8 100644 --- a/atom/browser/api/atom_api_system_preferences.cc +++ b/atom/browser/api/atom_api_system_preferences.cc @@ -53,6 +53,10 @@ void SystemPreferences::BuildPrototype( &SystemPreferences::SubscribeNotification) .SetMethod("unsubscribeNotification", &SystemPreferences::UnsubscribeNotification) + .SetMethod("subscribeLocalNotification", + &SystemPreferences::SubscribeLocalNotification) + .SetMethod("unsubscribeLocalNotification", + &SystemPreferences::UnsubscribeLocalNotification) .SetMethod("getUserDefault", &SystemPreferences::GetUserDefault) #endif .SetMethod("isDarkMode", &SystemPreferences::IsDarkMode); diff --git a/atom/browser/api/atom_api_system_preferences.h b/atom/browser/api/atom_api_system_preferences.h index 7779ce007816..9a5cf990357a 100644 --- a/atom/browser/api/atom_api_system_preferences.h +++ b/atom/browser/api/atom_api_system_preferences.h @@ -26,17 +26,18 @@ class SystemPreferences : public mate::EventEmitter { static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); -#if defined(OS_MACOSX) - using NotificationCallback = base::Callback< - void(const std::string&, const base::DictionaryValue&)>; -#endif - #if defined(OS_WIN) bool IsAeroGlassEnabled(); #elif defined(OS_MACOSX) + using NotificationCallback = base::Callback< + void(const std::string&, const base::DictionaryValue&)>; + int SubscribeNotification(const std::string& name, const NotificationCallback& callback); void UnsubscribeNotification(int id); + int SubscribeLocalNotification(const std::string& name, + const NotificationCallback& callback); + void UnsubscribeLocalNotification(int request_id); v8::Local GetUserDefault(const std::string& name, const std::string& type); #endif @@ -46,6 +47,13 @@ class SystemPreferences : public mate::EventEmitter { explicit SystemPreferences(v8::Isolate* isolate); ~SystemPreferences() override; +#if defined(OS_MACOSX) + int DoSubscribeNotification(const std::string& name, + const NotificationCallback& callback, + bool is_local); + void DoUnsubscribeNotification(int request_id, bool is_local); +#endif + private: DISALLOW_COPY_AND_ASSIGN(SystemPreferences); }; diff --git a/atom/browser/api/atom_api_system_preferences_mac.mm b/atom/browser/api/atom_api_system_preferences_mac.mm index 72011500bd04..6f7055ab9135 100644 --- a/atom/browser/api/atom_api_system_preferences_mac.mm +++ b/atom/browser/api/atom_api_system_preferences_mac.mm @@ -30,34 +30,59 @@ std::map g_id_map; int SystemPreferences::SubscribeNotification( const std::string& name, const NotificationCallback& callback) { + return DoSubscribeNotification(name, callback, false); +} + +void SystemPreferences::UnsubscribeNotification(int request_id) { + DoUnsubscribeNotification(request_id, false); +} + +int SystemPreferences::SubscribeLocalNotification( + const std::string& name, const NotificationCallback& callback) { + return DoSubscribeNotification(name, callback, true); +} + +void SystemPreferences::UnsubscribeLocalNotification(int request_id) { + DoUnsubscribeNotification(request_id, true); +} + +int SystemPreferences::DoSubscribeNotification(const std::string& name, + const NotificationCallback& callback, bool is_local) { int request_id = g_next_id++; __block NotificationCallback copied_callback = callback; - g_id_map[request_id] = [[NSDistributedNotificationCenter defaultCenter] - addObserverForName:base::SysUTF8ToNSString(name) - object:nil - queue:nil - usingBlock:^(NSNotification* notification) { - std::unique_ptr user_info = - NSDictionaryToDictionaryValue(notification.userInfo); - if (user_info) { - copied_callback.Run( - base::SysNSStringToUTF8(notification.name), - *user_info); - } else { - copied_callback.Run( - base::SysNSStringToUTF8(notification.name), - base::DictionaryValue()); - } + NSNotificationCenter* center = is_local ? + [NSNotificationCenter defaultCenter] : + [NSDistributedNotificationCenter defaultCenter]; + + g_id_map[request_id] = [center + addObserverForName:base::SysUTF8ToNSString(name) + object:nil + queue:nil + usingBlock:^(NSNotification* notification) { + std::unique_ptr user_info = + NSDictionaryToDictionaryValue(notification.userInfo); + if (user_info) { + copied_callback.Run( + base::SysNSStringToUTF8(notification.name), + *user_info); + } else { + copied_callback.Run( + base::SysNSStringToUTF8(notification.name), + base::DictionaryValue()); } + } ]; return request_id; } -void SystemPreferences::UnsubscribeNotification(int request_id) { +void SystemPreferences::DoUnsubscribeNotification(int request_id, bool is_local) { auto iter = g_id_map.find(request_id); if (iter != g_id_map.end()) { id observer = iter->second; - [[NSDistributedNotificationCenter defaultCenter] removeObserver:observer]; + NSNotificationCenter* center = is_local ? + [NSNotificationCenter defaultCenter] : + [NSDistributedNotificationCenter defaultCenter]; + [center removeObserver:observer]; g_id_map.erase(iter); } } diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md index a47975d0974b..586422e56654 100644 --- a/docs/api/system-preferences.md +++ b/docs/api/system-preferences.md @@ -40,6 +40,17 @@ example values of `event` are: Removes the subscriber with `id`. +### `systemPreferences.subscribeLocalNotification(event, callback)` _macOS_ + +Same as `subscribeNotification`, but uses `NSNotificationCenter` for local defaults. +This is necessary for events such as: + +* `NSUserDefaultsDidChangeNotification` + +### `systemPreferences.unsubscribeLocalNotification(id)` _macOS_ + +Same as `unsubscribeNotification`, but removes the subscriber from `NSNotificationCenter`. + ### `systemPreferences.getUserDefault(key, type)` _macOS_ * `key` String From 8a9f2261d0d8a5b73353a23c876897bdea180567 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:54:15 -0700 Subject: [PATCH 002/192] Add default error handler to remote promises (#6151) * Add failing spec for unhandled main process exception * Remove unused return * Use let/const instead of var * Add spec for unhandled rejection in renderer process * Prevent unhandled rejection defaul * Use once instead of on * Add default fulfilled/rejection handler to promise --- lib/browser/rpc-server.js | 23 ++++++++++-------- spec/api-ipc-spec.js | 27 +++++++++++++++++++++ spec/fixtures/module/unhandled-rejection.js | 3 +++ 3 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 spec/fixtures/module/unhandled-rejection.js diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index 0b954e518b95..083c5e4d03e0 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -54,7 +54,7 @@ let getObjectPrototype = function (object) { // Convert a real value into meta data. let valueToMeta = function (sender, value, optimizeSimpleObject = false) { // Determine the type of value. - let meta = { type: typeof value } + const meta = { type: typeof value } if (meta.type === 'object') { // Recognize certain types of objects. if (value === null) { @@ -93,6 +93,10 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) { } else if (meta.type === 'buffer') { meta.value = Array.prototype.slice.call(value, 0) } else if (meta.type === 'promise') { + // Add default handler to prevent unhandled rejections in main process + // Instead they should appear in the renderer process + value.then(function () {}, function () {}) + meta.then = valueToMeta(sender, function (onFulfilled, onRejected) { value.then(onFulfilled, onRejected) }) @@ -114,7 +118,7 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) { } // Convert object to meta by value. -var plainObjectToMeta = function (obj) { +const plainObjectToMeta = function (obj) { return Object.getOwnPropertyNames(obj).map(function (name) { return { name: name, @@ -124,7 +128,7 @@ var plainObjectToMeta = function (obj) { } // Convert Error into meta data. -var exceptionToMeta = function (error) { +const exceptionToMeta = function (error) { return { type: 'exception', message: error.message, @@ -133,10 +137,9 @@ var exceptionToMeta = function (error) { } // Convert array of meta data from renderer into array of real values. -var unwrapArgs = function (sender, args) { - var metaToValue - metaToValue = function (meta) { - var i, len, member, ref, returnValue +const unwrapArgs = function (sender, args) { + const metaToValue = function (meta) { + let i, len, member, ref, returnValue switch (meta.type) { case 'value': return meta.value @@ -200,8 +203,8 @@ var unwrapArgs = function (sender, args) { // Call a function and send reply asynchronously if it's a an asynchronous // style function and the caller didn't pass a callback. -var callFunction = function (event, func, caller, args) { - var funcMarkedAsync, funcName, funcPassedCallback, ref, ret +const callFunction = function (event, func, caller, args) { + let funcMarkedAsync, funcName, funcPassedCallback, ref, ret funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous') funcPassedCallback = typeof args[args.length - 1] === 'function' try { @@ -209,7 +212,7 @@ var callFunction = function (event, func, caller, args) { args.push(function (ret) { event.returnValue = valueToMeta(event.sender, ret, true) }) - return func.apply(caller, args) + func.apply(caller, args) } else { ret = func.apply(caller, args) event.returnValue = valueToMeta(event.sender, ret, true) diff --git a/spec/api-ipc-spec.js b/spec/api-ipc-spec.js index e88815873af9..cbf58fe8eaa8 100644 --- a/spec/api-ipc-spec.js +++ b/spec/api-ipc-spec.js @@ -126,6 +126,33 @@ describe('ipc module', function () { done() }) }) + + it('does not emit unhandled rejection events in the main process', function (done) { + remote.process.once('unhandledRejection', function (reason) { + done(reason) + }) + + var promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js')) + promise.reject().then(function () { + done(new Error('Promise was not rejected')) + }).catch(function (error) { + assert.equal(error.message, 'rejected') + done() + }) + }) + + it('emits unhandled rejection events in the renderer process', function (done) { + window.addEventListener('unhandledrejection', function (event) { + event.preventDefault() + assert.equal(event.reason.message, 'rejected') + done() + }) + + var promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js')) + promise.reject().then(function () { + done(new Error('Promise was not rejected')) + }) + }) }) describe('remote webContents', function () { diff --git a/spec/fixtures/module/unhandled-rejection.js b/spec/fixtures/module/unhandled-rejection.js new file mode 100644 index 000000000000..6cb870ec8862 --- /dev/null +++ b/spec/fixtures/module/unhandled-rejection.js @@ -0,0 +1,3 @@ +exports.reject = function () { + return Promise.reject(new Error('rejected')) +} From 9993aab5b13922a6e71ad9b16bf1c1c576e8bd15 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 21 Jun 2016 12:24:25 +0900 Subject: [PATCH 003/192] Update brightray for electron/brightray#228 --- vendor/brightray | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/brightray b/vendor/brightray index 4e4113ee4348..3a98173c6848 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 4e4113ee43484c6058ced9defa9b905e0e7339a6 +Subproject commit 3a98173c6848bac241074979b24f5a54cc92b5b0 From db98e256f283c3b79e1184bfa924f2ee99ce338c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 21 Jun 2016 15:40:30 +0900 Subject: [PATCH 004/192] mac: Add tray.getBounds() API --- atom/browser/api/atom_api_tray.cc | 7 ++++++- atom/browser/api/atom_api_tray.h | 1 + atom/browser/ui/tray_icon.cc | 4 ++++ atom/browser/ui/tray_icon.h | 4 ++++ atom/browser/ui/tray_icon_cocoa.h | 1 + atom/browser/ui/tray_icon_cocoa.mm | 23 +++++++++++++---------- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/atom/browser/api/atom_api_tray.cc b/atom/browser/api/atom_api_tray.cc index c84e8a1d66b6..85c0578a8e89 100644 --- a/atom/browser/api/atom_api_tray.cc +++ b/atom/browser/api/atom_api_tray.cc @@ -159,6 +159,10 @@ void Tray::SetContextMenu(v8::Isolate* isolate, mate::Handle menu) { tray_icon_->SetContextMenu(menu->model()); } +gfx::Rect Tray::GetBounds() { + return tray_icon_->GetBounds(); +} + v8::Local Tray::ModifiersToObject(v8::Isolate* isolate, int modifiers) { mate::Dictionary obj(isolate, v8::Object::New(isolate)); @@ -181,7 +185,8 @@ void Tray::BuildPrototype(v8::Isolate* isolate, .SetMethod("setHighlightMode", &Tray::SetHighlightMode) .SetMethod("displayBalloon", &Tray::DisplayBalloon) .SetMethod("popUpContextMenu", &Tray::PopUpContextMenu) - .SetMethod("setContextMenu", &Tray::SetContextMenu); + .SetMethod("setContextMenu", &Tray::SetContextMenu) + .SetMethod("getBounds", &Tray::GetBounds); } } // namespace api diff --git a/atom/browser/api/atom_api_tray.h b/atom/browser/api/atom_api_tray.h index a6c329567ca3..0bc28af818cf 100644 --- a/atom/browser/api/atom_api_tray.h +++ b/atom/browser/api/atom_api_tray.h @@ -65,6 +65,7 @@ class Tray : public mate::TrackableObject, void DisplayBalloon(mate::Arguments* args, const mate::Dictionary& options); void PopUpContextMenu(mate::Arguments* args); void SetContextMenu(v8::Isolate* isolate, mate::Handle menu); + gfx::Rect GetBounds(); private: v8::Local ModifiersToObject(v8::Isolate* isolate, int modifiers); diff --git a/atom/browser/ui/tray_icon.cc b/atom/browser/ui/tray_icon.cc index dcdb90ac159c..fda68b09cd11 100644 --- a/atom/browser/ui/tray_icon.cc +++ b/atom/browser/ui/tray_icon.cc @@ -30,6 +30,10 @@ void TrayIcon::PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) { } +gfx::Rect TrayIcon::GetBounds() { + return gfx::Rect(); +} + void TrayIcon::NotifyClicked(const gfx::Rect& bounds, int modifiers) { FOR_EACH_OBSERVER(TrayIconObserver, observers_, OnClicked(bounds, modifiers)); } diff --git a/atom/browser/ui/tray_icon.h b/atom/browser/ui/tray_icon.h index 1916c11b23af..2763e50941b6 100644 --- a/atom/browser/ui/tray_icon.h +++ b/atom/browser/ui/tray_icon.h @@ -60,8 +60,12 @@ class TrayIcon { // Set the context menu for this icon. virtual void SetContextMenu(ui::SimpleMenuModel* menu_model) = 0; + // Returns the bounds of tray icon. + virtual gfx::Rect GetBounds(); + void AddObserver(TrayIconObserver* obs) { observers_.AddObserver(obs); } void RemoveObserver(TrayIconObserver* obs) { observers_.RemoveObserver(obs); } + void NotifyClicked(const gfx::Rect& = gfx::Rect(), int modifiers = 0); void NotifyDoubleClicked(const gfx::Rect& = gfx::Rect(), int modifiers = 0); void NotifyBalloonShow(); diff --git a/atom/browser/ui/tray_icon_cocoa.h b/atom/browser/ui/tray_icon_cocoa.h index 59e2241aa480..cb972d54a9af 100644 --- a/atom/browser/ui/tray_icon_cocoa.h +++ b/atom/browser/ui/tray_icon_cocoa.h @@ -32,6 +32,7 @@ class TrayIconCocoa : public TrayIcon, void PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) override; void SetContextMenu(ui::SimpleMenuModel* menu_model) override; + gfx::Rect GetBounds() override; protected: // AtomMenuModel::Observer: diff --git a/atom/browser/ui/tray_icon_cocoa.mm b/atom/browser/ui/tray_icon_cocoa.mm index 0dfd59132ae8..8b25aa3540ff 100644 --- a/atom/browser/ui/tray_icon_cocoa.mm +++ b/atom/browser/ui/tray_icon_cocoa.mm @@ -8,6 +8,7 @@ #include "base/strings/sys_string_conversions.h" #include "ui/events/cocoa/cocoa_event_utils.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/mac/coordinate_conversion.h" #include "ui/gfx/screen.h" namespace { @@ -236,13 +237,13 @@ const CGFloat kVerticalTitleMargin = 2; // Single click event. if (event.clickCount == 1) trayIcon_->NotifyClicked( - [self getBoundsFromEvent:event], + gfx::ScreenRectFromNSRect(event.window.frame), ui::EventFlagsFromModifiers([event modifierFlags])); // Double click event. if (event.clickCount == 2) trayIcon_->NotifyDoubleClicked( - [self getBoundsFromEvent:event], + gfx::ScreenRectFromNSRect(event.window.frame), ui::EventFlagsFromModifiers([event modifierFlags])); [self setNeedsDisplay:YES]; @@ -273,7 +274,7 @@ const CGFloat kVerticalTitleMargin = 2; - (void)rightMouseUp:(NSEvent*)event { trayIcon_->NotifyRightClicked( - [self getBoundsFromEvent:event], + gfx::ScreenRectFromNSRect(event.window.frame), ui::EventFlagsFromModifiers([event modifierFlags])); } @@ -324,13 +325,6 @@ const CGFloat kVerticalTitleMargin = 2; return isHighlightEnable_ && (inMouseEventSequence_ || isMenuOpen); } -- (gfx::Rect)getBoundsFromEvent:(NSEvent*)event { - NSRect frame = event.window.frame; - gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame)); - NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; - bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame)); - return bounds; -} @end namespace atom { @@ -386,6 +380,15 @@ void TrayIconCocoa::SetContextMenu(ui::SimpleMenuModel* menu_model) { [status_item_view_ setMenuController:menu_.get()]; } +gfx::Rect TrayIconCocoa::GetBounds() { + auto bounds = gfx::ScreenRectFromNSRect([status_item_view_ window].frame); + // Calling [window frame] immediately after the view gets created will have + // negative |y| sometimes. + if (bounds.y() < 0) + bounds.set_y(0); + return bounds; +} + void TrayIconCocoa::MenuClosed() { [status_item_view_ setNeedsDisplay:YES]; } From 886ef1717c992eacfb827ff755ab3122b75fb12d Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 21 Jun 2016 15:49:22 +0900 Subject: [PATCH 005/192] win: Implement tray.getBounds() API --- atom/browser/ui/win/notify_icon.cc | 27 ++++++++++++++++----------- atom/browser/ui/win/notify_icon.h | 1 + 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/atom/browser/ui/win/notify_icon.cc b/atom/browser/ui/win/notify_icon.cc index 1cc616216c6e..d12b328b0d0c 100644 --- a/atom/browser/ui/win/notify_icon.cc +++ b/atom/browser/ui/win/notify_icon.cc @@ -48,26 +48,19 @@ NotifyIcon::~NotifyIcon() { void NotifyIcon::HandleClickEvent(int modifiers, bool left_mouse_click, bool double_button_click) { - NOTIFYICONIDENTIFIER icon_id; - memset(&icon_id, 0, sizeof(NOTIFYICONIDENTIFIER)); - icon_id.uID = icon_id_; - icon_id.hWnd = window_; - icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER); - - RECT rect = { 0 }; - Shell_NotifyIconGetRect(&icon_id, &rect); + gfx::Rect bounds = GetBounds(); if (left_mouse_click) { if (double_button_click) // double left click - NotifyDoubleClicked(gfx::Rect(rect), modifiers); + NotifyDoubleClicked(bounds, modifiers); else // single left click - NotifyClicked(gfx::Rect(rect), modifiers); + NotifyClicked(bounds, modifiers); return; } else if (!double_button_click) { // single right click if (menu_model_) PopUpContextMenu(gfx::Point(), menu_model_); else - NotifyRightClicked(gfx::Rect(rect), modifiers); + NotifyRightClicked(bounds, modifiers); } } @@ -163,6 +156,18 @@ void NotifyIcon::SetContextMenu(ui::SimpleMenuModel* menu_model) { menu_model_ = menu_model; } +gfx::Rect NotifyIcon::GetBounds() { + NOTIFYICONIDENTIFIER icon_id; + memset(&icon_id, 0, sizeof(NOTIFYICONIDENTIFIER)); + icon_id.uID = icon_id_; + icon_id.hWnd = window_; + icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER); + + RECT rect = { 0 }; + Shell_NotifyIconGetRect(&icon_id, &rect); + return gfx::Rect(rect); +} + void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) { memset(icon_data, 0, sizeof(NOTIFYICONDATA)); icon_data->cbSize = sizeof(NOTIFYICONDATA); diff --git a/atom/browser/ui/win/notify_icon.h b/atom/browser/ui/win/notify_icon.h index 95e9945a17f8..1284ebadcf1c 100644 --- a/atom/browser/ui/win/notify_icon.h +++ b/atom/browser/ui/win/notify_icon.h @@ -54,6 +54,7 @@ class NotifyIcon : public TrayIcon { void PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) override; void SetContextMenu(ui::SimpleMenuModel* menu_model) override; + gfx::Rect GetBounds() override; private: void InitIconData(NOTIFYICONDATA* icon_data); From 324153d3949f7a366962a6f518430164df782db0 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 21 Jun 2016 16:05:28 +0900 Subject: [PATCH 006/192] win: Use DIP rect for tray icon's bounds --- atom/browser/ui/win/notify_icon.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/atom/browser/ui/win/notify_icon.cc b/atom/browser/ui/win/notify_icon.cc index d12b328b0d0c..0ccf46b83212 100644 --- a/atom/browser/ui/win/notify_icon.cc +++ b/atom/browser/ui/win/notify_icon.cc @@ -13,6 +13,7 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/screen.h" +#include "ui/gfx/win/dpi.h" #include "ui/views/controls/menu/menu_runner.h" namespace atom { @@ -165,7 +166,7 @@ gfx::Rect NotifyIcon::GetBounds() { RECT rect = { 0 }; Shell_NotifyIconGetRect(&icon_id, &rect); - return gfx::Rect(rect); + return gfx::win::ScreenToDIPRect(gfx::Rect(rect)); } void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) { From 11f242a2411da03466fc0d5f8183ad1bed4d72ab Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 21 Jun 2016 16:13:50 +0900 Subject: [PATCH 007/192] docs: Tray.getBounds() --- docs/api/tray.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/api/tray.md b/docs/api/tray.md index 902444e5336b..7e0cc65a8f2f 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -3,20 +3,20 @@ > Add icons and context menus to the system's notification area. ```javascript -const {app, Menu, Tray} = require('electron'); +const {app, Menu, Tray} = require('electron') -let appIcon = null; +let appIcon = null app.on('ready', () => { - appIcon = new Tray('/path/to/my/icon'); + appIcon = new Tray('/path/to/my/icon') const contextMenu = Menu.buildFromTemplate([ {label: 'Item1', type: 'radio'}, {label: 'Item2', type: 'radio'}, {label: 'Item3', type: 'radio', checked: true}, {label: 'Item4', type: 'radio'} ]); - appIcon.setToolTip('This is my application.'); - appIcon.setContextMenu(contextMenu); -}); + appIcon.setToolTip('This is my application.') + appIcon.setContextMenu(contextMenu) +}) ``` __Platform limitations:__ @@ -208,4 +208,14 @@ The `position` is only available on Windows, and it is (0, 0) by default. Sets the context menu for this icon. +### `Tray.getBounds()` _macOS_ _Windows_ + +Returns the `bounds` of this tray icon as `Object`. + +* `bounds` Object + * `x` Integer + * `y` Integer + * `width` Integer + * `height` Integer + [event-emitter]: http://nodejs.org/api/events.html#events_class_events_eventemitter From 9d63199c373ba5c0c864535b7f613f95431e6faa Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 21 Jun 2016 16:20:33 +0900 Subject: [PATCH 008/192] docs: Make tray.md follow docs style guide --- docs/api/tray.md | 66 +++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/docs/api/tray.md b/docs/api/tray.md index 7e0cc65a8f2f..aed699b8ee43 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -5,17 +5,17 @@ ```javascript const {app, Menu, Tray} = require('electron') -let appIcon = null +let tray = null app.on('ready', () => { - appIcon = new Tray('/path/to/my/icon') + tray = new Tray('/path/to/my/icon') const contextMenu = Menu.buildFromTemplate([ {label: 'Item1', type: 'radio'}, {label: 'Item2', type: 'radio'}, {label: 'Item3', type: 'radio', checked: true}, {label: 'Item4', type: 'radio'} ]); - appIcon.setToolTip('This is my application.') - appIcon.setContextMenu(contextMenu) + tray.setToolTip('This is my application.') + tray.setContextMenu(contextMenu) }) ``` @@ -49,21 +49,18 @@ rely on the `click` event and always attach a context menu to the tray icon. Creates a new tray icon associated with the `image`. -## Events +### Instance Events The `Tray` module emits the following events: -**Note:** Some events are only available on specific operating systems and are -labeled as such. - -### Event: 'click' +#### Event: 'click' * `event` Event * `altKey` Boolean * `shiftKey` Boolean * `ctrlKey` Boolean * `metaKey` Boolean -* `bounds` Object - the bounds of tray icon. +* `bounds` Object _macOS_ _Windows_ - the bounds of tray icon. * `x` Integer * `y` Integer * `width` Integer @@ -71,9 +68,7 @@ labeled as such. Emitted when the tray icon is clicked. -**Note:** The `bounds` payload is only implemented on macOS and Windows. - -### Event: 'right-click' _macOS_ _Windows_ +#### Event: 'right-click' _macOS_ _Windows_ * `event` Event * `altKey` Boolean @@ -88,7 +83,7 @@ Emitted when the tray icon is clicked. Emitted when the tray icon is right clicked. -### Event: 'double-click' _macOS_ _Windows_ +#### Event: 'double-click' _macOS_ _Windows_ * `event` Event * `altKey` Boolean @@ -103,85 +98,82 @@ Emitted when the tray icon is right clicked. Emitted when the tray icon is double clicked. -### Event: 'balloon-show' _Windows_ +#### Event: 'balloon-show' _Windows_ Emitted when the tray balloon shows. -### Event: 'balloon-click' _Windows_ +#### Event: 'balloon-click' _Windows_ Emitted when the tray balloon is clicked. -### Event: 'balloon-closed' _Windows_ +#### Event: 'balloon-closed' _Windows_ Emitted when the tray balloon is closed because of timeout or user manually closes it. -### Event: 'drop' _macOS_ +#### Event: 'drop' _macOS_ Emitted when any dragged items are dropped on the tray icon. -### Event: 'drop-files' _macOS_ +#### Event: 'drop-files' _macOS_ * `event` * `files` Array - the file path of dropped files. Emitted when dragged files are dropped in the tray icon. -### Event: 'drag-enter' _macOS_ +#### Event: 'drag-enter' _macOS_ Emitted when a drag operation enters the tray icon. -### Event: 'drag-leave' _macOS_ +#### Event: 'drag-leave' _macOS_ Emitted when a drag operation exits the tray icon. -### Event: 'drag-end' _macOS_ +#### Event: 'drag-end' _macOS_ Emitted when a drag operation ends on the tray or ends at another location. -## Methods +### Instance Methods -The `Tray` module has the following methods: +The `Tray` class has the following methods: -**Note:** Some methods are only available on specific operating systems and are -labeled as such. - -### `Tray.destroy()` +#### `tray.destroy()` Destroys the tray icon immediately. -### `Tray.setImage(image)` +#### `tray.setImage(image)` * `image` [NativeImage](native-image.md) Sets the `image` associated with this tray icon. -### `Tray.setPressedImage(image)` _macOS_ +#### `tray.setPressedImage(image)` _macOS_ * `image` [NativeImage](native-image.md) Sets the `image` associated with this tray icon when pressed on macOS. -### `Tray.setToolTip(toolTip)` +#### `tray.setToolTip(toolTip)` * `toolTip` String Sets the hover text for this tray icon. -### `Tray.setTitle(title)` _macOS_ +#### `tray.setTitle(title)` _macOS_ * `title` String Sets the title displayed aside of the tray icon in the status bar. -### `Tray.setHighlightMode(highlight)` _macOS_ +#### `tray.setHighlightMode(highlight)` _macOS_ * `highlight` Boolean Sets whether the tray icon's background becomes highlighted (in blue) when the tray icon is clicked. Defaults to true. -### `Tray.displayBalloon(options)` _Windows_ +#### `tray.displayBalloon(options)` _Windows_ * `options` Object * `icon` [NativeImage](native-image.md) @@ -190,7 +182,7 @@ when the tray icon is clicked. Defaults to true. Displays a tray balloon. -### `Tray.popUpContextMenu([menu, position])` _macOS_ _Windows_ +#### `tray.popUpContextMenu([menu, position])` _macOS_ _Windows_ * `menu` Menu (optional) * `position` Object (optional) - The pop up position. @@ -202,13 +194,13 @@ be shown instead of the tray icon's context menu. The `position` is only available on Windows, and it is (0, 0) by default. -### `Tray.setContextMenu(menu)` +#### `tray.setContextMenu(menu)` * `menu` Menu Sets the context menu for this icon. -### `Tray.getBounds()` _macOS_ _Windows_ +#### `tray.getBounds()` _macOS_ _Windows_ Returns the `bounds` of this tray icon as `Object`. From 816b2ea8f259e098f737c27d969abb23060c9139 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 21 Jun 2016 08:54:55 +0000 Subject: [PATCH 009/192] linux: Disable parent window when showing modal dialogs (#6160) --- atom/browser/ui/file_dialog_gtk.cc | 17 ++++++++++------- atom/browser/ui/message_box_gtk.cc | 16 ++++++++++------ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/atom/browser/ui/file_dialog_gtk.cc b/atom/browser/ui/file_dialog_gtk.cc index 1ad7ff5f2d41..dcb7bea86b2b 100644 --- a/atom/browser/ui/file_dialog_gtk.cc +++ b/atom/browser/ui/file_dialog_gtk.cc @@ -4,7 +4,7 @@ #include "atom/browser/ui/file_dialog.h" -#include "atom/browser/native_window.h" +#include "atom/browser/native_window_views.h" #include "base/callback.h" #include "base/files/file_util.h" #include "base/strings/string_util.h" @@ -40,7 +40,8 @@ class FileChooserDialog { const std::string& button_label, const base::FilePath& default_path, const Filters& filters) - : dialog_scope_(parent_window), + : parent_(static_cast(parent_window)), + dialog_scope_(parent_window), filters_(filters) { const char* confirm_text = GTK_STOCK_OK; @@ -58,9 +59,10 @@ class FileChooserDialog { GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, confirm_text, GTK_RESPONSE_ACCEPT, NULL); - if (parent_window) { - gfx::NativeWindow window = parent_window->GetNativeWindow(); - libgtk2ui::SetGtkTransientForAura(dialog_, window); + if (parent_) { + parent_->SetEnabled(false); + libgtk2ui::SetGtkTransientForAura(dialog_, parent_->GetNativeWindow()); + gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); } if (action == GTK_FILE_CHOOSER_ACTION_SAVE) @@ -69,8 +71,6 @@ class FileChooserDialog { if (action != GTK_FILE_CHOOSER_ACTION_OPEN) gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dialog_), TRUE); - gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); - if (!default_path.empty()) { if (base::DirectoryExists(default_path)) { gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog_), @@ -89,6 +89,8 @@ class FileChooserDialog { virtual ~FileChooserDialog() { gtk_widget_destroy(dialog_); + if (parent_) + parent_->SetEnabled(true); } void RunAsynchronous() { @@ -143,6 +145,7 @@ class FileChooserDialog { void AddFilters(const Filters& filters); base::FilePath AddExtensionForFilename(const gchar* filename) const; + atom::NativeWindowViews* parent_; atom::NativeWindow::DialogScope dialog_scope_; GtkWidget* dialog_; diff --git a/atom/browser/ui/message_box_gtk.cc b/atom/browser/ui/message_box_gtk.cc index b09ccdd48279..d08171c6e426 100644 --- a/atom/browser/ui/message_box_gtk.cc +++ b/atom/browser/ui/message_box_gtk.cc @@ -5,7 +5,7 @@ #include "atom/browser/ui/message_box.h" #include "atom/browser/browser.h" -#include "atom/browser/native_window.h" +#include "atom/browser/native_window_views.h" #include "base/callback.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -36,7 +36,8 @@ class GtkMessageBox { const std::string& detail, const gfx::ImageSkia& icon) : dialog_scope_(parent_window), - cancel_id_(cancel_id) { + cancel_id_(cancel_id), + parent_(static_cast(parent_window)) { // Create dialog. dialog_ = gtk_message_dialog_new( nullptr, // parent @@ -75,14 +76,17 @@ class GtkMessageBox { } // Parent window. - if (parent_window) { - gfx::NativeWindow window = parent_window->GetNativeWindow(); - libgtk2ui::SetGtkTransientForAura(dialog_, window); + if (parent_) { + parent_->SetEnabled(false); + libgtk2ui::SetGtkTransientForAura(dialog_, parent_->GetNativeWindow()); + gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); } } ~GtkMessageBox() { gtk_widget_destroy(dialog_); + if (parent_) + parent_->SetEnabled(true); } GtkMessageType GetMessageType(MessageBoxType type) { @@ -123,7 +127,6 @@ class GtkMessageBox { } int RunSynchronous() { - gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); Show(); int response = gtk_dialog_run(GTK_DIALOG(dialog_)); if (response < 0) @@ -149,6 +152,7 @@ class GtkMessageBox { // The id to return when the dialog is closed without pressing buttons. int cancel_id_; + NativeWindowViews* parent_; GtkWidget* dialog_; MessageBoxCallback callback_; From aca37c897439171344924f79d8d20978b893cf42 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 21 Jun 2016 18:05:01 +0900 Subject: [PATCH 010/192] Update menubar color when theme changes --- atom/browser/ui/views/menu_bar.cc | 23 +++++++++++++++-------- atom/browser/ui/views/menu_bar.h | 4 +++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/atom/browser/ui/views/menu_bar.cc b/atom/browser/ui/views/menu_bar.cc index 872375ccbbd1..796e6ef80457 100644 --- a/atom/browser/ui/views/menu_bar.cc +++ b/atom/browser/ui/views/menu_bar.cc @@ -50,14 +50,7 @@ void GetMenuBarColor(SkColor* enabled, SkColor* disabled, SkColor* highlight, MenuBar::MenuBar() : background_color_(kDefaultColor), menu_model_(NULL) { -#if defined(OS_WIN) - background_color_ = color_utils::GetSysSkColor(COLOR_MENUBAR); -#elif defined(USE_X11) - GetMenuBarColor(&enabled_color_, &disabled_color_, &highlight_color_, - &hover_color_, &background_color_); -#endif - - set_background(views::Background::CreateSolidBackground(background_color_)); + UpdateMenuBarColor(); SetLayoutManager(new views::BoxLayout( views::BoxLayout::kHorizontal, 0, 0, 0)); } @@ -159,4 +152,18 @@ void MenuBar::OnMenuButtonClicked(views::MenuButton* source, menu_delegate.RunMenu(menu_model_->GetSubmenuModelAt(id), source); } +void MenuBar::OnNativeThemeChanged(const ui::NativeTheme* theme) { + UpdateMenuBarColor(); +} + +void MenuBar::UpdateMenuBarColor() { +#if defined(OS_WIN) + background_color_ = color_utils::GetSysSkColor(COLOR_MENUBAR); +#elif defined(USE_X11) + GetMenuBarColor(&enabled_color_, &disabled_color_, &highlight_color_, + &hover_color_, &background_color_); +#endif + set_background(views::Background::CreateSolidBackground(background_color_)); +} + } // namespace atom diff --git a/atom/browser/ui/views/menu_bar.h b/atom/browser/ui/views/menu_bar.h index 9f38a5981c7f..eb62784e71ca 100644 --- a/atom/browser/ui/views/menu_bar.h +++ b/atom/browser/ui/views/menu_bar.h @@ -60,9 +60,11 @@ class MenuBar : public views::View, void OnMenuButtonClicked(views::MenuButton* source, const gfx::Point& point, const ui::Event* event) override; - + void OnNativeThemeChanged(const ui::NativeTheme* theme) override; private: + void UpdateMenuBarColor(); + SkColor background_color_; #if defined(USE_X11) From a16d907684fca735ca1b5a402adabb6cefdd0bdd Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:03:48 -0700 Subject: [PATCH 011/192] Add quit role --- lib/browser/api/menu-item.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 4d2737d0fa98..5ff96766c9ca 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -15,7 +15,8 @@ rolesMap = { selectall: 'selectAll', minimize: 'minimize', close: 'close', - delete: 'delete' + delete: 'delete', + quit: 'quit' } // Maps methods that should be called directly on the BrowserWindow instance @@ -24,12 +25,16 @@ methodInBrowserWindow = { close: true } +const methodInApp = { + quit: true +} + MenuItem = (function () { MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] function MenuItem (options) { var click, ref - const Menu = require('electron').Menu + const {app, Menu} = require('electron') click = options.click this.selector = options.selector this.type = options.type @@ -73,7 +78,9 @@ MenuItem = (function () { } if (this.role && rolesMap[this.role] && process.platform !== 'darwin' && (focusedWindow != null)) { methodName = rolesMap[this.role] - if (methodInBrowserWindow[methodName]) { + if(methodInApp[methodName]) { + return app[methodName]() + } else if (methodInBrowserWindow[methodName]) { return focusedWindow[methodName]() } else { return (ref2 = focusedWindow.webContents) != null ? ref2[methodName]() : void 0 From 67f7b4d4c583798ab0037d735732e1befad49030 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:03:59 -0700 Subject: [PATCH 012/192] Map quit role to terminate selector --- atom/browser/ui/cocoa/atom_menu_controller.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/atom/browser/ui/cocoa/atom_menu_controller.mm b/atom/browser/ui/cocoa/atom_menu_controller.mm index 642721e6bd35..26cf2a94edb3 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.mm +++ b/atom/browser/ui/cocoa/atom_menu_controller.mm @@ -38,6 +38,7 @@ Role kRolesMap[] = { { @selector(performMiniaturize:), "minimize" }, { @selector(performClose:), "close" }, { @selector(performZoom:), "zoom" }, + { @selector(terminate:), "quit" }, }; } // namespace From 7a6436014e9a3d67faf84d979c4fec7938b7a532 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:04:03 -0700 Subject: [PATCH 013/192] Use quit role --- default_app/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default_app/main.js b/default_app/main.js index 4e5ea96b03eb..0fdf30bf8258 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -214,7 +214,7 @@ app.once('ready', () => { { label: 'Quit ' + app.getName(), accelerator: 'Command+Q', - click () { app.quit() } + role: 'quit' } ] }) From 11ba7eaa91d8e9d1d55746a485ccb2d1940a6aa0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:08:45 -0700 Subject: [PATCH 014/192] Add File > Exit menu on Windows --- default_app/main.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/default_app/main.js b/default_app/main.js index 0fdf30bf8258..4c3c65842094 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -243,6 +243,18 @@ app.once('ready', () => { ] } + if (process.platform === 'win32') { + template.unshift({ + label: 'File', + submenu: [ + { + label: 'Exit', + role: 'quit' + } + ] + }) + } + const menu = Menu.buildFromTemplate(template) Menu.setApplicationMenu(menu) }) From 2d8ec60eedecc0aa53d14a41f763719bc6288002 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:14:16 -0700 Subject: [PATCH 015/192] Use const/let instead of var --- lib/browser/api/menu-item.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 5ff96766c9ca..15ede39609cc 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -1,11 +1,9 @@ 'use strict' -var MenuItem, methodInBrowserWindow, nextCommandId, rolesMap - -nextCommandId = 0 +let nextCommandId = 0 // Maps role to methods of webContents -rolesMap = { +const rolesMap = { undo: 'undo', redo: 'redo', cut: 'cut', @@ -20,7 +18,7 @@ rolesMap = { } // Maps methods that should be called directly on the BrowserWindow instance -methodInBrowserWindow = { +const methodInBrowserWindow = { minimize: true, close: true } @@ -29,11 +27,11 @@ const methodInApp = { quit: true } -MenuItem = (function () { +const MenuItem = (function () { MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] function MenuItem (options) { - var click, ref + let click, ref const {app, Menu} = require('electron') click = options.click this.selector = options.selector @@ -72,7 +70,7 @@ MenuItem = (function () { this.commandId = ++nextCommandId this.click = (focusedWindow) => { // Manually flip the checked flags when clicked. - var methodName, ref1, ref2 + let methodName, ref1, ref2 if ((ref1 = this.type) === 'checkbox' || ref1 === 'radio') { this.checked = !this.checked } From 32ea5a5e27e1d99e6bef46a3b44d9761ff261873 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:16:34 -0700 Subject: [PATCH 016/192] Remove CoffeeScript ref variables --- lib/browser/api/menu-item.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 15ede39609cc..7b5a688cb179 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -31,9 +31,9 @@ const MenuItem = (function () { MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] function MenuItem (options) { - let click, ref const {app, Menu} = require('electron') - click = options.click + + const click = options.click this.selector = options.selector this.type = options.type this.role = options.role @@ -51,7 +51,7 @@ const MenuItem = (function () { if ((this.type == null) && (this.submenu != null)) { this.type = 'submenu' } - if (this.type === 'submenu' && ((ref = this.submenu) != null ? ref.constructor : void 0) !== Menu) { + if (this.type === 'submenu' && (this.submenu != null ? this.submenu.constructor : void 0) !== Menu) { throw new Error('Invalid submenu') } this.overrideReadOnlyProperty('type', 'normal') @@ -70,18 +70,19 @@ const MenuItem = (function () { this.commandId = ++nextCommandId this.click = (focusedWindow) => { // Manually flip the checked flags when clicked. - let methodName, ref1, ref2 - if ((ref1 = this.type) === 'checkbox' || ref1 === 'radio') { + if (this.type === 'checkbox' || this.type === 'radio') { this.checked = !this.checked } + if (this.role && rolesMap[this.role] && process.platform !== 'darwin' && (focusedWindow != null)) { - methodName = rolesMap[this.role] + const methodName = rolesMap[this.role] if(methodInApp[methodName]) { return app[methodName]() } else if (methodInBrowserWindow[methodName]) { return focusedWindow[methodName]() } else { - return (ref2 = focusedWindow.webContents) != null ? ref2[methodName]() : void 0 + const {webContents} = focusedWindow + return webContents != null ? webContents[methodName]() : void 0 } } else if (typeof click === 'function') { return click(this, focusedWindow) From 395c3727020792a9a8975d4d8b5aa5f788f7fc93 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 20 Jun 2016 17:20:57 -0700 Subject: [PATCH 017/192] Document quit role --- docs/api/menu-item.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index ec3fef55e576..391b639e119e 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -51,6 +51,7 @@ The `role` property can have following values: * `delete` * `minimize` - Minimize current window * `close` - Close current window +* `quit`- Quit the application On macOS `role` can also have following additional values: From fbfac6d49bd49c90106e68ae78e71d9789459ac3 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 09:07:56 -0700 Subject: [PATCH 018/192] Fix lint errors --- lib/browser/api/menu-item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 7b5a688cb179..f38ab970d695 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -76,7 +76,7 @@ const MenuItem = (function () { if (this.role && rolesMap[this.role] && process.platform !== 'darwin' && (focusedWindow != null)) { const methodName = rolesMap[this.role] - if(methodInApp[methodName]) { + if (methodInApp[methodName]) { return app[methodName]() } else if (methodInBrowserWindow[methodName]) { return focusedWindow[methodName]() From e3e1ed41ca8c9910064c6c829c541b8d308e622a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 09:31:17 -0700 Subject: [PATCH 019/192] Add togglefullscreen role --- lib/browser/api/menu-item.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index f38ab970d695..de5aab28ef10 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -14,13 +14,17 @@ const rolesMap = { minimize: 'minimize', close: 'close', delete: 'delete', - quit: 'quit' + quit: 'quit', + togglefullscreen: 'toggleFullScreen' } // Maps methods that should be called directly on the BrowserWindow instance const methodInBrowserWindow = { minimize: true, - close: true + close: true, + toggleFullScreen: function (window) { + window.setFullScreen(!window.isFullScreen()) + } } const methodInApp = { @@ -79,7 +83,11 @@ const MenuItem = (function () { if (methodInApp[methodName]) { return app[methodName]() } else if (methodInBrowserWindow[methodName]) { - return focusedWindow[methodName]() + if (typeof methodName === 'function') { + return methodName(focusedWindow) + } else { + return focusedWindow[methodName]() + } } else { const {webContents} = focusedWindow return webContents != null ? webContents[methodName]() : void 0 From 2536e62fb05dcb3580d170f7efbfd7a0d5d5112f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 09:34:12 -0700 Subject: [PATCH 020/192] Add togglefullscreen role selector --- atom/browser/ui/cocoa/atom_menu_controller.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/atom/browser/ui/cocoa/atom_menu_controller.mm b/atom/browser/ui/cocoa/atom_menu_controller.mm index 26cf2a94edb3..536f48760589 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.mm +++ b/atom/browser/ui/cocoa/atom_menu_controller.mm @@ -39,6 +39,7 @@ Role kRolesMap[] = { { @selector(performClose:), "close" }, { @selector(performZoom:), "zoom" }, { @selector(terminate:), "quit" }, + { @selector(toggleFullScreen:), "togglefullscreen" }, }; } // namespace From 3cdf36c53630e766eb9a4b0f92f92995d7a43476 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 09:34:41 -0700 Subject: [PATCH 021/192] Use togglefullscreen role --- default_app/main.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/default_app/main.js b/default_app/main.js index 4c3c65842094..9df5f394aec1 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -107,12 +107,8 @@ app.once('ready', () => { }, { label: 'Toggle Full Screen', - accelerator: (() => { - return (process.platform === 'darwin') ? 'Ctrl+Command+F' : 'F11' - })(), - click (item, focusedWindow) { - if (focusedWindow) focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) - } + role: 'togglefullscreen', + accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11' }, { label: 'Toggle Developer Tools', From dcd832e88cb8c032c91ea181bd2024217d4f9316 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 09:38:35 -0700 Subject: [PATCH 022/192] :art: --- default_app/main.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/default_app/main.js b/default_app/main.js index 9df5f394aec1..91fbbdfbef11 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -112,9 +112,7 @@ app.once('ready', () => { }, { label: 'Toggle Developer Tools', - accelerator: (() => { - return (process.platform === 'darwin') ? 'Alt+Command+I' : 'Ctrl+Shift+I' - })(), + accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', click (item, focusedWindow) { if (focusedWindow) focusedWindow.toggleDevTools() } From 35097b244e372a87ba36c3256cd1e48937b93a68 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 09:43:29 -0700 Subject: [PATCH 023/192] Doc togglefullscreen menu item role --- docs/api/menu-item.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 391b639e119e..13e8317b972d 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -52,6 +52,7 @@ The `role` property can have following values: * `minimize` - Minimize current window * `close` - Close current window * `quit`- Quit the application +* `togglefullscreen`- Toggle full screen mode on the current window On macOS `role` can also have following additional values: From 3505e98a46c9edf06c7b110b743a6dbe2da0a054 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 10:06:56 -0700 Subject: [PATCH 024/192] Access function in methodInBrowserWindow --- lib/browser/api/menu-item.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index de5aab28ef10..651de147af1b 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -82,12 +82,10 @@ const MenuItem = (function () { const methodName = rolesMap[this.role] if (methodInApp[methodName]) { return app[methodName]() + } else if (typeof methodInBrowserWindow[methodName] === 'function') { + return methodInBrowserWindow[methodName](focusedWindow) } else if (methodInBrowserWindow[methodName]) { - if (typeof methodName === 'function') { - return methodName(focusedWindow) - } else { - return focusedWindow[methodName]() - } + return focusedWindow[methodName]() } else { const {webContents} = focusedWindow return webContents != null ? webContents[methodName]() : void 0 From 738edb650d8a6b2023284b05e8731f5cbc81039a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 12:33:02 -0700 Subject: [PATCH 025/192] Make NSDeleteFunctionKey to NSDeleteCharacter --- atom/browser/ui/accelerator_util_mac.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/atom/browser/ui/accelerator_util_mac.mm b/atom/browser/ui/accelerator_util_mac.mm index e2448833ab0d..cf1d3de4d212 100644 --- a/atom/browser/ui/accelerator_util_mac.mm +++ b/atom/browser/ui/accelerator_util_mac.mm @@ -29,6 +29,10 @@ void SetPlatformAccelerator(ui::Accelerator* accelerator) { modifiers ^= NSShiftKeyMask; } + if (character == NSDeleteFunctionKey) { + character = NSDeleteCharacter; + } + NSString* characters = [[[NSString alloc] initWithCharacters:&character length:1] autorelease]; From 9486df1303e889a3ac2f8e3284cf271624383cc3 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 12:38:44 -0700 Subject: [PATCH 026/192] Add delete accelerator to delete menu --- default_app/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/default_app/main.js b/default_app/main.js index 4c3c65842094..7b9ba67d5998 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -86,6 +86,7 @@ app.once('ready', () => { }, { label: 'Delete', + accelerator: 'Delete', role: 'delete' }, { From 583cc925ac93ed3bcc8a958bffec281f80f8c361 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 15:25:14 -0700 Subject: [PATCH 027/192] Map roles by command id --- atom/browser/ui/atom_menu_model.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/atom/browser/ui/atom_menu_model.cc b/atom/browser/ui/atom_menu_model.cc index 9add7a22715e..369684dd3f21 100644 --- a/atom/browser/ui/atom_menu_model.cc +++ b/atom/browser/ui/atom_menu_model.cc @@ -17,12 +17,14 @@ AtomMenuModel::~AtomMenuModel() { } void AtomMenuModel::SetRole(int index, const base::string16& role) { - roles_[index] = role; + int command_id = GetCommandIdAt(index); + roles_[command_id] = role; } base::string16 AtomMenuModel::GetRoleAt(int index) { - if (ContainsKey(roles_, index)) - return roles_[index]; + int command_id = GetCommandIdAt(index); + if (ContainsKey(roles_, command_id)) + return roles_[command_id]; else return base::string16(); } From 812dea9a8b80a05d8fda6e176dd2314f4e6aad9b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 15:28:02 -0700 Subject: [PATCH 028/192] Add comment about map key/values --- atom/browser/ui/atom_menu_model.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atom/browser/ui/atom_menu_model.h b/atom/browser/ui/atom_menu_model.h index d091df9fb570..1112949e7efa 100644 --- a/atom/browser/ui/atom_menu_model.h +++ b/atom/browser/ui/atom_menu_model.h @@ -42,7 +42,7 @@ class AtomMenuModel : public ui::SimpleMenuModel { private: Delegate* delegate_; // weak ref. - std::map roles_; + std::map roles_; // command id -> role base::ObserverList observers_; DISALLOW_COPY_AND_ASSIGN(AtomMenuModel); From 08a1e926500a8eb16dd600ae60277fa042821748 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 10:26:54 +0900 Subject: [PATCH 029/192] Bump v1.2.4 --- atom/browser/resources/mac/Info.plist | 4 ++-- atom/browser/resources/win/atom.rc | 8 ++++---- atom/common/atom_version.h | 2 +- electron.gyp | 2 +- package.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 46898637c510..41d8a27ea8d7 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile electron.icns CFBundleVersion - 1.2.3 + 1.2.4 CFBundleShortVersionString - 1.2.3 + 1.2.4 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index b6df17ca1d41..370a1e8f3d46 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,3,0 - PRODUCTVERSION 1,2,3,0 + FILEVERSION 1,2,4,0 + PRODUCTVERSION 1,2,4,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "1.2.3" + VALUE "FileVersion", "1.2.4" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "1.2.3" + VALUE "ProductVersion", "1.2.4" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index d649af2a6edc..7bf5a0970244 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 1 #define ATOM_MINOR_VERSION 2 -#define ATOM_PATCH_VERSION 3 +#define ATOM_PATCH_VERSION 4 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/electron.gyp b/electron.gyp index 09c1e279793c..d0657df31d70 100644 --- a/electron.gyp +++ b/electron.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '1.2.3', + 'version%': '1.2.4', }, 'includes': [ 'filenames.gypi', diff --git a/package.json b/package.json index f4eb3eb287aa..bce8c02dfc2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "1.2.3", + "version": "1.2.4", "devDependencies": { "asar": "^0.11.0", "request": "*", From e6327fb01570698d5578ca505fa6b696c50fe8f5 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 11:00:45 +0900 Subject: [PATCH 030/192] Add EventEmitter::CreateEventFromFlags --- atom/browser/api/atom_api_tray.cc | 26 +++----------------------- atom/browser/api/atom_api_tray.h | 2 -- atom/browser/api/event_emitter.cc | 10 ++++++++++ atom/browser/api/event_emitter.h | 11 +++++++++++ 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/atom/browser/api/atom_api_tray.cc b/atom/browser/api/atom_api_tray.cc index 85c0578a8e89..6bbad738b32d 100644 --- a/atom/browser/api/atom_api_tray.cc +++ b/atom/browser/api/atom_api_tray.cc @@ -16,7 +16,6 @@ #include "atom/common/node_includes.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" -#include "ui/events/event_constants.h" #include "ui/gfx/image/image.h" namespace atom { @@ -44,24 +43,15 @@ mate::WrappableBase* Tray::New(v8::Isolate* isolate, } void Tray::OnClicked(const gfx::Rect& bounds, int modifiers) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - EmitCustomEvent("click", - ModifiersToObject(isolate(), modifiers), bounds); + EmitWithFlags("click", modifiers, bounds); } void Tray::OnDoubleClicked(const gfx::Rect& bounds, int modifiers) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - EmitCustomEvent("double-click", - ModifiersToObject(isolate(), modifiers), bounds); + EmitWithFlags("double-click", modifiers, bounds); } void Tray::OnRightClicked(const gfx::Rect& bounds, int modifiers) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - EmitCustomEvent("right-click", - ModifiersToObject(isolate(), modifiers), bounds); + EmitWithFlags("right-click", modifiers, bounds); } void Tray::OnBalloonShow() { @@ -163,16 +153,6 @@ gfx::Rect Tray::GetBounds() { return tray_icon_->GetBounds(); } -v8::Local Tray::ModifiersToObject(v8::Isolate* isolate, - int modifiers) { - mate::Dictionary obj(isolate, v8::Object::New(isolate)); - obj.Set("shiftKey", static_cast(modifiers & ui::EF_SHIFT_DOWN)); - obj.Set("ctrlKey", static_cast(modifiers & ui::EF_CONTROL_DOWN)); - obj.Set("altKey", static_cast(modifiers & ui::EF_ALT_DOWN)); - obj.Set("metaKey", static_cast(modifiers & ui::EF_COMMAND_DOWN)); - return obj.GetHandle(); -} - // static void Tray::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { diff --git a/atom/browser/api/atom_api_tray.h b/atom/browser/api/atom_api_tray.h index 0bc28af818cf..4dd79c467f09 100644 --- a/atom/browser/api/atom_api_tray.h +++ b/atom/browser/api/atom_api_tray.h @@ -68,8 +68,6 @@ class Tray : public mate::TrackableObject, gfx::Rect GetBounds(); private: - v8::Local ModifiersToObject(v8::Isolate* isolate, int modifiers); - v8::Global menu_; std::unique_ptr tray_icon_; diff --git a/atom/browser/api/event_emitter.cc b/atom/browser/api/event_emitter.cc index 7e392fddee34..e98008b85d49 100644 --- a/atom/browser/api/event_emitter.cc +++ b/atom/browser/api/event_emitter.cc @@ -8,6 +8,7 @@ #include "native_mate/arguments.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" +#include "ui/events/event_constants.h" namespace mate { @@ -65,6 +66,15 @@ v8::Local CreateCustomEvent( return event; } +v8::Local CreateEventFromFlags(v8::Isolate* isolate, int flags) { + mate::Dictionary obj = mate::Dictionary::CreateEmpty(isolate); + obj.Set("shiftKey", static_cast(flags & ui::EF_SHIFT_DOWN)); + obj.Set("ctrlKey", static_cast(flags & ui::EF_CONTROL_DOWN)); + obj.Set("altKey", static_cast(flags & ui::EF_ALT_DOWN)); + obj.Set("metaKey", static_cast(flags & ui::EF_COMMAND_DOWN)); + return obj.GetHandle(); +} + } // namespace internal } // namespace mate diff --git a/atom/browser/api/event_emitter.h b/atom/browser/api/event_emitter.h index 99f6ed46e48f..ead3beddaac5 100644 --- a/atom/browser/api/event_emitter.h +++ b/atom/browser/api/event_emitter.h @@ -30,6 +30,7 @@ v8::Local CreateCustomEvent( v8::Isolate* isolate, v8::Local object, v8::Local event); +v8::Local CreateEventFromFlags(v8::Isolate* isolate, int flags); } // namespace internal @@ -54,6 +55,16 @@ class EventEmitter : public Wrappable { internal::CreateCustomEvent(isolate(), GetWrapper(), event), args...); } + // this.emit(name, new Event(flags), args...); + template + bool EmitWithFlags(const base::StringPiece& name, + int flags, + const Args&... args) { + return EmitCustomEvent( + name, + internal::CreateEventFromFlags(isolate(), flags), args...); + } + // this.emit(name, new Event(), args...); template bool Emit(const base::StringPiece& name, const Args&... args) { From 8d08e215b2284a66267b2209954b3f37157dac34 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 11:22:14 +0900 Subject: [PATCH 031/192] Add "event" parameter for "click" handler of MenuItem --- atom/browser/api/atom_api_menu.cc | 6 ++++-- atom/browser/api/atom_api_menu.h | 2 +- docs/api/menu-item.md | 4 ++-- lib/browser/api/menu-item.js | 4 ++-- lib/browser/api/menu.js | 4 ++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index 996c71739bc4..c9cd37522b82 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -61,8 +61,10 @@ bool Menu::GetAcceleratorForCommandId(int command_id, return mate::ConvertFromV8(isolate(), val, accelerator); } -void Menu::ExecuteCommand(int command_id, int event_flags) { - execute_command_.Run(command_id); +void Menu::ExecuteCommand(int command_id, int flags) { + execute_command_.Run( + mate::internal::CreateEventFromFlags(isolate(), flags), + command_id); } void Menu::MenuWillShow(ui::SimpleMenuModel* source) { diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 9ba4d7a754c4..53c6bdaf4ec9 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -90,7 +90,7 @@ class Menu : public mate::TrackableObject, base::Callback is_enabled_; base::Callback is_visible_; base::Callback(int)> get_accelerator_; - base::Callback execute_command_; + base::Callback, int)> execute_command_; base::Callback menu_will_show_; DISALLOW_COPY_AND_ASSIGN(Menu); diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 13e8317b972d..7cb434288b0a 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -11,8 +11,8 @@ Create a new `MenuItem` with the following method: ### new MenuItem(options) * `options` Object - * `click` Function - Will be called with `click(menuItem, browserWindow)` when - the menu item is clicked + * `click` Function - Will be called with + `click(menuItem, browserWindow, event)` when the menu item is clicked * `role` String - Define the action of the menu item; when specified the `click` property will be ignored * `type` String - Can be `normal`, `separator`, `submenu`, `checkbox` or diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 651de147af1b..b0100b7f2ff7 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -72,7 +72,7 @@ const MenuItem = (function () { throw new Error('Unknown menu type ' + this.type) } this.commandId = ++nextCommandId - this.click = (focusedWindow) => { + this.click = (event, focusedWindow) => { // Manually flip the checked flags when clicked. if (this.type === 'checkbox' || this.type === 'radio') { this.checked = !this.checked @@ -91,7 +91,7 @@ const MenuItem = (function () { return webContents != null ? webContents[methodName]() : void 0 } } else if (typeof click === 'function') { - return click(this, focusedWindow) + return click(this, focusedWindow, event) } else if (typeof this.selector === 'string' && process.platform === 'darwin') { return Menu.sendActionToFirstResponder(this.selector) } diff --git a/lib/browser/api/menu.js b/lib/browser/api/menu.js index b122fc36cf37..3b1082193639 100644 --- a/lib/browser/api/menu.js +++ b/lib/browser/api/menu.js @@ -114,9 +114,9 @@ Menu.prototype._init = function () { var command = this.commandsMap[commandId] return command != null ? command.icon : undefined }, - executeCommand: (commandId) => { + executeCommand: (event, commandId) => { var command = this.commandsMap[commandId] - return command != null ? command.click(BrowserWindow.getFocusedWindow()) : undefined + return command != null ? command.click(event, BrowserWindow.getFocusedWindow()) : undefined }, menuWillShow: () => { // Make sure radio groups have at least one menu item seleted. From 62d0dbea5a2382dacc2231a6150d0a4699498e4b Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 13:23:07 +0900 Subject: [PATCH 032/192] docs: Reformat the menu-item.md --- docs/api/menu-item.md | 52 ++++++++++++++++++++++++++----------------- docs/api/session.md | 1 + 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 7cb434288b0a..0d05d81bcd00 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -2,7 +2,7 @@ > Add items to native application menus and context menus. -See [`menu`](menu.md) for examples. +See [`Menu`](menu.md) for examples. ## Class: MenuItem @@ -12,11 +12,11 @@ Create a new `MenuItem` with the following method: * `options` Object * `click` Function - Will be called with - `click(menuItem, browserWindow, event)` when the menu item is clicked - * `role` String - Define the action of the menu item; when specified the - `click` property will be ignored + `click(menuItem, browserWindow, event)` when the menu item is clicked. + * `role` String - Define the action of the menu item, when specified the + `click` property will be ignored. * `type` String - Can be `normal`, `separator`, `submenu`, `checkbox` or - `radio` + `radio`. * `label` String * `sublabel` String * `accelerator` [Accelerator](accelerator.md) @@ -25,15 +25,15 @@ Create a new `MenuItem` with the following method: unclickable. * `visible` Boolean - If false, the menu item will be entirely hidden. * `checked` Boolean - Should only be specified for `checkbox` or `radio` type - menu items. + menu items. * `submenu` Menu - Should be specified for `submenu` type menu items. If - `submenu` is specified, the `type: 'submenu'` can be omitted. If the value - is not a `Menu` then it will be automatically converted to one using - `Menu.buildFromTemplate`. + `submenu` is specified, the `type: 'submenu'` can be omitted. If the value + is not a `Menu` then it will be automatically converted to one using + `Menu.buildFromTemplate`. * `id` String - Unique within a single menu. If defined then it can be used - as a reference to this item by the position attribute. + as a reference to this item by the position attribute. * `position` String - This field allows fine-grained definition of the - specific location within a given menu. + specific location within a given menu. It is best to specify `role` for any menu item that matches a standard role, rather than trying to manually implement the behavior in a `click` function. @@ -69,19 +69,29 @@ On macOS `role` can also have following additional values: When specifying `role` on macOS, `label` and `accelerator` are the only options that will affect the MenuItem. All other options will be ignored. -## Instance Properties +### Instance Properties -The following properties (and no others) can be updated on an existing `MenuItem`: +The following properties are available on instances of `MenuItem`: - * `enabled` Boolean - * `visible` Boolean - * `checked` Boolean +#### `menuItem.enabled` -Their meanings are as described above. +A Boolean indicating whether the item is enabled, this property can be +dynamically changed. -A `checkbox` menu item will toggle its `checked` property on and off when -selected. You can add a `click` function to do additional work. +#### `menuItem.visible` + +A Boolean indicating whether the item is visible, this property can be +dynamically changed. + +#### `menuItem.checked` + +A Boolean indicating whether the item is checked, this property can be +dynamically changed. + +A `checkbox` menu item will toggle the `checked` property on and off when +selected. A `radio` menu item will turn on its `checked` property when clicked, and -will turn off that property for all adjacent items in the same menu. Again, -you can add a `click` function for additional behavior. +will turn off that property for all adjacent items in the same menu. + +You can add a `click` function for additional behavior. diff --git a/docs/api/session.md b/docs/api/session.md index 030b2c7542ea..0486b1ddbdd6 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -566,3 +566,4 @@ app.on('ready', function () { console.error('Failed to register protocol') }) }) +``` From 90b64504fcc4d72272ee12a89133a45904739999 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 13:36:10 +0900 Subject: [PATCH 033/192] spec: Fix failing tests of Menu --- spec/api-menu-spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index 6866448e0fd1..a8cc5b518cba 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -231,7 +231,7 @@ describe('menu module', function () { } } ]) - menu.delegate.executeCommand(menu.items[0].commandId) + menu.delegate.executeCommand({}, menu.items[0].commandId) }) }) @@ -244,7 +244,7 @@ describe('menu module', function () { } ]) assert.equal(menu.items[0].checked, false) - menu.delegate.executeCommand(menu.items[0].commandId) + menu.delegate.executeCommand({}, menu.items[0].commandId) assert.equal(menu.items[0].checked, true) }) @@ -255,9 +255,9 @@ describe('menu module', function () { type: 'radio' } ]) - menu.delegate.executeCommand(menu.items[0].commandId) + menu.delegate.executeCommand({}, menu.items[0].commandId) assert.equal(menu.items[0].checked, true) - menu.delegate.executeCommand(menu.items[0].commandId) + menu.delegate.executeCommand({}, menu.items[0].commandId) assert.equal(menu.items[0].checked, true) }) From 8ffd069689d9ad81a8492b5e01ecb85bada604d4 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 14:06:54 +0900 Subject: [PATCH 034/192] mac: Fix maximize/unmaximize event emitted before window is maximized --- atom/browser/native_window_mac.mm | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 5f997ee9b6b9..3330eb8e9f3e 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -70,6 +70,7 @@ bool ScopedDisableResize::disable_resize_ = false; @interface AtomNSWindowDelegate : NSObject { @private atom::NativeWindowMac* shell_; + bool is_zooming_; } - (id)initWithShell:(atom::NativeWindowMac*)shell; @end @@ -79,6 +80,7 @@ bool ScopedDisableResize::disable_resize_ = false; - (id)initWithShell:(atom::NativeWindowMac*)shell { if ((self = [super init])) { shell_ = shell; + is_zooming_ = false; } return self; } @@ -172,16 +174,20 @@ bool ScopedDisableResize::disable_resize_ = false; } - (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame { - // Cocoa doen't have concept of maximize/unmaximize, so wee need to emulate - // them by calculating size change when zooming. - if (newFrame.size.width < [window frame].size.width || - newFrame.size.height < [window frame].size.height) - shell_->NotifyWindowUnmaximize(); - else - shell_->NotifyWindowMaximize(); + is_zooming_ = true; return YES; } +- (void)windowDidEndLiveResize:(NSNotification*)notification { + if (is_zooming_) { + if (shell_->IsMaximized()) + shell_->NotifyWindowMaximize(); + else + shell_->NotifyWindowUnmaximize(); + is_zooming_ = false; + } +} + - (void)windowWillEnterFullScreen:(NSNotification*)notification { // Hide the native toolbar before entering fullscreen, so there is no visual // artifacts. From 0d481aaeb8a4f77b1aebf205657132d79d64aa15 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Fri, 17 Jun 2016 12:16:15 -0700 Subject: [PATCH 035/192] :memo: use h3 headings for events and methods, per the styleguide --- docs-translations/jp/api/session.md | 44 ++++++++-------- docs-translations/ko-KR/api/menu.md | 2 +- docs-translations/ko-KR/api/session.md | 54 +++++++++---------- docs-translations/ko-KR/api/web-contents.md | 4 +- docs-translations/zh-CN/api/session.md | 50 +++++++++--------- docs-translations/zh-CN/api/web-contents.md | 4 +- docs/api/session.md | 58 ++++++++++----------- docs/api/web-contents.md | 4 +- 8 files changed, 110 insertions(+), 110 deletions(-) diff --git a/docs-translations/jp/api/session.md b/docs-translations/jp/api/session.md index a5d47d404a6d..84e0d08deb51 100644 --- a/docs-translations/jp/api/session.md +++ b/docs-translations/jp/api/session.md @@ -70,7 +70,7 @@ session.defaultSession.on('will-download', function(event, item, webContents) { `Session`のインスタンス上で次のメソッドが提供されています: -#### `ses.cookies` +### `ses.cookies` `cookies`は、cookiesに問い合わせしたり、修正をできるようにします。例: @@ -94,7 +94,7 @@ session.defaultSession.cookies.set(cookie, function(error) { }); ``` -#### `ses.cookies.get(filter, callback)` +### `ses.cookies.get(filter, callback)` * `filter` Object * `url` String (オプション) - `url`に関連付けられているcookiesを取得します。空の場合すべてのurlのcookiesを取得します @@ -122,7 +122,7 @@ session.defaultSession.cookies.set(cookie, function(error) { cookieの有効期限をUNIX時間で何秒かを示します。セッションcookiesは提供されません。 -#### `ses.cookies.set(details, callback)` +### `ses.cookies.set(details, callback)` * `details` Object * `url` String - `url`に関連付けられているcookiesを取得します。 @@ -137,7 +137,7 @@ cookieの有効期限をUNIX時間で何秒かを示します。セッションc `details`でcookieを設定し、完了すると`callback(error)`で`callback`がコールされます。 -#### `ses.cookies.remove(url, name, callback)` +### `ses.cookies.remove(url, name, callback)` * `url` String - cookieに関連付けられているURL * `name` String - 削除するcookieの名前 @@ -145,20 +145,20 @@ cookieの有効期限をUNIX時間で何秒かを示します。セッションc `url` と `name`と一致するcookiesを削除し、完了すると`callback`が、`callback()`でコールされます。 -#### `ses.getCacheSize(callback)` +### `ses.getCacheSize(callback)` * `callback` Function * `size` Integer - 使用しているキャッシュサイズバイト数 現在のセッションのキャッシュサイズを返します。 -#### `ses.clearCache(callback)` +### `ses.clearCache(callback)` * `callback` Function - 操作が完了したら、コールされます。 セッションのHTTPキャッシュをクリアします。 -#### `ses.clearStorageData([options, ]callback)` +### `ses.clearStorageData([options, ]callback)` * `options` Object (オプション) * `origin` String - `window.location.origin`の説明で、`scheme://host:port`に従う @@ -171,11 +171,11 @@ cookieの有効期限をUNIX時間で何秒かを示します。セッションc ウェブストレージのデータをクリアします。 -#### `ses.flushStorageData()` +### `ses.flushStorageData()` 書き込まれていないDOMStorageデータをディスクに書き込みます。 -#### `ses.setProxy(config, callback)` +### `ses.setProxy(config, callback)` * `config` Object * `pacScript` String - PACファイルに関連付けらえたURL @@ -213,13 +213,13 @@ proxyURL = ["://"][":"] `url`をプロキシ情報で解決します。リクエストが実行された時、`callback(proxy)`で `callback`がコールされます。 -#### `ses.setDownloadPath(path)` +### `ses.setDownloadPath(path)` * `path` String - ダウンロード場所 ダウンロードの保存ディレクトリを設定します。既定では、ダウンロードディレクトリは、個別のアプリフォルダー下の`Downloads`です。 -#### `ses.enableNetworkEmulation(options)` +### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - ネットワーク停止を再現するかどうか @@ -241,11 +241,11 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -#### `ses.disableNetworkEmulation()` +### `ses.disableNetworkEmulation()` `session`ですでに有効になっているネットワークエミュレーションを無効化します。オリジナルのネットワーク設定にリセットします。 -#### `ses.setCertificateVerifyProc(proc)` +### `ses.setCertificateVerifyProc(proc)` * `proc` Function @@ -262,7 +262,7 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c }); ``` -#### `ses.webRequest` +### `ses.webRequest` `webRequest`APIセットをインターセプトし、そのライフタイムの様々な段階でリクエストの内容を変更できます。 @@ -284,7 +284,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, }); ``` -#### `ses.webRequest.onBeforeRequest([filter, ]listener)` +### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -312,7 +312,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `cancel` Boolean (オプション) * `redirectURL` String (オプション) - オリジナルリクエストが送信もしくは完了するのを中断し、代わりに付与したURLにリダイレクトします。 -#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -334,7 +334,7 @@ The `callback` has to be called with an `response` object: * `cancel` Boolean (オプション) * `requestHeaders` Object (オプション) - 付与されると、リクエストはそれらのヘッダーで作成されます。 -#### `ses.webRequest.onSendHeaders([filter, ]listener)` +### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -349,7 +349,7 @@ The `callback` has to be called with an `response` object: * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter,] listener)` +### `ses.webRequest.onHeadersReceived([filter,] listener)` * `filter` Object * `listener` Function @@ -373,7 +373,7 @@ The `callback` has to be called with an `response` object: * `cancel` Boolean * `responseHeaders` Object (オプション) - 付与されていると、これらのヘッダーでサーバーはレスポンスしたと仮定します。 -#### `ses.webRequest.onResponseStarted([filter, ]listener)` +### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -391,7 +391,7 @@ The `callback` has to be called with an `response` object: * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -410,7 +410,7 @@ The `callback` has to be called with an `response` object: * `fromCache` Boolean * `responseHeaders` Object -#### `ses.webRequest.onCompleted([filter, ]listener)` +### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -428,7 +428,7 @@ The `callback` has to be called with an `response` object: * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onErrorOccurred([filter, ]listener)` +### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function diff --git a/docs-translations/ko-KR/api/menu.md b/docs-translations/ko-KR/api/menu.md index eaee7e8d4f9d..36e6c091c170 100644 --- a/docs-translations/ko-KR/api/menu.md +++ b/docs-translations/ko-KR/api/menu.md @@ -217,7 +217,7 @@ Menu.setApplicationMenu(menu); 새로운 메뉴를 생성합니다. -### Methods +## Methods `menu` 클래스는 다음과 같은 메서드를 가지고 있습니다: diff --git a/docs-translations/ko-KR/api/session.md b/docs-translations/ko-KR/api/session.md index a2dc9b682a6b..5ee37a270535 100644 --- a/docs-translations/ko-KR/api/session.md +++ b/docs-translations/ko-KR/api/session.md @@ -53,7 +53,7 @@ const ses = session.fromPartition('persist:name'); `Session` 객체는 다음과 같은 이벤트를 가지고 있습니다: -#### Event: 'will-download' +### Event: 'will-download' * `event` Event * `item` [DownloadItem](download-item.md) @@ -77,7 +77,7 @@ session.defaultSession.on('will-download', (event, item, webContents) => { `Session` 객체는 다음과 같은 메서드와 속성을 가지고 있습니다: -#### `ses.cookies` +### `ses.cookies` `cookies` 속성은 쿠키를 조작하는 방법을 제공합니다. 예를 들어 다음과 같이 할 수 있습니다: @@ -102,7 +102,7 @@ session.defaultSession.cookies.set(cookie, (error) => { }); ``` -#### `ses.cookies.get(filter, callback)` +### `ses.cookies.get(filter, callback)` * `filter` Object * `url` String (optional) - `url`에 해당하는 쿠키를 취득합니다. 이 속성을 @@ -133,7 +133,7 @@ session.defaultSession.cookies.set(cookie, (error) => { * `expirationDate` Double - (Option) UNIX 시간으로 표시되는 쿠키의 만료일에 대한 초 단위 시간. 세션 쿠키는 지원되지 않음. -#### `ses.cookies.set(details, callback)` +### `ses.cookies.set(details, callback)` * `details` Object * `url` String - 쿠키에 대한 `url` 링크. @@ -153,7 +153,7 @@ session.defaultSession.cookies.set(cookie, (error) => { `details` 객체에 따라 쿠키를 설정합니다. 작업이 완료되면 `callback`이 `callback(error)` 형태로 호출됩니다. -#### `ses.cookies.remove(url, name, callback)` +### `ses.cookies.remove(url, name, callback)` * `url` String - 쿠키와 관련된 URL입니다. * `name` String - 지울 쿠키의 이름입니다. @@ -162,20 +162,20 @@ session.defaultSession.cookies.set(cookie, (error) => { `url`과 `name`에 일치하는 쿠키를 삭제합니다. 작업이 완료되면 `callback`이 `callback()` 형식으로 호출됩니다. -#### `ses.getCacheSize(callback)` +### `ses.getCacheSize(callback)` * `callback` Function * `size` Integer - 바이트로 표현된 캐시 크기 세션의 현재 캐시 크기를 반환합니다. -#### `ses.clearCache(callback)` +### `ses.clearCache(callback)` * `callback` Function - 작업이 완료되면 호출됩니다. 세션의 HTTP 캐시를 비웁니다. -#### `ses.clearStorageData([options, ]callback)` +### `ses.clearStorageData([options, ]callback)` * `options` Object (optional), proprties: * `origin` String - `scheme://host:port`와 같은 `window.location.origin` 규칙을 @@ -189,11 +189,11 @@ session.defaultSession.cookies.set(cookie, (error) => { 웹 스토리지의 데이터를 비웁니다. -#### `ses.flushStorageData()` +### `ses.flushStorageData()` 디스크에 사용되지 않은 DOMStorage 데이터를 모두 덮어씌웁니다. -#### `ses.setProxy(config, callback)` +### `ses.setProxy(config, callback)` * `config` Object * `pacScript` String - PAC 파일과 관련된 URL입니다. @@ -240,14 +240,14 @@ proxyURL = ["://"][":"] `url`의 프록시 정보를 해석합니다. `callback`은 요청이 수행되었을 때 `callback(proxy)` 형태로 호출됩니다. -#### `ses.setDownloadPath(path)` +### `ses.setDownloadPath(path)` * `path` String - 다운로드 위치 다운로드 저장 위치를 지정합니다. 기본 다운로드 위치는 각 애플리케이션 데이터 디렉터리의 `Downloads` 폴더입니다. -#### `ses.enableNetworkEmulation(options)` +### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - 네트워크의 오프라인 상태 여부 @@ -269,11 +269,11 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -#### `ses.disableNetworkEmulation()` +### `ses.disableNetworkEmulation()` 활성화된 `session`의 에뮬레이션을 비활성화합니다. 기본 네트워크 설정으로 돌아갑니다. -#### `ses.setCertificateVerifyProc(proc)` +### `ses.setCertificateVerifyProc(proc)` * `proc` Function @@ -292,7 +292,7 @@ myWindow.webContents.session.setCertificateVerifyProc((hostname, cert, callback) callback(false); }); ``` -#### `ses.setPermissionRequestHandler(handler)` +### `ses.setPermissionRequestHandler(handler)` * `handler` Function * `webContents` Object - [WebContents](web-contents.md) 권한을 요청. @@ -317,13 +317,13 @@ session.fromPartition(partition).setPermissionRequestHandler((webContents, permi }); ``` -#### `ses.clearHostResolverCache([callback])` +### `ses.clearHostResolverCache([callback])` * `callback` Function (optional) - 작업이 완료되면 호출됩니다. 호스트 리소버(resolver) 캐시를 지웁니다. -#### `ses.allowNTLMCredentialsForDomains(domains)` +### `ses.allowNTLMCredentialsForDomains(domains)` * `domains` String - 통합 인증을 사용하도록 설정할 쉼표로 구분된 서버의 리스트. @@ -339,7 +339,7 @@ session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com session.defaultSession.allowNTLMCredentialsForDomains('*') ``` -#### `ses.webRequest` +### `ses.webRequest` `webRequest` API는 생명주기의 다양한 단계에 맞춰 요청 콘텐츠를 가로채거나 변경할 수 있도록 합니다. @@ -367,7 +367,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback }); ``` -#### `ses.webRequest.onBeforeRequest([filter, ]listener)` +### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -396,7 +396,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback * `redirectURL` String (optional) - 원래 요청은 전송과 완료가 방지되지만 이 속성을 지정하면 해당 URL로 리다이렉트됩니다. -#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -421,7 +421,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `requestHeaders` Object (optional) - 이 속성이 제공되면, 요청은 이 헤더로 만들어 집니다. -#### `ses.webRequest.onSendHeaders([filter, ]listener)` +### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -438,7 +438,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter, ]listener)` +### `ses.webRequest.onHeadersReceived([filter, ]listener)` * `filter` Object * `listener` Function @@ -467,7 +467,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener 변경하기 위해 반드시 지정되어야 합니다. 그렇지 않은 경우, 기존의 응답 헤더의 상태가 사용됩니다. -#### `ses.webRequest.onResponseStarted([filter, ]listener)` +### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -486,7 +486,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -506,7 +506,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `fromCache` Boolean * `responseHeaders` Object -#### `ses.webRequest.onCompleted([filter, ]listener)` +### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -524,7 +524,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onErrorOccurred([filter, ]listener)` +### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function @@ -540,7 +540,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `fromCache` Boolean * `error` String - 에러 설명. -#### `ses.protocol` +### `ses.protocol` 현재 세션의 [protocol](protocol.md) 모듈 인스턴스를 반환합니다. diff --git a/docs-translations/ko-KR/api/web-contents.md b/docs-translations/ko-KR/api/web-contents.md index 437b5dabbe7b..e0e5fd777123 100644 --- a/docs-translations/ko-KR/api/web-contents.md +++ b/docs-translations/ko-KR/api/web-contents.md @@ -1022,7 +1022,7 @@ win.webContents.debugger.sendCommand('Network.enable'); 지정한 커맨드를 디버깅 대상에게 전송합니다. -#### Event: 'detach' +### Event: 'detach' * `event` Event * `reason` String - 디버거 분리 사유. @@ -1030,7 +1030,7 @@ win.webContents.debugger.sendCommand('Network.enable'); 디버깅 세션이 종료될 때 발생하는 이벤트입니다. `webContents`가 닫히거나 개발자 도구가 부착된 `webContents`에 대해 호출될 때 발생합니다. -#### Event: 'message' +### Event: 'message' * `event` Event * `method` String - 메서드 이름. diff --git a/docs-translations/zh-CN/api/session.md b/docs-translations/zh-CN/api/session.md index b9664c08e652..b96290dbe330 100644 --- a/docs-translations/zh-CN/api/session.md +++ b/docs-translations/zh-CN/api/session.md @@ -47,7 +47,7 @@ var ses = session.fromPartition('persist:name'); 实例 `Session` 有以下事件: -#### Event: 'will-download' +### Event: 'will-download' * `event` Event * `item` [DownloadItem](download-item.md) @@ -70,7 +70,7 @@ session.defaultSession.on('will-download', function(event, item, webContents) { 实例 `Session` 有以下方法: -#### `ses.cookies` +### `ses.cookies` `cookies` 赋予你全力来查询和修改 cookies. 例如: @@ -94,7 +94,7 @@ session.defaultSession.cookies.set(cookie, function(error) { }); ``` -#### `ses.cookies.get(filter, callback)` +### `ses.cookies.get(filter, callback)` * `filter` Object * `url` String (可选) - 与获取 cookies 相关的 @@ -123,7 +123,7 @@ session.defaultSession.cookies.set(cookie, function(error) { cookie . * `expirationDate` Double (可选) - cookie的截至日期,数值为UNIX纪元以来的秒数. 对session cookies 不提供. -#### `ses.cookies.set(details, callback)` +### `ses.cookies.set(details, callback)` * `details` Object * `url` String - 与获取 cookies 相关的 @@ -140,7 +140,7 @@ session.defaultSession.cookies.set(cookie, function(error) { 使用 `details` 设置 cookie, 完成时使用 `callback(error)` 掉哟个 `callback` . -#### `ses.cookies.remove(url, name, callback)` +### `ses.cookies.remove(url, name, callback)` * `url` String - 与 cookies 相关的 `url`. @@ -149,20 +149,20 @@ session.defaultSession.cookies.set(cookie, function(error) { 删除匹配 `url` 和 `name` 的 cookie, 完成时使用 `callback()`调用`callback`. -#### `ses.getCacheSize(callback)` +### `ses.getCacheSize(callback)` * `callback` Function * `size` Integer - 单位 bytes 的缓存 size. 返回 session 的当前缓存 size . -#### `ses.clearCache(callback)` +### `ses.clearCache(callback)` * `callback` Function - 操作完成时调用 清空 session 的 HTTP 缓存. -#### `ses.clearStorageData([options, ]callback)` +### `ses.clearStorageData([options, ]callback)` * `options` Object (可选) * `origin` String - 应当遵循 `window.location.origin` 的格式 @@ -176,11 +176,11 @@ session.defaultSession.cookies.set(cookie, function(error) { 清除 web storages 的数据. -#### `ses.flushStorageData()` +### `ses.flushStorageData()` 将没有写入的 DOMStorage 写入磁盘. -#### `ses.setProxy(config, callback)` +### `ses.setProxy(config, callback)` * `config` Object * `pacScript` String - 与 PAC 文件相关的 URL. @@ -219,13 +219,13 @@ proxyURL = ["://"][":"] 解析 `url` 的代理信息.当请求完成的时候使用 `callback(proxy)` 调用 `callback`. -#### `ses.setDownloadPath(path)` +### `ses.setDownloadPath(path)` * `path` String - 下载地址 设置下载保存地址,默认保存地址为各自 app 应用的 `Downloads`目录. -#### `ses.enableNetworkEmulation(options)` +### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - 是否模拟网络故障. @@ -247,12 +247,12 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -#### `ses.disableNetworkEmulation()` +### `ses.disableNetworkEmulation()` 停止所有已经使用 `session` 的活跃模拟网络. 重置为原始网络类型. -#### `ses.setCertificateVerifyProc(proc)` +### `ses.setCertificateVerifyProc(proc)` * `proc` Function @@ -269,7 +269,7 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c }); ``` -#### `ses.setPermissionRequestHandler(handler)` +### `ses.setPermissionRequestHandler(handler)` * `handler` Function * `webContents` Object - [WebContents](web-contents.md) 请求许可. @@ -291,13 +291,13 @@ session.fromPartition(partition).setPermissionRequestHandler(function(webContent }); ``` -#### `ses.clearHostResolverCache([callback])` +### `ses.clearHostResolverCache([callback])` * `callback` Function (可选) - 操作结束调用. 清除主机解析缓存. -#### `ses.webRequest` +### `ses.webRequest` 在其生命周期的不同阶段,`webRequest` API 设置允许拦截并修改请求内容. @@ -319,7 +319,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, }); ``` -#### `ses.webRequest.onBeforeRequest([filter, ]listener)` +### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -347,7 +347,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `cancel` Boolean (可选) * `redirectURL` String (可选) - 原始请求阻止发送或完成,而不是重定向. -#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -369,7 +369,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `cancel` Boolean (可选) * `requestHeaders` Object (可选) - 如果提供了,将使用这些 headers 来创建请求. -#### `ses.webRequest.onSendHeaders([filter, ]listener)` +### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -384,7 +384,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter,] listener)` +### `ses.webRequest.onHeadersReceived([filter,] listener)` * `filter` Object * `listener` Function @@ -408,7 +408,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `cancel` Boolean * `responseHeaders` Object (可选) - 如果提供, 服务器将假定使用这些头来响应. -#### `ses.webRequest.onResponseStarted([filter, ]listener)` +### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -427,7 +427,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -446,7 +446,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `fromCache` Boolean * `responseHeaders` Object -#### `ses.webRequest.onCompleted([filter, ]listener)` +### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -464,7 +464,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onErrorOccurred([filter, ]listener)` +### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function diff --git a/docs-translations/zh-CN/api/web-contents.md b/docs-translations/zh-CN/api/web-contents.md index 54b9c56dcc6c..c05b6d2593c7 100644 --- a/docs-translations/zh-CN/api/web-contents.md +++ b/docs-translations/zh-CN/api/web-contents.md @@ -846,14 +846,14 @@ win.webContents.debugger.sendCommand("Network.enable"); 发送给定命令给调试目标. -#### Event: 'detach' +### Event: 'detach' * `event` Event * `reason` String - 拆分调试器原因. 在调试 session 结束时发出事件.这在 `webContents` 关闭时或 `webContents` 请求开发者工具栏时发生. -#### Event: 'message' +### Event: 'message' * `event` Event * `method` String - 方法名. diff --git a/docs/api/session.md b/docs/api/session.md index 0486b1ddbdd6..e4ef07d6c772 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -50,11 +50,11 @@ const session = require('electron').session; const ses = session.fromPartition('persist:name'); ``` -### Instance Events +## Events The following events are available on instances of `Session`: -#### Event: 'will-download' +### Event: 'will-download' * `event` Event * `item` [DownloadItem](download-item.md) @@ -74,11 +74,11 @@ session.defaultSession.on('will-download', (event, item, webContents) => { }); ``` -### Instance Methods +## Instance Methods The following methods are available on instances of `Session`: -#### `ses.cookies` +### `ses.cookies` The `cookies` gives you ability to query and modify cookies. For example: @@ -102,7 +102,7 @@ session.defaultSession.cookies.set(cookie, (error) => { }); ``` -#### `ses.cookies.get(filter, callback)` +### `ses.cookies.get(filter, callback)` * `filter` Object * `url` String (optional) - Retrieves cookies which are associated with @@ -134,7 +134,7 @@ with `callback(error, cookies)` on complete. the number of seconds since the UNIX epoch. Not provided for session cookies. -#### `ses.cookies.set(details, callback)` +### `ses.cookies.set(details, callback)` * `details` Object * `url` String - The url to associate the cookie with. @@ -154,7 +154,7 @@ with `callback(error, cookies)` on complete. Sets a cookie with `details`, `callback` will be called with `callback(error)` on complete. -#### `ses.cookies.remove(url, name, callback)` +### `ses.cookies.remove(url, name, callback)` * `url` String - The URL associated with the cookie. * `name` String - The name of cookie to remove. @@ -163,20 +163,20 @@ on complete. Removes the cookies matching `url` and `name`, `callback` will called with `callback()` on complete. -#### `ses.getCacheSize(callback)` +### `ses.getCacheSize(callback)` * `callback` Function * `size` Integer - Cache size used in bytes. Returns the session's current cache size. -#### `ses.clearCache(callback)` +### `ses.clearCache(callback)` * `callback` Function - Called when operation is done Clears the session’s HTTP cache. -#### `ses.clearStorageData([options, ]callback)` +### `ses.clearStorageData([options, ]callback)` * `options` Object (optional) * `origin` String - Should follow `window.location.origin`’s representation @@ -190,11 +190,11 @@ Clears the session’s HTTP cache. Clears the data of web storages. -#### `ses.flushStorageData()` +### `ses.flushStorageData()` Writes any unwritten DOMStorage data to disk. -#### `ses.setProxy(config, callback)` +### `ses.setProxy(config, callback)` * `config` Object * `pacScript` String - The URL associated with the PAC file. @@ -239,14 +239,14 @@ For example: Resolves the proxy information for `url`. The `callback` will be called with `callback(proxy)` when the request is performed. -#### `ses.setDownloadPath(path)` +### `ses.setDownloadPath(path)` * `path` String - The download location Sets download saving directory. By default, the download directory will be the `Downloads` under the respective app folder. -#### `ses.enableNetworkEmulation(options)` +### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - Whether to emulate network outage. @@ -268,12 +268,12 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -#### `ses.disableNetworkEmulation()` +### `ses.disableNetworkEmulation()` Disables any network emulation already active for the `session`. Resets to the original network configuration. -#### `ses.setCertificateVerifyProc(proc)` +### `ses.setCertificateVerifyProc(proc)` * `proc` Function @@ -294,7 +294,7 @@ myWindow.webContents.session.setCertificateVerifyProc((hostname, cert, callback) }); ``` -#### `ses.setPermissionRequestHandler(handler)` +### `ses.setPermissionRequestHandler(handler)` * `handler` Function * `webContents` Object - [WebContents](web-contents.md) requesting the permission. @@ -318,13 +318,13 @@ session.fromPartition(partition).setPermissionRequestHandler((webContents, permi }); ``` -#### `ses.clearHostResolverCache([callback])` +### `ses.clearHostResolverCache([callback])` * `callback` Function (optional) - Called when operation is done. Clears the host resolver cache. -#### `ses.allowNTLMCredentialsForDomains(domains)` +### `ses.allowNTLMCredentialsForDomains(domains)` * `domains` String - A comma-seperated list of servers for which integrated authentication is enabled. @@ -341,7 +341,7 @@ session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com session.defaultSession.allowNTLMCredentialsForDomains('*') ``` -#### `ses.webRequest` +### `ses.webRequest` The `webRequest` API set allows to intercept and modify contents of a request at various stages of its lifetime. @@ -370,7 +370,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback }); ``` -#### `ses.webRequest.onBeforeRequest([filter, ]listener)` +### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -400,7 +400,7 @@ The `callback` has to be called with an `response` object: * `redirectURL` String (optional) - The original request is prevented from being sent or completed, and is instead redirected to the given URL. -#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -425,7 +425,7 @@ The `callback` has to be called with an `response` object: * `requestHeaders` Object (optional) - When provided, request will be made with these headers. -#### `ses.webRequest.onSendHeaders([filter, ]listener)` +### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -442,7 +442,7 @@ response are visible by the time this listener is fired. * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter,]listener)` +### `ses.webRequest.onHeadersReceived([filter,]listener)` * `filter` Object * `listener` Function @@ -471,7 +471,7 @@ The `callback` has to be called with an `response` object: `responseHeaders` to change header status otherwise original response header's status will be used. -#### `ses.webRequest.onResponseStarted([filter, ]listener)` +### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -492,7 +492,7 @@ and response headers are available. * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -513,7 +513,7 @@ redirect is about to occur. * `fromCache` Boolean * `responseHeaders` Object -#### `ses.webRequest.onCompleted([filter, ]listener)` +### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -532,7 +532,7 @@ completed. * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onErrorOccurred([filter, ]listener)` +### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function @@ -548,7 +548,7 @@ The `listener` will be called with `listener(details)` when an error occurs. * `fromCache` Boolean * `error` String - The error description. -#### `ses.protocol` +### `ses.protocol` Returns an instance of [protocol](protocol.md) module for this session. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index fec4708d1c60..220776b05847 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -1035,7 +1035,7 @@ Detaches the debugger from the `webContents`. Send given command to the debugging target. -#### Event: 'detach' +### Event: 'detach' * `event` Event * `reason` String - Reason for detaching debugger. @@ -1043,7 +1043,7 @@ Send given command to the debugging target. Emitted when debugging session is terminated. This happens either when `webContents` is closed or devtools is invoked for the attached `webContents`. -#### Event: 'message' +### Event: 'message' * `event` Event * `method` String - Method name. From 74e0535659d1f37060680e4730904c4a47e39eba Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Fri, 17 Jun 2016 12:26:08 -0700 Subject: [PATCH 036/192] use unformatted raw code examples in styleguide --- docs/styleguide.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index 44afe2aadb81..c388b3184b01 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -51,14 +51,12 @@ Here are some tips for understanding Electron documentation syntax. An example of [method](https://developer.mozilla.org/en-US/docs/Glossary/Method) documentation: ---- - +``` `methodName(required[, optional]))` -* `require` String (**required**) +* `required` String (**required**) * `optional` Integer - ---- +``` The method name is followed by the arguments it takes. Optional arguments are notated by brackets surrounding the optional argument as well as the comma @@ -77,15 +75,13 @@ or a custom type like Electron's [`webContent`](api/web-content.md). An example of [event](https://developer.mozilla.org/en-US/docs/Web/API/Event) documentation: ---- - +``` Event: 'wake-up' Returns: * `time` String - ---- +``` The event is a string that is used after a `.on` listener method. If it returns a value it and its type is noted below. If you were to listen and respond to @@ -93,6 +89,6 @@ this event it might look something like this: ```javascript Alarm.on('wake-up', (time) => { - console.log(time); -}); + console.log(time) +}) ``` From 49fd3ff41b64e388b8bd81db97b0af54f484a5dd Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Fri, 17 Jun 2016 12:33:50 -0700 Subject: [PATCH 037/192] document platform-specific arguments --- docs/styleguide.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/styleguide.md b/docs/styleguide.md index c388b3184b01..b4d9150ae030 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -70,6 +70,14 @@ of argument is notated by either the common types: [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) or a custom type like Electron's [`webContent`](api/web-content.md). +If an argument is unique to certain platforms, those platforms are denoted +using a space-delimited italicized list following the datatype. Values can be +`OS X`, `Windows`, or `Linux`. + +``` +* `animate` Boolean (optional) _OS X_ _Windows_ +``` + ### Events An example of [event](https://developer.mozilla.org/en-US/docs/Web/API/Event) From a82a2485d7191acc88ba11f83bc1f5b2d05f6a5e Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Fri, 17 Jun 2016 14:01:33 -0700 Subject: [PATCH 038/192] fix debugger event headings --- docs/api/web-contents.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 220776b05847..fec4708d1c60 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -1035,7 +1035,7 @@ Detaches the debugger from the `webContents`. Send given command to the debugging target. -### Event: 'detach' +#### Event: 'detach' * `event` Event * `reason` String - Reason for detaching debugger. @@ -1043,7 +1043,7 @@ Send given command to the debugging target. Emitted when debugging session is terminated. This happens either when `webContents` is closed or devtools is invoked for the attached `webContents`. -### Event: 'message' +#### Event: 'message' * `event` Event * `method` String - Method name. From 574e8974b666dc202523cd49687895895ca224bd Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Mon, 20 Jun 2016 08:30:09 -0700 Subject: [PATCH 039/192] undo heading changes to special-case session --- docs/api/session.md | 58 ++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/api/session.md b/docs/api/session.md index e4ef07d6c772..0486b1ddbdd6 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -50,11 +50,11 @@ const session = require('electron').session; const ses = session.fromPartition('persist:name'); ``` -## Events +### Instance Events The following events are available on instances of `Session`: -### Event: 'will-download' +#### Event: 'will-download' * `event` Event * `item` [DownloadItem](download-item.md) @@ -74,11 +74,11 @@ session.defaultSession.on('will-download', (event, item, webContents) => { }); ``` -## Instance Methods +### Instance Methods The following methods are available on instances of `Session`: -### `ses.cookies` +#### `ses.cookies` The `cookies` gives you ability to query and modify cookies. For example: @@ -102,7 +102,7 @@ session.defaultSession.cookies.set(cookie, (error) => { }); ``` -### `ses.cookies.get(filter, callback)` +#### `ses.cookies.get(filter, callback)` * `filter` Object * `url` String (optional) - Retrieves cookies which are associated with @@ -134,7 +134,7 @@ with `callback(error, cookies)` on complete. the number of seconds since the UNIX epoch. Not provided for session cookies. -### `ses.cookies.set(details, callback)` +#### `ses.cookies.set(details, callback)` * `details` Object * `url` String - The url to associate the cookie with. @@ -154,7 +154,7 @@ with `callback(error, cookies)` on complete. Sets a cookie with `details`, `callback` will be called with `callback(error)` on complete. -### `ses.cookies.remove(url, name, callback)` +#### `ses.cookies.remove(url, name, callback)` * `url` String - The URL associated with the cookie. * `name` String - The name of cookie to remove. @@ -163,20 +163,20 @@ on complete. Removes the cookies matching `url` and `name`, `callback` will called with `callback()` on complete. -### `ses.getCacheSize(callback)` +#### `ses.getCacheSize(callback)` * `callback` Function * `size` Integer - Cache size used in bytes. Returns the session's current cache size. -### `ses.clearCache(callback)` +#### `ses.clearCache(callback)` * `callback` Function - Called when operation is done Clears the session’s HTTP cache. -### `ses.clearStorageData([options, ]callback)` +#### `ses.clearStorageData([options, ]callback)` * `options` Object (optional) * `origin` String - Should follow `window.location.origin`’s representation @@ -190,11 +190,11 @@ Clears the session’s HTTP cache. Clears the data of web storages. -### `ses.flushStorageData()` +#### `ses.flushStorageData()` Writes any unwritten DOMStorage data to disk. -### `ses.setProxy(config, callback)` +#### `ses.setProxy(config, callback)` * `config` Object * `pacScript` String - The URL associated with the PAC file. @@ -239,14 +239,14 @@ For example: Resolves the proxy information for `url`. The `callback` will be called with `callback(proxy)` when the request is performed. -### `ses.setDownloadPath(path)` +#### `ses.setDownloadPath(path)` * `path` String - The download location Sets download saving directory. By default, the download directory will be the `Downloads` under the respective app folder. -### `ses.enableNetworkEmulation(options)` +#### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - Whether to emulate network outage. @@ -268,12 +268,12 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -### `ses.disableNetworkEmulation()` +#### `ses.disableNetworkEmulation()` Disables any network emulation already active for the `session`. Resets to the original network configuration. -### `ses.setCertificateVerifyProc(proc)` +#### `ses.setCertificateVerifyProc(proc)` * `proc` Function @@ -294,7 +294,7 @@ myWindow.webContents.session.setCertificateVerifyProc((hostname, cert, callback) }); ``` -### `ses.setPermissionRequestHandler(handler)` +#### `ses.setPermissionRequestHandler(handler)` * `handler` Function * `webContents` Object - [WebContents](web-contents.md) requesting the permission. @@ -318,13 +318,13 @@ session.fromPartition(partition).setPermissionRequestHandler((webContents, permi }); ``` -### `ses.clearHostResolverCache([callback])` +#### `ses.clearHostResolverCache([callback])` * `callback` Function (optional) - Called when operation is done. Clears the host resolver cache. -### `ses.allowNTLMCredentialsForDomains(domains)` +#### `ses.allowNTLMCredentialsForDomains(domains)` * `domains` String - A comma-seperated list of servers for which integrated authentication is enabled. @@ -341,7 +341,7 @@ session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com session.defaultSession.allowNTLMCredentialsForDomains('*') ``` -### `ses.webRequest` +#### `ses.webRequest` The `webRequest` API set allows to intercept and modify contents of a request at various stages of its lifetime. @@ -370,7 +370,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback }); ``` -### `ses.webRequest.onBeforeRequest([filter, ]listener)` +#### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -400,7 +400,7 @@ The `callback` has to be called with an `response` object: * `redirectURL` String (optional) - The original request is prevented from being sent or completed, and is instead redirected to the given URL. -### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -425,7 +425,7 @@ The `callback` has to be called with an `response` object: * `requestHeaders` Object (optional) - When provided, request will be made with these headers. -### `ses.webRequest.onSendHeaders([filter, ]listener)` +#### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -442,7 +442,7 @@ response are visible by the time this listener is fired. * `timestamp` Double * `requestHeaders` Object -### `ses.webRequest.onHeadersReceived([filter,]listener)` +#### `ses.webRequest.onHeadersReceived([filter,]listener)` * `filter` Object * `listener` Function @@ -471,7 +471,7 @@ The `callback` has to be called with an `response` object: `responseHeaders` to change header status otherwise original response header's status will be used. -### `ses.webRequest.onResponseStarted([filter, ]listener)` +#### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -492,7 +492,7 @@ and response headers are available. * `statusCode` Integer * `statusLine` String -### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -513,7 +513,7 @@ redirect is about to occur. * `fromCache` Boolean * `responseHeaders` Object -### `ses.webRequest.onCompleted([filter, ]listener)` +#### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -532,7 +532,7 @@ completed. * `statusCode` Integer * `statusLine` String -### `ses.webRequest.onErrorOccurred([filter, ]listener)` +#### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function @@ -548,7 +548,7 @@ The `listener` will be called with `listener(details)` when an error occurs. * `fromCache` Boolean * `error` String - The error description. -### `ses.protocol` +#### `ses.protocol` Returns an instance of [protocol](protocol.md) module for this session. From 0ff50650d283fd6d9e52dde363323d60621182a2 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Mon, 20 Jun 2016 16:51:31 -0700 Subject: [PATCH 040/192] document drop-files event type --- docs/api/tray.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/tray.md b/docs/api/tray.md index aed699b8ee43..a93e0dbf38d5 100644 --- a/docs/api/tray.md +++ b/docs/api/tray.md @@ -117,7 +117,7 @@ Emitted when any dragged items are dropped on the tray icon. #### Event: 'drop-files' _macOS_ -* `event` +* `event` Event * `files` Array - the file path of dropped files. Emitted when dragged files are dropped in the tray icon. From 46c1ef60aaa73209083d878817105957119bd5a4 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Tue, 21 Jun 2016 10:00:55 -0700 Subject: [PATCH 041/192] restore headings in translated session docs --- docs-translations/jp/api/session.md | 44 ++++++++++----------- docs-translations/ko-KR/api/session.md | 54 +++++++++++++------------- docs-translations/zh-CN/api/session.md | 50 ++++++++++++------------ 3 files changed, 74 insertions(+), 74 deletions(-) diff --git a/docs-translations/jp/api/session.md b/docs-translations/jp/api/session.md index 84e0d08deb51..a5d47d404a6d 100644 --- a/docs-translations/jp/api/session.md +++ b/docs-translations/jp/api/session.md @@ -70,7 +70,7 @@ session.defaultSession.on('will-download', function(event, item, webContents) { `Session`のインスタンス上で次のメソッドが提供されています: -### `ses.cookies` +#### `ses.cookies` `cookies`は、cookiesに問い合わせしたり、修正をできるようにします。例: @@ -94,7 +94,7 @@ session.defaultSession.cookies.set(cookie, function(error) { }); ``` -### `ses.cookies.get(filter, callback)` +#### `ses.cookies.get(filter, callback)` * `filter` Object * `url` String (オプション) - `url`に関連付けられているcookiesを取得します。空の場合すべてのurlのcookiesを取得します @@ -122,7 +122,7 @@ session.defaultSession.cookies.set(cookie, function(error) { cookieの有効期限をUNIX時間で何秒かを示します。セッションcookiesは提供されません。 -### `ses.cookies.set(details, callback)` +#### `ses.cookies.set(details, callback)` * `details` Object * `url` String - `url`に関連付けられているcookiesを取得します。 @@ -137,7 +137,7 @@ cookieの有効期限をUNIX時間で何秒かを示します。セッションc `details`でcookieを設定し、完了すると`callback(error)`で`callback`がコールされます。 -### `ses.cookies.remove(url, name, callback)` +#### `ses.cookies.remove(url, name, callback)` * `url` String - cookieに関連付けられているURL * `name` String - 削除するcookieの名前 @@ -145,20 +145,20 @@ cookieの有効期限をUNIX時間で何秒かを示します。セッションc `url` と `name`と一致するcookiesを削除し、完了すると`callback`が、`callback()`でコールされます。 -### `ses.getCacheSize(callback)` +#### `ses.getCacheSize(callback)` * `callback` Function * `size` Integer - 使用しているキャッシュサイズバイト数 現在のセッションのキャッシュサイズを返します。 -### `ses.clearCache(callback)` +#### `ses.clearCache(callback)` * `callback` Function - 操作が完了したら、コールされます。 セッションのHTTPキャッシュをクリアします。 -### `ses.clearStorageData([options, ]callback)` +#### `ses.clearStorageData([options, ]callback)` * `options` Object (オプション) * `origin` String - `window.location.origin`の説明で、`scheme://host:port`に従う @@ -171,11 +171,11 @@ cookieの有効期限をUNIX時間で何秒かを示します。セッションc ウェブストレージのデータをクリアします。 -### `ses.flushStorageData()` +#### `ses.flushStorageData()` 書き込まれていないDOMStorageデータをディスクに書き込みます。 -### `ses.setProxy(config, callback)` +#### `ses.setProxy(config, callback)` * `config` Object * `pacScript` String - PACファイルに関連付けらえたURL @@ -213,13 +213,13 @@ proxyURL = ["://"][":"] `url`をプロキシ情報で解決します。リクエストが実行された時、`callback(proxy)`で `callback`がコールされます。 -### `ses.setDownloadPath(path)` +#### `ses.setDownloadPath(path)` * `path` String - ダウンロード場所 ダウンロードの保存ディレクトリを設定します。既定では、ダウンロードディレクトリは、個別のアプリフォルダー下の`Downloads`です。 -### `ses.enableNetworkEmulation(options)` +#### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - ネットワーク停止を再現するかどうか @@ -241,11 +241,11 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -### `ses.disableNetworkEmulation()` +#### `ses.disableNetworkEmulation()` `session`ですでに有効になっているネットワークエミュレーションを無効化します。オリジナルのネットワーク設定にリセットします。 -### `ses.setCertificateVerifyProc(proc)` +#### `ses.setCertificateVerifyProc(proc)` * `proc` Function @@ -262,7 +262,7 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c }); ``` -### `ses.webRequest` +#### `ses.webRequest` `webRequest`APIセットをインターセプトし、そのライフタイムの様々な段階でリクエストの内容を変更できます。 @@ -284,7 +284,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, }); ``` -### `ses.webRequest.onBeforeRequest([filter, ]listener)` +#### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -312,7 +312,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `cancel` Boolean (オプション) * `redirectURL` String (オプション) - オリジナルリクエストが送信もしくは完了するのを中断し、代わりに付与したURLにリダイレクトします。 -### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -334,7 +334,7 @@ The `callback` has to be called with an `response` object: * `cancel` Boolean (オプション) * `requestHeaders` Object (オプション) - 付与されると、リクエストはそれらのヘッダーで作成されます。 -### `ses.webRequest.onSendHeaders([filter, ]listener)` +#### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -349,7 +349,7 @@ The `callback` has to be called with an `response` object: * `timestamp` Double * `requestHeaders` Object -### `ses.webRequest.onHeadersReceived([filter,] listener)` +#### `ses.webRequest.onHeadersReceived([filter,] listener)` * `filter` Object * `listener` Function @@ -373,7 +373,7 @@ The `callback` has to be called with an `response` object: * `cancel` Boolean * `responseHeaders` Object (オプション) - 付与されていると、これらのヘッダーでサーバーはレスポンスしたと仮定します。 -### `ses.webRequest.onResponseStarted([filter, ]listener)` +#### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -391,7 +391,7 @@ The `callback` has to be called with an `response` object: * `statusCode` Integer * `statusLine` String -### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -410,7 +410,7 @@ The `callback` has to be called with an `response` object: * `fromCache` Boolean * `responseHeaders` Object -### `ses.webRequest.onCompleted([filter, ]listener)` +#### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -428,7 +428,7 @@ The `callback` has to be called with an `response` object: * `statusCode` Integer * `statusLine` String -### `ses.webRequest.onErrorOccurred([filter, ]listener)` +#### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function diff --git a/docs-translations/ko-KR/api/session.md b/docs-translations/ko-KR/api/session.md index 5ee37a270535..a2dc9b682a6b 100644 --- a/docs-translations/ko-KR/api/session.md +++ b/docs-translations/ko-KR/api/session.md @@ -53,7 +53,7 @@ const ses = session.fromPartition('persist:name'); `Session` 객체는 다음과 같은 이벤트를 가지고 있습니다: -### Event: 'will-download' +#### Event: 'will-download' * `event` Event * `item` [DownloadItem](download-item.md) @@ -77,7 +77,7 @@ session.defaultSession.on('will-download', (event, item, webContents) => { `Session` 객체는 다음과 같은 메서드와 속성을 가지고 있습니다: -### `ses.cookies` +#### `ses.cookies` `cookies` 속성은 쿠키를 조작하는 방법을 제공합니다. 예를 들어 다음과 같이 할 수 있습니다: @@ -102,7 +102,7 @@ session.defaultSession.cookies.set(cookie, (error) => { }); ``` -### `ses.cookies.get(filter, callback)` +#### `ses.cookies.get(filter, callback)` * `filter` Object * `url` String (optional) - `url`에 해당하는 쿠키를 취득합니다. 이 속성을 @@ -133,7 +133,7 @@ session.defaultSession.cookies.set(cookie, (error) => { * `expirationDate` Double - (Option) UNIX 시간으로 표시되는 쿠키의 만료일에 대한 초 단위 시간. 세션 쿠키는 지원되지 않음. -### `ses.cookies.set(details, callback)` +#### `ses.cookies.set(details, callback)` * `details` Object * `url` String - 쿠키에 대한 `url` 링크. @@ -153,7 +153,7 @@ session.defaultSession.cookies.set(cookie, (error) => { `details` 객체에 따라 쿠키를 설정합니다. 작업이 완료되면 `callback`이 `callback(error)` 형태로 호출됩니다. -### `ses.cookies.remove(url, name, callback)` +#### `ses.cookies.remove(url, name, callback)` * `url` String - 쿠키와 관련된 URL입니다. * `name` String - 지울 쿠키의 이름입니다. @@ -162,20 +162,20 @@ session.defaultSession.cookies.set(cookie, (error) => { `url`과 `name`에 일치하는 쿠키를 삭제합니다. 작업이 완료되면 `callback`이 `callback()` 형식으로 호출됩니다. -### `ses.getCacheSize(callback)` +#### `ses.getCacheSize(callback)` * `callback` Function * `size` Integer - 바이트로 표현된 캐시 크기 세션의 현재 캐시 크기를 반환합니다. -### `ses.clearCache(callback)` +#### `ses.clearCache(callback)` * `callback` Function - 작업이 완료되면 호출됩니다. 세션의 HTTP 캐시를 비웁니다. -### `ses.clearStorageData([options, ]callback)` +#### `ses.clearStorageData([options, ]callback)` * `options` Object (optional), proprties: * `origin` String - `scheme://host:port`와 같은 `window.location.origin` 규칙을 @@ -189,11 +189,11 @@ session.defaultSession.cookies.set(cookie, (error) => { 웹 스토리지의 데이터를 비웁니다. -### `ses.flushStorageData()` +#### `ses.flushStorageData()` 디스크에 사용되지 않은 DOMStorage 데이터를 모두 덮어씌웁니다. -### `ses.setProxy(config, callback)` +#### `ses.setProxy(config, callback)` * `config` Object * `pacScript` String - PAC 파일과 관련된 URL입니다. @@ -240,14 +240,14 @@ proxyURL = ["://"][":"] `url`의 프록시 정보를 해석합니다. `callback`은 요청이 수행되었을 때 `callback(proxy)` 형태로 호출됩니다. -### `ses.setDownloadPath(path)` +#### `ses.setDownloadPath(path)` * `path` String - 다운로드 위치 다운로드 저장 위치를 지정합니다. 기본 다운로드 위치는 각 애플리케이션 데이터 디렉터리의 `Downloads` 폴더입니다. -### `ses.enableNetworkEmulation(options)` +#### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - 네트워크의 오프라인 상태 여부 @@ -269,11 +269,11 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -### `ses.disableNetworkEmulation()` +#### `ses.disableNetworkEmulation()` 활성화된 `session`의 에뮬레이션을 비활성화합니다. 기본 네트워크 설정으로 돌아갑니다. -### `ses.setCertificateVerifyProc(proc)` +#### `ses.setCertificateVerifyProc(proc)` * `proc` Function @@ -292,7 +292,7 @@ myWindow.webContents.session.setCertificateVerifyProc((hostname, cert, callback) callback(false); }); ``` -### `ses.setPermissionRequestHandler(handler)` +#### `ses.setPermissionRequestHandler(handler)` * `handler` Function * `webContents` Object - [WebContents](web-contents.md) 권한을 요청. @@ -317,13 +317,13 @@ session.fromPartition(partition).setPermissionRequestHandler((webContents, permi }); ``` -### `ses.clearHostResolverCache([callback])` +#### `ses.clearHostResolverCache([callback])` * `callback` Function (optional) - 작업이 완료되면 호출됩니다. 호스트 리소버(resolver) 캐시를 지웁니다. -### `ses.allowNTLMCredentialsForDomains(domains)` +#### `ses.allowNTLMCredentialsForDomains(domains)` * `domains` String - 통합 인증을 사용하도록 설정할 쉼표로 구분된 서버의 리스트. @@ -339,7 +339,7 @@ session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com session.defaultSession.allowNTLMCredentialsForDomains('*') ``` -### `ses.webRequest` +#### `ses.webRequest` `webRequest` API는 생명주기의 다양한 단계에 맞춰 요청 콘텐츠를 가로채거나 변경할 수 있도록 합니다. @@ -367,7 +367,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback }); ``` -### `ses.webRequest.onBeforeRequest([filter, ]listener)` +#### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -396,7 +396,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback * `redirectURL` String (optional) - 원래 요청은 전송과 완료가 방지되지만 이 속성을 지정하면 해당 URL로 리다이렉트됩니다. -### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -421,7 +421,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `requestHeaders` Object (optional) - 이 속성이 제공되면, 요청은 이 헤더로 만들어 집니다. -### `ses.webRequest.onSendHeaders([filter, ]listener)` +#### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -438,7 +438,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `timestamp` Double * `requestHeaders` Object -### `ses.webRequest.onHeadersReceived([filter, ]listener)` +#### `ses.webRequest.onHeadersReceived([filter, ]listener)` * `filter` Object * `listener` Function @@ -467,7 +467,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener 변경하기 위해 반드시 지정되어야 합니다. 그렇지 않은 경우, 기존의 응답 헤더의 상태가 사용됩니다. -### `ses.webRequest.onResponseStarted([filter, ]listener)` +#### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -486,7 +486,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `statusCode` Integer * `statusLine` String -### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -506,7 +506,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `fromCache` Boolean * `responseHeaders` Object -### `ses.webRequest.onCompleted([filter, ]listener)` +#### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -524,7 +524,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `statusCode` Integer * `statusLine` String -### `ses.webRequest.onErrorOccurred([filter, ]listener)` +#### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function @@ -540,7 +540,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `fromCache` Boolean * `error` String - 에러 설명. -### `ses.protocol` +#### `ses.protocol` 현재 세션의 [protocol](protocol.md) 모듈 인스턴스를 반환합니다. diff --git a/docs-translations/zh-CN/api/session.md b/docs-translations/zh-CN/api/session.md index b96290dbe330..b9664c08e652 100644 --- a/docs-translations/zh-CN/api/session.md +++ b/docs-translations/zh-CN/api/session.md @@ -47,7 +47,7 @@ var ses = session.fromPartition('persist:name'); 实例 `Session` 有以下事件: -### Event: 'will-download' +#### Event: 'will-download' * `event` Event * `item` [DownloadItem](download-item.md) @@ -70,7 +70,7 @@ session.defaultSession.on('will-download', function(event, item, webContents) { 实例 `Session` 有以下方法: -### `ses.cookies` +#### `ses.cookies` `cookies` 赋予你全力来查询和修改 cookies. 例如: @@ -94,7 +94,7 @@ session.defaultSession.cookies.set(cookie, function(error) { }); ``` -### `ses.cookies.get(filter, callback)` +#### `ses.cookies.get(filter, callback)` * `filter` Object * `url` String (可选) - 与获取 cookies 相关的 @@ -123,7 +123,7 @@ session.defaultSession.cookies.set(cookie, function(error) { cookie . * `expirationDate` Double (可选) - cookie的截至日期,数值为UNIX纪元以来的秒数. 对session cookies 不提供. -### `ses.cookies.set(details, callback)` +#### `ses.cookies.set(details, callback)` * `details` Object * `url` String - 与获取 cookies 相关的 @@ -140,7 +140,7 @@ session.defaultSession.cookies.set(cookie, function(error) { 使用 `details` 设置 cookie, 完成时使用 `callback(error)` 掉哟个 `callback` . -### `ses.cookies.remove(url, name, callback)` +#### `ses.cookies.remove(url, name, callback)` * `url` String - 与 cookies 相关的 `url`. @@ -149,20 +149,20 @@ session.defaultSession.cookies.set(cookie, function(error) { 删除匹配 `url` 和 `name` 的 cookie, 完成时使用 `callback()`调用`callback`. -### `ses.getCacheSize(callback)` +#### `ses.getCacheSize(callback)` * `callback` Function * `size` Integer - 单位 bytes 的缓存 size. 返回 session 的当前缓存 size . -### `ses.clearCache(callback)` +#### `ses.clearCache(callback)` * `callback` Function - 操作完成时调用 清空 session 的 HTTP 缓存. -### `ses.clearStorageData([options, ]callback)` +#### `ses.clearStorageData([options, ]callback)` * `options` Object (可选) * `origin` String - 应当遵循 `window.location.origin` 的格式 @@ -176,11 +176,11 @@ session.defaultSession.cookies.set(cookie, function(error) { 清除 web storages 的数据. -### `ses.flushStorageData()` +#### `ses.flushStorageData()` 将没有写入的 DOMStorage 写入磁盘. -### `ses.setProxy(config, callback)` +#### `ses.setProxy(config, callback)` * `config` Object * `pacScript` String - 与 PAC 文件相关的 URL. @@ -219,13 +219,13 @@ proxyURL = ["://"][":"] 解析 `url` 的代理信息.当请求完成的时候使用 `callback(proxy)` 调用 `callback`. -### `ses.setDownloadPath(path)` +#### `ses.setDownloadPath(path)` * `path` String - 下载地址 设置下载保存地址,默认保存地址为各自 app 应用的 `Downloads`目录. -### `ses.enableNetworkEmulation(options)` +#### `ses.enableNetworkEmulation(options)` * `options` Object * `offline` Boolean - 是否模拟网络故障. @@ -247,12 +247,12 @@ window.webContents.session.enableNetworkEmulation({ window.webContents.session.enableNetworkEmulation({offline: true}); ``` -### `ses.disableNetworkEmulation()` +#### `ses.disableNetworkEmulation()` 停止所有已经使用 `session` 的活跃模拟网络. 重置为原始网络类型. -### `ses.setCertificateVerifyProc(proc)` +#### `ses.setCertificateVerifyProc(proc)` * `proc` Function @@ -269,7 +269,7 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c }); ``` -### `ses.setPermissionRequestHandler(handler)` +#### `ses.setPermissionRequestHandler(handler)` * `handler` Function * `webContents` Object - [WebContents](web-contents.md) 请求许可. @@ -291,13 +291,13 @@ session.fromPartition(partition).setPermissionRequestHandler(function(webContent }); ``` -### `ses.clearHostResolverCache([callback])` +#### `ses.clearHostResolverCache([callback])` * `callback` Function (可选) - 操作结束调用. 清除主机解析缓存. -### `ses.webRequest` +#### `ses.webRequest` 在其生命周期的不同阶段,`webRequest` API 设置允许拦截并修改请求内容. @@ -319,7 +319,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, }); ``` -### `ses.webRequest.onBeforeRequest([filter, ]listener)` +#### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -347,7 +347,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `cancel` Boolean (可选) * `redirectURL` String (可选) - 原始请求阻止发送或完成,而不是重定向. -### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -369,7 +369,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `cancel` Boolean (可选) * `requestHeaders` Object (可选) - 如果提供了,将使用这些 headers 来创建请求. -### `ses.webRequest.onSendHeaders([filter, ]listener)` +#### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -384,7 +384,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `timestamp` Double * `requestHeaders` Object -### `ses.webRequest.onHeadersReceived([filter,] listener)` +#### `ses.webRequest.onHeadersReceived([filter,] listener)` * `filter` Object * `listener` Function @@ -408,7 +408,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `cancel` Boolean * `responseHeaders` Object (可选) - 如果提供, 服务器将假定使用这些头来响应. -### `ses.webRequest.onResponseStarted([filter, ]listener)` +#### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -427,7 +427,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `statusCode` Integer * `statusLine` String -### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -446,7 +446,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `fromCache` Boolean * `responseHeaders` Object -### `ses.webRequest.onCompleted([filter, ]listener)` +#### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -464,7 +464,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, * `statusCode` Integer * `statusLine` String -### `ses.webRequest.onErrorOccurred([filter, ]listener)` +#### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function From 2a079b2e0a78d6c2925cb3042228ff324e8c7445 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Tue, 21 Jun 2016 11:05:34 -0700 Subject: [PATCH 042/192] =?UTF-8?q?wether=20=E2=86=92=20whether?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api/web-contents.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index fec4708d1c60..610fa2aa6d73 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -339,9 +339,9 @@ Returns: was invoked on. Elements with source URLs are images, audio and video. * `mediaType` String - Type of the node the context menu was invoked on. Can be `none`, `image`, `audio`, `video`, `canvas`, `file` or `plugin`. - * `hasImageContent` Boolean - Wether the context menu was invoked on an image + * `hasImageContent` Boolean - Whether the context menu was invoked on an image which has non-empty contents. - * `isEditable` Boolean - Wether the context is editable. + * `isEditable` Boolean - Whether the context is editable. * `selectionText` String - Text of the selection that the context menu was invoked on. * `titleText` String - Title or alt text of the selection that the context @@ -356,29 +356,29 @@ Returns: Can be `none`, `mouse`, `keyboard`, `touch`, `touchMenu`. * `mediaFlags` Object - The flags for the media element the context menu was invoked on. See more about this below. - * `editFlags` Object - These flags indicate wether the renderer believes it is + * `editFlags` Object - These flags indicate whether the renderer believes it is able to perform the corresponding action. See more about this below. The `mediaFlags` is an object with the following properties: - * `inError` Boolean - Wether the media element has crashed. - * `isPaused` Boolean - Wether the media element is paused. - * `isMuted` Boolean - Wether the media element is muted. - * `hasAudio` Boolean - Wether the media element has audio. - * `isLooping` Boolean - Wether the media element is looping. - * `isControlsVisible` Boolean - Wether the media element's controls are + * `inError` Boolean - Whether the media element has crashed. + * `isPaused` Boolean - Whether the media element is paused. + * `isMuted` Boolean - Whether the media element is muted. + * `hasAudio` Boolean - Whether the media element has audio. + * `isLooping` Boolean - Whether the media element is looping. + * `isControlsVisible` Boolean - Whether the media element's controls are visible. - * `canToggleControls` Boolean - Wether the media element's controls are + * `canToggleControls` Boolean - Whether the media element's controls are toggleable. - * `canRotate` Boolean - Wether the media element can be rotated. + * `canRotate` Boolean - Whether the media element can be rotated. The `editFlags` is an object with the following properties: - * `canUndo` Boolean - Wether the renderer believes it can undo. - * `canRedo` Boolean - Wether the renderer believes it can redo. - * `canCut` Boolean - Wether the renderer believes it can cut. - * `canCopy` Boolean - Wether the renderer believes it can copy - * `canPaste` Boolean - Wether the renderer believes it can paste. - * `canDelete` Boolean - Wether the renderer believes it can delete. - * `canSelectAll` Boolean - Wether the renderer believes it can select all. + * `canUndo` Boolean - Whether the renderer believes it can undo. + * `canRedo` Boolean - Whether the renderer believes it can redo. + * `canCut` Boolean - Whether the renderer believes it can cut. + * `canCopy` Boolean - Whether the renderer believes it can copy + * `canPaste` Boolean - Whether the renderer believes it can paste. + * `canDelete` Boolean - Whether the renderer believes it can delete. + * `canSelectAll` Boolean - Whether the renderer believes it can select all. Emitted when there is a new context menu that needs to be handled. From 90d1b9169f22aa771425cd955d195dc3bf2ba4ae Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Tue, 21 Jun 2016 22:55:26 -0700 Subject: [PATCH 043/192] prepositions are hard --- docs/api/browser-window.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index e95570450b62..a87be1e59743 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -546,7 +546,7 @@ Focus on the window. ### `win.blur()` -Remove focus on the window. +Remove focus from the window. ### `win.isFocused()` From 72403c1a8db4167e559da928ae8e8f4343b93da6 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Tue, 21 Jun 2016 22:56:51 -0700 Subject: [PATCH 044/192] use consistent verb style --- docs/api/browser-window.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index a87be1e59743..22b01244422c 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -542,11 +542,11 @@ the [close event](#event-close). ### `win.focus()` -Focus on the window. +Focuses on the window. ### `win.blur()` -Remove focus from the window. +Removes focus from the window. ### `win.isFocused()` From 271c39ceb3990931a809715d9d2d66d029a3e826 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Tue, 21 Jun 2016 23:02:01 -0700 Subject: [PATCH 045/192] =?UTF-8?q?OS=20X=20=E2=86=92=20macOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/styleguide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index b4d9150ae030..21d7c63b3455 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -72,10 +72,10 @@ or a custom type like Electron's [`webContent`](api/web-content.md). If an argument is unique to certain platforms, those platforms are denoted using a space-delimited italicized list following the datatype. Values can be -`OS X`, `Windows`, or `Linux`. +`macOS`, `Windows`, or `Linux`. ``` -* `animate` Boolean (optional) _OS X_ _Windows_ +* `animate` Boolean (optional) _macOS_ _Windows_ ``` ### Events From b0f04de50c40d7a42f7e0eb33c3d82082033af46 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 15:41:56 +0900 Subject: [PATCH 046/192] Do not force creating default session in C++ --- atom/browser/atom_access_token_store.cc | 39 +++++++++++++++++-------- atom/browser/atom_access_token_store.h | 9 ++++-- vendor/brightray | 2 +- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/atom/browser/atom_access_token_store.cc b/atom/browser/atom_access_token_store.cc index ed87ea979920..9f57148120c8 100644 --- a/atom/browser/atom_access_token_store.cc +++ b/atom/browser/atom_access_token_store.cc @@ -7,8 +7,8 @@ #include #include "atom/browser/atom_browser_context.h" -#include "atom/browser/atom_browser_main_parts.h" #include "atom/common/google_api_key.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/geolocation_provider.h" namespace atom { @@ -25,6 +25,7 @@ const char* kGeolocationProviderURL = } // namespace AtomAccessTokenStore::AtomAccessTokenStore() { + LOG(ERROR) << "AtomAccessTokenStore"; content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices(); } @@ -33,21 +34,35 @@ AtomAccessTokenStore::~AtomAccessTokenStore() { void AtomAccessTokenStore::LoadAccessTokens( const LoadAccessTokensCallback& callback) { - AccessTokenMap access_token_map; - - // Equivelent to access_token_map[kGeolocationProviderURL]. - // Somehow base::string16 is causing compilation errors when used in a pair - // of std::map on Linux, this can work around it. - std::pair token_pair; - token_pair.first = GURL(kGeolocationProviderURL); - access_token_map.insert(token_pair); - - auto browser_context = AtomBrowserMainParts::Get()->browser_context(); - callback.Run(access_token_map, browser_context->url_request_context_getter()); + content::BrowserThread::PostTaskAndReply( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(&AtomAccessTokenStore::GetRequestContextOnUIThread, this), + base::Bind(&AtomAccessTokenStore::RespondOnOriginatingThread, + this, callback)); } void AtomAccessTokenStore::SaveAccessToken(const GURL& server_url, const base::string16& access_token) { } +void AtomAccessTokenStore::GetRequestContextOnUIThread() { + auto browser_context = brightray::BrowserContext::From("", false); + request_context_getter_ = browser_context->GetRequestContext(); +} + +void AtomAccessTokenStore::RespondOnOriginatingThread( + const LoadAccessTokensCallback& callback) { + // Equivelent to access_token_map[kGeolocationProviderURL]. + // Somehow base::string16 is causing compilation errors when used in a pair + // of std::map on Linux, this can work around it. + AccessTokenMap access_token_map; + std::pair token_pair; + token_pair.first = GURL(kGeolocationProviderURL); + access_token_map.insert(token_pair); + + callback.Run(access_token_map, request_context_getter_.get()); + request_context_getter_ = nullptr; +} + } // namespace atom diff --git a/atom/browser/atom_access_token_store.h b/atom/browser/atom_access_token_store.h index 54bc2cee3c29..27c1911a65fa 100644 --- a/atom/browser/atom_access_token_store.h +++ b/atom/browser/atom_access_token_store.h @@ -9,12 +9,10 @@ namespace atom { -class AtomBrowserContext; - class AtomAccessTokenStore : public content::AccessTokenStore { public: AtomAccessTokenStore(); - virtual ~AtomAccessTokenStore(); + ~AtomAccessTokenStore(); // content::AccessTokenStore: void LoadAccessTokens( @@ -23,6 +21,11 @@ class AtomAccessTokenStore : public content::AccessTokenStore { const base::string16& access_token) override; private: + void GetRequestContextOnUIThread(); + void RespondOnOriginatingThread(const LoadAccessTokensCallback& callback); + + scoped_refptr request_context_getter_; + DISALLOW_COPY_AND_ASSIGN(AtomAccessTokenStore); }; diff --git a/vendor/brightray b/vendor/brightray index 3a98173c6848..e26f8073df2b 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 3a98173c6848bac241074979b24f5a54cc92b5b0 +Subproject commit e26f8073df2b20ba2169ffb082c5f135d542313b From 9c8e64f26834e5652e6b94d3d256cfba65507499 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 15:46:46 +0900 Subject: [PATCH 047/192] Cache the BrowserContext::GetUserAgent --- atom/browser/atom_browser_context.cc | 25 ++++++++++++++----------- atom/browser/atom_browser_context.h | 1 + 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index 8268a7778193..b9ae67b684a6 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -68,16 +68,7 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, : brightray::BrowserContext(partition, in_memory), cert_verifier_(new AtomCertVerifier), network_delegate_(new AtomNetworkDelegate) { -} - -AtomBrowserContext::~AtomBrowserContext() { -} - -net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() { - return network_delegate_; -} - -std::string AtomBrowserContext::GetUserAgent() { + // Construct user agent string. Browser* browser = Browser::Get(); std::string name = RemoveWhitespace(browser->GetName()); std::string user_agent; @@ -91,7 +82,19 @@ std::string AtomBrowserContext::GetUserAgent() { browser->GetVersion().c_str(), CHROME_VERSION_STRING); } - return content::BuildUserAgentFromProduct(user_agent); + user_agent_ = content::BuildUserAgentFromProduct(user_agent); +} + +AtomBrowserContext::~AtomBrowserContext() { +} + +net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() { + return network_delegate_; +} + +std::string AtomBrowserContext::GetUserAgent() { + LOG(ERROR) << "GetUserAgent"; + return user_agent_; } std::unique_ptr diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index c9b1de947fba..dbbae38272d7 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -47,6 +47,7 @@ class AtomBrowserContext : public brightray::BrowserContext { std::unique_ptr download_manager_delegate_; std::unique_ptr guest_manager_; std::unique_ptr permission_manager_; + std::string user_agent_; // Managed by brightray::BrowserContext. AtomCertVerifier* cert_verifier_; From 01bc8305f8d0477dfea2939f32ffc1ecadf6cf27 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 15:57:51 +0900 Subject: [PATCH 048/192] Add ses.getUserAgent()/setUserAgent(...) APIs --- atom/browser/api/atom_api_session.cc | 30 +++++++++++++++++++++++ atom/browser/api/atom_api_session.h | 21 +++++++++------- atom/browser/api/atom_api_web_contents.cc | 22 +++-------------- atom/browser/api/atom_api_web_contents.h | 2 +- atom/browser/atom_browser_context.cc | 5 +++- atom/browser/atom_browser_context.h | 2 ++ vendor/brightray | 2 +- 7 files changed, 53 insertions(+), 31 deletions(-) diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 602729b9e382..4a4ffba0c84a 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -41,8 +41,10 @@ #include "net/http/http_auth_preferences.h" #include "net/proxy/proxy_service.h" #include "net/proxy/proxy_config_service_fixed.h" +#include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" +#include "ui/base/l10n/l10n_util.h" using content::BrowserThread; using content::StoragePartition; @@ -93,6 +95,15 @@ uint32_t GetQuotaMask(const std::vector& quota_types) { return quota_mask; } +void SetUserAgentInIO(scoped_refptr getter, + const std::string& accept_lang, + const std::string& user_agent) { + getter->GetURLRequestContext()->set_http_user_agent_settings( + new net::StaticHttpUserAgentSettings( + net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang), + user_agent)); +} + } // namespace namespace mate { @@ -455,6 +466,23 @@ void Session::AllowNTLMCredentialsForDomains(const std::string& domains) { domains)); } +void Session::SetUserAgent(const std::string& user_agent, + mate::Arguments* args) { + browser_context_->SetUserAgent(user_agent); + + std::string accept_lang = l10n_util::GetApplicationLocale(""); + args->GetNext(&accept_lang); + + auto getter = browser_context_->GetRequestContext(); + getter->GetNetworkTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&SetUserAgentInIO, getter, accept_lang, user_agent)); +} + +std::string Session::GetUserAgent() { + return browser_context_->GetUserAgent(); +} + v8::Local Session::Cookies(v8::Isolate* isolate) { if (cookies_.IsEmpty()) { auto handle = atom::api::Cookies::Create(isolate, browser_context()); @@ -520,6 +548,8 @@ void Session::BuildPrototype(v8::Isolate* isolate, .SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache) .SetMethod("allowNTLMCredentialsForDomains", &Session::AllowNTLMCredentialsForDomains) + .SetMethod("setUserAgent", &Session::SetUserAgent) + .SetMethod("getUserAgent", &Session::GetUserAgent) .SetProperty("cookies", &Session::Cookies) .SetProperty("protocol", &Session::Protocol) .SetProperty("webRequest", &Session::WebRequest); diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index bb67fa106de1..0868cbe18bee 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -57,15 +57,7 @@ class Session: public mate::TrackableObject, static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); - protected: - Session(v8::Isolate* isolate, AtomBrowserContext* browser_context); - ~Session(); - - // content::DownloadManager::Observer: - void OnDownloadCreated(content::DownloadManager* manager, - content::DownloadItem* item) override; - - private: + // Methods. void ResolveProxy(const GURL& url, ResolveProxyCallback callback); template void DoCacheAction(const net::CompletionCallback& callback); @@ -80,10 +72,21 @@ class Session: public mate::TrackableObject, mate::Arguments* args); void ClearHostResolverCache(mate::Arguments* args); void AllowNTLMCredentialsForDomains(const std::string& domains); + void SetUserAgent(const std::string& user_agent, mate::Arguments* args); + std::string GetUserAgent(); v8::Local Cookies(v8::Isolate* isolate); v8::Local Protocol(v8::Isolate* isolate); v8::Local WebRequest(v8::Isolate* isolate); + protected: + Session(v8::Isolate* isolate, AtomBrowserContext* browser_context); + ~Session(); + + // content::DownloadManager::Observer: + void OnDownloadCreated(content::DownloadManager* manager, + content::DownloadItem* item) override; + + private: // Cached object. v8::Global cookies_; v8::Global protocol_; diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 52d24084d4b3..58b6a4729a84 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -61,11 +61,9 @@ #include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" #include "net/http/http_response_headers.h" -#include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request_context.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebFindOptions.h" -#include "ui/base/l10n/l10n_util.h" #include "atom/common/node_includes.h" @@ -76,15 +74,6 @@ struct PrintSettings { bool print_background; }; -void SetUserAgentInIO(scoped_refptr getter, - std::string accept_lang, - std::string user_agent) { - getter->GetURLRequestContext()->set_http_user_agent_settings( - new net::StaticHttpUserAgentSettings( - net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang), - user_agent)); -} - } // namespace namespace mate { @@ -811,7 +800,7 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) { std::string user_agent; if (options.Get("userAgent", &user_agent)) - SetUserAgent(user_agent); + web_contents()->SetUserAgentOverride(user_agent); std::string extra_headers; if (options.Get("extraHeaders", &extra_headers)) @@ -898,14 +887,9 @@ bool WebContents::IsCrashed() const { return web_contents()->IsCrashed(); } -void WebContents::SetUserAgent(const std::string& user_agent) { +void WebContents::SetUserAgent(const std::string& user_agent, + mate::Arguments* args) { web_contents()->SetUserAgentOverride(user_agent); - scoped_refptr getter = - web_contents()->GetBrowserContext()->GetRequestContext(); - - auto accept_lang = l10n_util::GetApplicationLocale(""); - getter->GetNetworkTaskRunner()->PostTask(FROM_HERE, - base::Bind(&SetUserAgentInIO, getter, accept_lang, user_agent)); } std::string WebContents::GetUserAgent() { diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 6151110887aa..a99d4d4356b0 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -81,7 +81,7 @@ class WebContents : public mate::TrackableObject, void GoForward(); void GoToOffset(int offset); bool IsCrashed() const; - void SetUserAgent(const std::string& user_agent); + void SetUserAgent(const std::string& user_agent, mate::Arguments* args); std::string GetUserAgent(); void InsertCSS(const std::string& css); bool SavePage(const base::FilePath& full_file_path, diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index b9ae67b684a6..7b682543a09c 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -88,12 +88,15 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, AtomBrowserContext::~AtomBrowserContext() { } +void AtomBrowserContext::SetUserAgent(const std::string& user_agent) { + user_agent_ = user_agent; +} + net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() { return network_delegate_; } std::string AtomBrowserContext::GetUserAgent() { - LOG(ERROR) << "GetUserAgent"; return user_agent_; } diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index dbbae38272d7..f793bc9870d6 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -22,6 +22,8 @@ class AtomBrowserContext : public brightray::BrowserContext { AtomBrowserContext(const std::string& partition, bool in_memory); ~AtomBrowserContext() override; + void SetUserAgent(const std::string& user_agent); + // brightray::URLRequestContextGetter::Delegate: net::NetworkDelegate* CreateNetworkDelegate() override; std::string GetUserAgent() override; diff --git a/vendor/brightray b/vendor/brightray index e26f8073df2b..8244628f0c1d 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit e26f8073df2b20ba2169ffb082c5f135d542313b +Subproject commit 8244628f0c1d0eb15c659d42e882fb5d447c77ba From 6b1df5838cc5a372fd7d5a817c29cffb069bc446 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 16:05:38 +0900 Subject: [PATCH 049/192] docs: ses.setUserAgent(userAgent[, acceptLanguages]) --- docs/api/session.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/api/session.md b/docs/api/session.md index 0486b1ddbdd6..1b23ff5865d9 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -341,6 +341,23 @@ session.defaultSession.allowNTLMCredentialsForDomains('*example.com, *foobar.com session.defaultSession.allowNTLMCredentialsForDomains('*') ``` +#### `ses.setUserAgent(userAgent[, acceptLanguages])` + +* `userAgent` String +* `acceptLanguages` String (optional) + +Overrides the `userAgent` and `acceptLanguages` for this session. + +The `acceptLanguages` must a comma separated ordered list of language codes, for +example `"en-US,fr,de,ko,zh-CN,ja"`. + +This doesn't affect existing `WebContents`, and each `WebContents` can use +`webContents.setUserAgent` to override the session-wide user agent. + +#### `ses.getUserAgent()` + +Returns a `String` representing the user agent for this session. + #### `ses.webRequest` The `webRequest` API set allows to intercept and modify contents of a request at From 6f9c4766fcd12b0b834f476a5c323dbbf74ea079 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 16:10:36 +0900 Subject: [PATCH 050/192] Fix usages of global BrowserContext in App::ImportCertificate --- atom/browser/api/atom_api_app.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index bc29b988eeea..265ed8f36b7d 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -463,10 +463,11 @@ void App::DisableHardwareAcceleration(mate::Arguments* args) { void App::ImportCertificate( const base::DictionaryValue& options, const net::CompletionCallback& callback) { - auto browser_context = AtomBrowserMainParts::Get()->browser_context(); + auto browser_context = brightray::BrowserContext::From("", false); if (!certificate_manager_model_) { std::unique_ptr copy = options.CreateDeepCopy(); - CertificateManagerModel::Create(browser_context, + CertificateManagerModel::Create( + browser_context.get(), base::Bind(&App::OnCertificateManagerModelCreated, base::Unretained(this), base::Passed(©), From 5a702e01533190b9f7b8784c4ce3ccc6f5a86ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arif=20=C3=87ak=C4=B1ro=C4=9Flu?= Date: Wed, 22 Jun 2016 10:11:13 +0300 Subject: [PATCH 051/192] Update README.md add `electron-tr` community --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83ba1f5a618f..61e17e0c8813 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,6 @@ forums - [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)* - [`electron-kr`](http://www.meetup.com/electron-kr/) *(Korean)* - [`electron-jp`](https://electron-jp-slackin.herokuapp.com/) *(Japanese)* - +- [`electron-tr`](http://www.meetup.com/Electron-JS-Istanbul/) *(Turkish)* Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) for a community maintained list of useful example apps, tools and resources. From 1a74177e2d0bb1d86d2d2ac2b2399f88b638445c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 22 Jun 2016 16:34:25 +0900 Subject: [PATCH 052/192] docs: Separate Cookies and WebRequest classes in session.md --- docs/api/session.md | 283 ++++++++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 129 deletions(-) diff --git a/docs/api/session.md b/docs/api/session.md index 1b23ff5865d9..b6026c32ddda 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -5,16 +5,15 @@ The `session` module can be used to create new `Session` objects. You can also access the `session` of existing pages by using the `session` -property of [`webContents`](web-contents.md) which is a property of -[`BrowserWindow`](browser-window.md). +property of [`WebContents`](web-contents.md), or from the `session` module. ```javascript -const {BrowserWindow} = require('electron'); +const {session, BrowserWindow} = require('electron') -let win = new BrowserWindow({width: 800, height: 600}); -win.loadURL('http://github.com'); +let win = new BrowserWindow({width: 800, height: 600}) +win.loadURL('http://github.com') -const ses = win.webContents.session; +const ses = win.webContents.session ``` ## Methods @@ -78,91 +77,6 @@ session.defaultSession.on('will-download', (event, item, webContents) => { The following methods are available on instances of `Session`: -#### `ses.cookies` - -The `cookies` gives you ability to query and modify cookies. For example: - -```javascript -// Query all cookies. -session.defaultSession.cookies.get({}, (error, cookies) => { - console.log(cookies); -}); - -// Query all cookies associated with a specific url. -session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => { - console.log(cookies); -}); - -// Set a cookie with the given cookie data; -// may overwrite equivalent cookies if they exist. -const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'}; -session.defaultSession.cookies.set(cookie, (error) => { - if (error) - console.error(error); -}); -``` - -#### `ses.cookies.get(filter, callback)` - -* `filter` Object - * `url` String (optional) - Retrieves cookies which are associated with - `url`. Empty implies retrieving cookies of all urls. - * `name` String (optional) - Filters cookies by name. - * `domain` String (optional) - Retrieves cookies whose domains match or are - subdomains of `domains` - * `path` String (optional) - Retrieves cookies whose path matches `path`. - * `secure` Boolean (optional) - Filters cookies by their Secure property. - * `session` Boolean (optional) - Filters out session or persistent cookies. -* `callback` Function - -Sends a request to get all cookies matching `details`, `callback` will be called -with `callback(error, cookies)` on complete. - -`cookies` is an Array of `cookie` objects. - -* `cookie` Object - * `name` String - The name of the cookie. - * `value` String - The value of the cookie. - * `domain` String - The domain of the cookie. - * `hostOnly` String - Whether the cookie is a host-only cookie. - * `path` String - The path of the cookie. - * `secure` Boolean - Whether the cookie is marked as secure. - * `httpOnly` Boolean - Whether the cookie is marked as HTTP only. - * `session` Boolean - Whether the cookie is a session cookie or a persistent - cookie with an expiration date. - * `expirationDate` Double (optional) - The expiration date of the cookie as - the number of seconds since the UNIX epoch. Not provided for session - cookies. - -#### `ses.cookies.set(details, callback)` - -* `details` Object - * `url` String - The url to associate the cookie with. - * `name` String - The name of the cookie. Empty by default if omitted. - * `value` String - The value of the cookie. Empty by default if omitted. - * `domain` String - The domain of the cookie. Empty by default if omitted. - * `path` String - The path of the cookie. Empty by default if omitted. - * `secure` Boolean - Whether the cookie should be marked as Secure. Defaults to - false. - * `session` Boolean - Whether the cookie should be marked as HTTP only. Defaults - to false. - * `expirationDate` Double - The expiration date of the cookie as the number of - seconds since the UNIX epoch. If omitted then the cookie becomes a session - cookie and will not be retained between sessions. -* `callback` Function - -Sets a cookie with `details`, `callback` will be called with `callback(error)` -on complete. - -#### `ses.cookies.remove(url, name, callback)` - -* `url` String - The URL associated with the cookie. -* `name` String - The name of cookie to remove. -* `callback` Function - -Removes the cookies matching `url` and `name`, `callback` will called with -`callback()` on complete. - #### `ses.getCacheSize(callback)` * `callback` Function @@ -358,15 +272,140 @@ This doesn't affect existing `WebContents`, and each `WebContents` can use Returns a `String` representing the user agent for this session. +### Instance Properties + +The following properties are available on instances of `Session`: + +#### `ses.cookies` + +Returns an instance of `Cookies` class for this session. + #### `ses.webRequest` -The `webRequest` API set allows to intercept and modify contents of a request at -various stages of its lifetime. +Returns an instance of `WebRequest` class for this session. -Each API accepts an optional `filter` and a `listener`, the `listener` will be -called with `listener(details)` when the API's event has happened, the `details` -is an object that describes the request. Passing `null` as `listener` will -unsubscribe from the event. +#### `ses.protocol` + +Returns an instance of [protocol](protocol.md) module for this session. + +```javascript +const {app, session} = require('electron') +const path = require('path') + +app.on('ready', function () { + const protocol = session.fromPartition(partitionName).protocol + protocol.registerFileProtocol('atom', function (request, callback) { + var url = request.url.substr(7) + callback({path: path.normalize(__dirname + '/' + url)}) + }, function (error) { + if (error) + console.error('Failed to register protocol') + }) +}) +``` + +## Class: Cookies + +The `Cookies` class gives you ability to query and modify cookies. Instances of +`Cookies` class must be received by using `cookies` property of `Session` class. + +For example: + +```javascript +// Query all cookies. +session.defaultSession.cookies.get({}, (error, cookies) => { + console.log(cookies) +}) + +// Query all cookies associated with a specific url. +session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => { + console.log(cookies) +}) + +// Set a cookie with the given cookie data; +// may overwrite equivalent cookies if they exist. +const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'} +session.defaultSession.cookies.set(cookie, (error) => { + if (error) + console.error(error) +}) +``` + +### Instance Methods + +The following methods are available on instances of `Cookies`: + +#### `cookies.get(filter, callback)` + +* `filter` Object + * `url` String (optional) - Retrieves cookies which are associated with + `url`. Empty implies retrieving cookies of all urls. + * `name` String (optional) - Filters cookies by name. + * `domain` String (optional) - Retrieves cookies whose domains match or are + subdomains of `domains` + * `path` String (optional) - Retrieves cookies whose path matches `path`. + * `secure` Boolean (optional) - Filters cookies by their Secure property. + * `session` Boolean (optional) - Filters out session or persistent cookies. +* `callback` Function + +Sends a request to get all cookies matching `details`, `callback` will be called +with `callback(error, cookies)` on complete. + +`cookies` is an Array of `cookie` objects. + +* `cookie` Object + * `name` String - The name of the cookie. + * `value` String - The value of the cookie. + * `domain` String - The domain of the cookie. + * `hostOnly` String - Whether the cookie is a host-only cookie. + * `path` String - The path of the cookie. + * `secure` Boolean - Whether the cookie is marked as secure. + * `httpOnly` Boolean - Whether the cookie is marked as HTTP only. + * `session` Boolean - Whether the cookie is a session cookie or a persistent + cookie with an expiration date. + * `expirationDate` Double (optional) - The expiration date of the cookie as + the number of seconds since the UNIX epoch. Not provided for session + cookies. + +#### `cookies.set(details, callback)` + +* `details` Object + * `url` String - The url to associate the cookie with. + * `name` String - The name of the cookie. Empty by default if omitted. + * `value` String - The value of the cookie. Empty by default if omitted. + * `domain` String - The domain of the cookie. Empty by default if omitted. + * `path` String - The path of the cookie. Empty by default if omitted. + * `secure` Boolean - Whether the cookie should be marked as Secure. Defaults to + false. + * `session` Boolean - Whether the cookie should be marked as HTTP only. Defaults + to false. + * `expirationDate` Double - The expiration date of the cookie as the number of + seconds since the UNIX epoch. If omitted then the cookie becomes a session + cookie and will not be retained between sessions. +* `callback` Function + +Sets a cookie with `details`, `callback` will be called with `callback(error)` +on complete. + +#### `cookies.remove(url, name, callback)` + +* `url` String - The URL associated with the cookie. +* `name` String - The name of cookie to remove. +* `callback` Function + +Removes the cookies matching `url` and `name`, `callback` will called with +`callback()` on complete. + +## Class: WebRequest + +The `WebRequest` class allows to intercept and modify contents of a request at +various stages of its lifetime. Instances of `WebRequest` class must be received +by using `webRequest` property of `Session` class. + +The methods of `WebRequest` accept an optional `filter` and a `listener`, the +`listener` will be called with `listener(details)` when the API's event has +happened, the `details` is an object that describes the request. Passing `null` +as `listener` will unsubscribe from the event. The `filter` is an object that has an `urls` property, which is an Array of URL patterns that will be used to filter out the requests that do not match the URL @@ -375,19 +414,25 @@ patterns. If the `filter` is omitted then all requests will be matched. For certain events the `listener` is passed with a `callback`, which should be called with an `response` object when `listener` has done its work. +An example of adding `User-Agent` header for requests: + ```javascript // Modify the user agent for all requests to the following urls. const filter = { urls: ['https://*.github.com/*', '*://electron.github.io'] -}; +} session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => { - details.requestHeaders['User-Agent'] = "MyAgent"; - callback({cancel: false, requestHeaders: details.requestHeaders}); -}); + details.requestHeaders['User-Agent'] = "MyAgent" + callback({cancel: false, requestHeaders: details.requestHeaders}) +}) ``` -#### `ses.webRequest.onBeforeRequest([filter, ]listener)` +### Instance Methods + +The following methods are available on instances of `WebRequest`: + +#### `webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -417,7 +462,7 @@ The `callback` has to be called with an `response` object: * `redirectURL` String (optional) - The original request is prevented from being sent or completed, and is instead redirected to the given URL. -#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +#### `webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -442,7 +487,7 @@ The `callback` has to be called with an `response` object: * `requestHeaders` Object (optional) - When provided, request will be made with these headers. -#### `ses.webRequest.onSendHeaders([filter, ]listener)` +#### `webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -459,7 +504,7 @@ response are visible by the time this listener is fired. * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter,]listener)` +#### `webRequest.onHeadersReceived([filter,]listener)` * `filter` Object * `listener` Function @@ -488,7 +533,7 @@ The `callback` has to be called with an `response` object: `responseHeaders` to change header status otherwise original response header's status will be used. -#### `ses.webRequest.onResponseStarted([filter, ]listener)` +#### `webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -509,7 +554,7 @@ and response headers are available. * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +#### `webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -530,7 +575,7 @@ redirect is about to occur. * `fromCache` Boolean * `responseHeaders` Object -#### `ses.webRequest.onCompleted([filter, ]listener)` +#### `webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -549,7 +594,7 @@ completed. * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onErrorOccurred([filter, ]listener)` +#### `webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function @@ -564,23 +609,3 @@ The `listener` will be called with `listener(details)` when an error occurs. * `timestamp` Double * `fromCache` Boolean * `error` String - The error description. - -#### `ses.protocol` - -Returns an instance of [protocol](protocol.md) module for this session. - -```javascript -const {app, session} = require('electron') -const path = require('path') - -app.on('ready', function () { - const protocol = session.fromPartition(partitionName).protocol - protocol.registerFileProtocol('atom', function (request, callback) { - var url = request.url.substr(7) - callback({path: path.normalize(__dirname + '/' + url)}) - }, function (error) { - if (error) - console.error('Failed to register protocol') - }) -}) -``` From 7e7c085dc7449d8ed2527f4e2ddb50bc627c0748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arif=20=C3=87ak=C4=B1ro=C4=9Flu?= Date: Wed, 22 Jun 2016 11:02:14 +0300 Subject: [PATCH 053/192] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 61e17e0c8813..cf8d97a56098 100644 --- a/README.md +++ b/README.md @@ -73,5 +73,6 @@ forums - [`electron-kr`](http://www.meetup.com/electron-kr/) *(Korean)* - [`electron-jp`](https://electron-jp-slackin.herokuapp.com/) *(Japanese)* - [`electron-tr`](http://www.meetup.com/Electron-JS-Istanbul/) *(Turkish)* + Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) for a community maintained list of useful example apps, tools and resources. From c243cf0c0ff641f930b5dfb7918e5c3f20b24ec1 Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Wed, 22 Jun 2016 10:40:01 +0200 Subject: [PATCH 054/192] Add BrowserWindow.prototype.setContentProtection(enable) --- atom/browser/api/atom_api_window.cc | 5 +++++ atom/browser/api/atom_api_window.h | 1 + atom/browser/native_window.h | 1 + atom/browser/native_window_mac.h | 1 + atom/browser/native_window_mac.mm | 5 +++++ atom/browser/native_window_views.cc | 7 +++++++ atom/browser/native_window_views.h | 1 + docs/api/browser-window.md | 7 +++++++ 8 files changed, 28 insertions(+) diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index cc3749bd0143..ae6bb77cf9bc 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -572,6 +572,10 @@ void Window::SetIgnoreMouseEvents(bool ignore) { return window_->SetIgnoreMouseEvents(ignore); } +void Window::SetContentProtection(bool enable) { + return window_->SetContentProtection(enable); +} + void Window::SetFocusable(bool focusable) { return window_->SetFocusable(focusable); } @@ -833,6 +837,7 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("setDocumentEdited", &Window::SetDocumentEdited) .SetMethod("isDocumentEdited", &Window::IsDocumentEdited) .SetMethod("setIgnoreMouseEvents", &Window::SetIgnoreMouseEvents) + .SetMethod("setContentProtection", &Window::SetContentProtection) .SetMethod("setFocusable", &Window::SetFocusable) .SetMethod("focusOnWebView", &Window::FocusOnWebView) .SetMethod("blurWebView", &Window::BlurWebView) diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index 21ecca4c7292..a204b70e296c 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -153,6 +153,7 @@ class Window : public mate::TrackableObject, void SetDocumentEdited(bool edited); bool IsDocumentEdited(); void SetIgnoreMouseEvents(bool ignore); + void SetContentProtection(bool enable); void SetFocusable(bool focusable); void CapturePage(mate::Arguments* args); void SetProgressBar(double progress); diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index d3b6f8a2b0be..8656c02ca939 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -157,6 +157,7 @@ class NativeWindow : public base::SupportsUserData, virtual void SetDocumentEdited(bool edited); virtual bool IsDocumentEdited(); virtual void SetIgnoreMouseEvents(bool ignore) = 0; + virtual void SetContentProtection(bool enable) = 0; virtual void SetFocusable(bool focusable); virtual void SetMenu(ui::MenuModel* menu); virtual bool HasModalDialog(); diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index af99b3912e16..d07d586a8439 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -79,6 +79,7 @@ class NativeWindowMac : public NativeWindow { void SetDocumentEdited(bool edited) override; bool IsDocumentEdited() override; void SetIgnoreMouseEvents(bool ignore) override; + void SetContentProtection(bool enable) override; bool HasModalDialog() override; void SetParentWindow(NativeWindow* parent) override; gfx::NativeWindow GetNativeWindow() override; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 3330eb8e9f3e..004d2cad23de 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -941,6 +941,11 @@ void NativeWindowMac::SetIgnoreMouseEvents(bool ignore) { [window_ setIgnoresMouseEvents:ignore]; } +void NativeWindowMac::SetContentProtection(bool enable) { + [window_ setSharingType:enable ? NSWindowSharingNone + : NSWindowSharingReadOnly]; +} + bool NativeWindowMac::HasModalDialog() { return [window_ attachedSheet] != nil; } diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 1feb9618ac66..e9ff6952816f 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -728,6 +728,13 @@ void NativeWindowViews::SetIgnoreMouseEvents(bool ignore) { #endif } +void NativeWindowViews::SetContentProtection(bool enable) { +#if defined(OS_WIN) + DWORD affinity = enable ? WDA_MONITOR : WDA_NONE; + ::SetWindowDisplayAffinity(GetAcceleratedWidget(), affinity); +#endif +} + void NativeWindowViews::SetFocusable(bool focusable) { #if defined(OS_WIN) LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index 69d3d27a35da..71f3741f2fad 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -96,6 +96,7 @@ class NativeWindowViews : public NativeWindow, void SetHasShadow(bool has_shadow) override; bool HasShadow() override; void SetIgnoreMouseEvents(bool ignore) override; + void SetContentProtection(bool enable) override; void SetFocusable(bool focusable) override; void SetMenu(ui::MenuModel* menu_model) override; void SetParentWindow(NativeWindow* parent) override; diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index e95570450b62..e5a9732e2269 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -1050,6 +1050,13 @@ All mouse events happened in this window will be passed to the window below this window, but if this window has focus, it will still receive keyboard events. +### `win.setContentProtection(enable)` _macOS_ _Windows_ + +Prevents the window contents from being captured by other apps. + +On macOS it sets the NSWindow's sharingType to NSWindowSharingNone. +On Windows it calls SetWindowDisplayAffinity with WDA_MONITOR. + ### `win.setFocusable(focusable)` _Windows_ * `focusable` Boolean From ca57f8a3913eeb9f11af850ef6fb43e8999869d0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 15:41:37 -0700 Subject: [PATCH 055/192] Remove CoffeeScript class wrapper --- lib/browser/api/menu-item.js | 159 +++++++++++++++++------------------ 1 file changed, 78 insertions(+), 81 deletions(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index b0100b7f2ff7..25d52f8fbdb1 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -31,97 +31,94 @@ const methodInApp = { quit: true } -const MenuItem = (function () { - MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] +const MenuItem = function (options) { + const {app, Menu} = require('electron') - function MenuItem (options) { - const {app, Menu} = require('electron') + const click = options.click + this.selector = options.selector + this.type = options.type + this.role = options.role + this.label = options.label + this.sublabel = options.sublabel + this.accelerator = options.accelerator + this.icon = options.icon + this.enabled = options.enabled + this.visible = options.visible + this.checked = options.checked - const click = options.click - this.selector = options.selector - this.type = options.type - this.role = options.role - this.label = options.label - this.sublabel = options.sublabel - this.accelerator = options.accelerator - this.icon = options.icon - this.enabled = options.enabled - this.visible = options.visible - this.checked = options.checked - this.submenu = options.submenu - if ((this.submenu != null) && this.submenu.constructor !== Menu) { - this.submenu = Menu.buildFromTemplate(this.submenu) + this.submenu = options.submenu + if (this.submenu != null && this.submenu.constructor !== Menu) { + this.submenu = Menu.buildFromTemplate(this.submenu) + } + if (this.type == null && this.submenu != null) { + this.type = 'submenu' + } + if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) { + throw new Error('Invalid submenu') + } + + this.overrideReadOnlyProperty('type', 'normal') + this.overrideReadOnlyProperty('role') + this.overrideReadOnlyProperty('accelerator') + this.overrideReadOnlyProperty('icon') + this.overrideReadOnlyProperty('submenu') + + this.overrideProperty('label', '') + this.overrideProperty('sublabel', '') + this.overrideProperty('enabled', true) + this.overrideProperty('visible', true) + this.overrideProperty('checked', false) + + if (!MenuItem.types.includes(this.type)) { + throw new Error(`Unknown menu type ${this.type}`) + } + + this.commandId = ++nextCommandId + + this.click = (focusedWindow) => { + // Manually flip the checked flags when clicked. + if (this.type === 'checkbox' || this.type === 'radio') { + this.checked = !this.checked } - if ((this.type == null) && (this.submenu != null)) { - this.type = 'submenu' - } - if (this.type === 'submenu' && (this.submenu != null ? this.submenu.constructor : void 0) !== Menu) { - throw new Error('Invalid submenu') - } - this.overrideReadOnlyProperty('type', 'normal') - this.overrideReadOnlyProperty('role') - this.overrideReadOnlyProperty('accelerator') - this.overrideReadOnlyProperty('icon') - this.overrideReadOnlyProperty('submenu') - this.overrideProperty('label', '') - this.overrideProperty('sublabel', '') - this.overrideProperty('enabled', true) - this.overrideProperty('visible', true) - this.overrideProperty('checked', false) - if (MenuItem.types.indexOf(this.type) === -1) { - throw new Error('Unknown menu type ' + this.type) - } - this.commandId = ++nextCommandId - this.click = (event, focusedWindow) => { - // Manually flip the checked flags when clicked. - if (this.type === 'checkbox' || this.type === 'radio') { - this.checked = !this.checked - } - - if (this.role && rolesMap[this.role] && process.platform !== 'darwin' && (focusedWindow != null)) { - const methodName = rolesMap[this.role] - if (methodInApp[methodName]) { - return app[methodName]() - } else if (typeof methodInBrowserWindow[methodName] === 'function') { - return methodInBrowserWindow[methodName](focusedWindow) - } else if (methodInBrowserWindow[methodName]) { - return focusedWindow[methodName]() - } else { - const {webContents} = focusedWindow - return webContents != null ? webContents[methodName]() : void 0 - } - } else if (typeof click === 'function') { - return click(this, focusedWindow, event) - } else if (typeof this.selector === 'string' && process.platform === 'darwin') { - return Menu.sendActionToFirstResponder(this.selector) + + if (this.role && rolesMap[this.role] && process.platform !== 'darwin' && focusedWindow != null) { + const methodName = rolesMap[this.role] + if (methodInApp[methodName]) { + return app[methodName]() + } else if (typeof methodInBrowserWindow[methodName] === 'function') { + return methodInBrowserWindow[methodName](focusedWindow) + } else if (methodInBrowserWindow[methodName]) { + return focusedWindow[methodName]() + } else { + const {webContents} = focusedWindow + return webContents != null ? webContents[methodName]() : void 0 } + } else if (typeof click === 'function') { + return click(this, focusedWindow) + } else if (typeof this.selector === 'string' && process.platform === 'darwin') { + return Menu.sendActionToFirstResponder(this.selector) } } +} - MenuItem.prototype.overrideProperty = function (name, defaultValue) { - if (defaultValue == null) { - defaultValue = null - } - this[name] != null ? this[name] : this[name] = defaultValue +MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] - return this[name] +MenuItem.prototype.overrideProperty = function (name, defaultValue) { + if (defaultValue == null) { + defaultValue = null } - - MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) { - if (defaultValue == null) { - defaultValue = null - } - if (this[name] == null) { - this[name] = defaultValue - } - return Object.defineProperty(this, name, { - enumerable: true, - writable: false, - value: this[name] - }) + if (this[name] == null) { + this[name] = defaultValue } +} - return MenuItem -})() +MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) { + this.overrideProperty(name, defaultValue) + Object.defineProperty(this, name, { + enumerable: true, + writable: false, + value: this[name] + }) +} module.exports = MenuItem From 93cbe6539f601f8cc643cd84a7cbeb44ebe6e772 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 15:59:02 -0700 Subject: [PATCH 056/192] Add spec for invalid menu item type --- lib/browser/api/menu-item.js | 2 +- spec/api-menu-spec.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 25d52f8fbdb1..b8057922cb42 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -70,7 +70,7 @@ const MenuItem = function (options) { this.overrideProperty('checked', false) if (!MenuItem.types.includes(this.type)) { - throw new Error(`Unknown menu type ${this.type}`) + throw new Error(`Unknown menu item type: ${this.type}`) } this.commandId = ++nextCommandId diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index a8cc5b518cba..a9cf51189325 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -359,4 +359,17 @@ describe('menu module', function () { } }) }) + + describe('MenuItem with invalid type', function () { + it('throws an exception', function () { + assert.throws(function () { + var menu = Menu.buildFromTemplate([ + { + label: 'text', + type: 'not-a-type' + } + ]) + }, /Unknown menu item type: not-a-type/) + }) + }) }) From 51b8c0292c49c22585a6a9d2f087cdb0901fc87b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 16:02:15 -0700 Subject: [PATCH 057/192] Add spec for submenu type with no submenu --- spec/api-menu-spec.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index a9cf51189325..db897cafdc65 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -372,4 +372,17 @@ describe('menu module', function () { }, /Unknown menu item type: not-a-type/) }) }) + + describe('MenuItem with submenu type and missing submenu', function () { + it('throws an exception', function () { + assert.throws(function () { + var menu = Menu.buildFromTemplate([ + { + label: 'text', + type: 'submenu' + } + ]) + }, /Invalid submenu/) + }) + }) }) From 38e1abc5990c82d5a5bebf88d88d4be34733289b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 16:03:41 -0700 Subject: [PATCH 058/192] :art: Destructure requires --- spec/api-menu-spec.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index db897cafdc65..34ce250778fa 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -1,10 +1,7 @@ const assert = require('assert') -const remote = require('electron').remote -const ipcRenderer = require('electron').ipcRenderer - -const Menu = remote.require('electron').Menu -const MenuItem = remote.require('electron').MenuItem +const {ipcRenderer, remote} = require('electron') +const {Menu, MenuItem} = remote describe('menu module', function () { describe('Menu.buildFromTemplate', function () { From 4e8d4dfda9148767467597da0013671571342a15 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 21 Jun 2016 16:07:20 -0700 Subject: [PATCH 059/192] Move click variable near this.click --- lib/browser/api/menu-item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index b8057922cb42..b895cbebeac2 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -34,7 +34,6 @@ const methodInApp = { const MenuItem = function (options) { const {app, Menu} = require('electron') - const click = options.click this.selector = options.selector this.type = options.type this.role = options.role @@ -75,6 +74,7 @@ const MenuItem = function (options) { this.commandId = ++nextCommandId + const click = options.click this.click = (focusedWindow) => { // Manually flip the checked flags when clicked. if (this.type === 'checkbox' || this.type === 'radio') { From 50a62429e419f4e8083fd5f07f9b6f5c383156e0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 09:35:11 -0700 Subject: [PATCH 060/192] Add back event param --- lib/browser/api/menu-item.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index b895cbebeac2..e1ccda488611 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -75,7 +75,7 @@ const MenuItem = function (options) { this.commandId = ++nextCommandId const click = options.click - this.click = (focusedWindow) => { + this.click = (event, focusedWindow) => { // Manually flip the checked flags when clicked. if (this.type === 'checkbox' || this.type === 'radio') { this.checked = !this.checked @@ -94,7 +94,7 @@ const MenuItem = function (options) { return webContents != null ? webContents[methodName]() : void 0 } } else if (typeof click === 'function') { - return click(this, focusedWindow) + return click(this, focusedWindow, event) } else if (typeof this.selector === 'string' && process.platform === 'darwin') { return Menu.sendActionToFirstResponder(this.selector) } From bd744125018f1c62b63c30ab76a60fc405dd7e60 Mon Sep 17 00:00:00 2001 From: Josh Abernathy Date: Wed, 22 Jun 2016 13:00:31 -0400 Subject: [PATCH 061/192] Update external frameworks version Bump to [1.1.0](https://github.com/electron/electron-frameworks/releases/tag/v1.1.0) which upgraded Squirrel.Mac. --- script/update-external-binaries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/update-external-binaries.py b/script/update-external-binaries.py index 470f735fdb42..4494df252629 100755 --- a/script/update-external-binaries.py +++ b/script/update-external-binaries.py @@ -8,7 +8,7 @@ from lib.config import get_target_arch from lib.util import safe_mkdir, rm_rf, extract_zip, tempdir, download -VERSION = 'v1.0.0' +VERSION = 'v1.1.0' SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) FRAMEWORKS_URL = 'http://github.com/electron/electron-frameworks/releases' \ '/download/' + VERSION From 90600ba653d2d17bfd739cc49403a3e322caa61a Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 10:06:54 -0700 Subject: [PATCH 062/192] Add failing spec for writeable command id --- spec/api-menu-spec.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index 34ce250778fa..d6ac56475f66 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -357,10 +357,23 @@ describe('menu module', function () { }) }) + describe('MenuItem command id', function () { + it('cannot be overwritten', function () { + var item = new MenuItem({ + label: 'item' + }) + + var commandId = item.commandId + assert(commandId != null) + item.commandId = '' + commandId + '-modified' + assert.equal(item.commandId, commandId) + }) + }) + describe('MenuItem with invalid type', function () { it('throws an exception', function () { assert.throws(function () { - var menu = Menu.buildFromTemplate([ + Menu.buildFromTemplate([ { label: 'text', type: 'not-a-type' @@ -373,7 +386,7 @@ describe('menu module', function () { describe('MenuItem with submenu type and missing submenu', function () { it('throws an exception', function () { assert.throws(function () { - var menu = Menu.buildFromTemplate([ + Menu.buildFromTemplate([ { label: 'text', type: 'submenu' From ad110fc806a94e16a5ead9288b5d041bc3e997b5 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 10:07:02 -0700 Subject: [PATCH 063/192] Make commandId a read only property --- lib/browser/api/menu-item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index e1ccda488611..264be8179084 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -72,7 +72,7 @@ const MenuItem = function (options) { throw new Error(`Unknown menu item type: ${this.type}`) } - this.commandId = ++nextCommandId + this.overrideReadOnlyProperty('commandId', ++nextCommandId) const click = options.click this.click = (event, focusedWindow) => { From 25b2724ab94e775229fe40e32d9409f0f5dd10c4 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 10:29:49 -0700 Subject: [PATCH 064/192] Add menu item role defaults --- filenames.gypi | 1 + lib/browser/api/menu-item-roles.js | 65 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 lib/browser/api/menu-item-roles.js diff --git a/filenames.gypi b/filenames.gypi index af7080821b4d..6b086e1f8b98 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -21,6 +21,7 @@ 'lib/browser/api/ipc-main.js', 'lib/browser/api/menu.js', 'lib/browser/api/menu-item.js', + 'lib/browser/api/menu-item-roles.js', 'lib/browser/api/navigation-controller.js', 'lib/browser/api/power-monitor.js', 'lib/browser/api/power-save-blocker.js', diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js new file mode 100644 index 000000000000..13484e5da9a5 --- /dev/null +++ b/lib/browser/api/menu-item-roles.js @@ -0,0 +1,65 @@ +module.exports = { + undo: { + label: 'Undo', + accelerator: 'CmdOrCtrl+Z', + method: 'undo' + }, + redo: { + label: 'Redo', + accelerator: 'Shift+CmdOrCtrl+Z', + method: 'redo' + }, + cut: { + label: 'Cut', + accelerator: 'CmdOrCtrl+X', + method: 'cut' + }, + copy: { + label: 'Copy', + accelerator: 'CmdOrCtrl+C', + method: 'copy' + }, + paste: { + label: 'Paste', + accelerator: 'CmdOrCtrl+V', + method: 'paste' + }, + pasteandmatchstyle: { + label: 'Paste and Match Style', + accelerator: 'Shift+Command+V', + method: 'pasteAndMatchStyle' + }, + selectall: { + label: 'Select All', + accelerator: 'CmdOrCtrl+A', + method: 'selectAll' + }, + minimize: { + label: 'Minimize', + accelerator: 'CmdOrCtrl+M', + method: 'minimize' + }, + close: { + label: 'Close', + accelerator: 'CmdOrCtrl+W', + method: 'close' + }, + delete: { + label: 'Delete', + accelerator: 'Delete', + method: 'delete' + }, + quit: { + get label () { + const {app} = require('electron') + return process.platform === 'win32' ? 'Exit' : `Quit ${app.getName()}` + }, + accelerator: process.platform === 'win32' ? null : 'Command+Q', + method: 'quit' + }, + togglefullscreen: { + label: 'Toggle Full Screen', + accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', + method: 'toggleFullScreen' + } +} From 13a6d32ee9ab48fdb5d81d6e5c5a2d106a9e7c20 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 10:45:01 -0700 Subject: [PATCH 065/192] Add default label/accelerator to role menu items --- lib/browser/api/menu-item-roles.js | 14 +++++++++++++- lib/browser/api/menu-item.js | 6 ++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index 13484e5da9a5..f26e9c5a5a91 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -1,4 +1,4 @@ -module.exports = { +const roles = { undo: { label: 'Undo', accelerator: 'CmdOrCtrl+Z', @@ -63,3 +63,15 @@ module.exports = { method: 'toggleFullScreen' } } + +exports.getDefaultLabel = function (role) { + if (roles.hasOwnProperty(role)) { + return roles[role].label + } else { + return '' + } +} + +exports.getDefaultAccelerator = function (role) { + if (roles.hasOwnProperty(role)) return roles[role].accelerator +} diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 264be8179084..ba980960b559 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -1,5 +1,7 @@ 'use strict' +const roles = require('./menu-item-roles') + let nextCommandId = 0 // Maps role to methods of webContents @@ -58,11 +60,11 @@ const MenuItem = function (options) { this.overrideReadOnlyProperty('type', 'normal') this.overrideReadOnlyProperty('role') - this.overrideReadOnlyProperty('accelerator') + this.overrideReadOnlyProperty('accelerator', roles.getDefaultAccelerator(this.role)) this.overrideReadOnlyProperty('icon') this.overrideReadOnlyProperty('submenu') - this.overrideProperty('label', '') + this.overrideProperty('label', roles.getDefaultLabel(this.role)) this.overrideProperty('sublabel', '') this.overrideProperty('enabled', true) this.overrideProperty('visible', true) From 566a407b363934ee003be09151a4bf9b3a5e8a89 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 10:46:17 -0700 Subject: [PATCH 066/192] Use default labels and accelerators --- default_app/main.js | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/default_app/main.js b/default_app/main.js index 8b2fb1bbf39d..085708cb44d7 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -52,46 +52,30 @@ app.once('ready', () => { label: 'Edit', submenu: [ { - label: 'Undo', - accelerator: 'CmdOrCtrl+Z', role: 'undo' }, { - label: 'Redo', - accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, { type: 'separator' }, { - label: 'Cut', - accelerator: 'CmdOrCtrl+X', role: 'cut' }, { - label: 'Copy', - accelerator: 'CmdOrCtrl+C', role: 'copy' }, { - label: 'Paste', - accelerator: 'CmdOrCtrl+V', role: 'paste' }, { - label: 'Paste and Match Style', - accelerator: 'Shift+Command+V', role: 'pasteandmatchstyle' }, { - label: 'Delete', - accelerator: 'Delete', role: 'delete' }, { - label: 'Select All', - accelerator: 'CmdOrCtrl+A', role: 'selectall' } ] @@ -107,9 +91,7 @@ app.once('ready', () => { } }, { - label: 'Toggle Full Screen', role: 'togglefullscreen', - accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11' }, { label: 'Toggle Developer Tools', @@ -125,13 +107,9 @@ app.once('ready', () => { role: 'window', submenu: [ { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { - label: 'Close', - accelerator: 'CmdOrCtrl+W', role: 'close' } ] From 888068b59717fe1e6ae14c178b216a30cd61cce3 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 10:47:25 -0700 Subject: [PATCH 067/192] Add default help/window labels --- default_app/main.js | 2 -- lib/browser/api/menu-item-roles.js | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/default_app/main.js b/default_app/main.js index 085708cb44d7..45ff78e1b96c 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -103,7 +103,6 @@ app.once('ready', () => { ] }, { - label: 'Window', role: 'window', submenu: [ { @@ -115,7 +114,6 @@ app.once('ready', () => { ] }, { - label: 'Help', role: 'help', submenu: [ { diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index f26e9c5a5a91..c78ff8aed810 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -61,6 +61,12 @@ const roles = { label: 'Toggle Full Screen', accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', method: 'toggleFullScreen' + }, + help: { + label: 'Help' + }, + window: { + label: 'Window' } } From c0562d16d58c2ee727da493c70c2730d15fefafb Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 13:09:39 -0700 Subject: [PATCH 068/192] Add more role defaults --- default_app/main.js | 16 ---------------- lib/browser/api/menu-item-roles.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/default_app/main.js b/default_app/main.js index 45ff78e1b96c..a484c030dbfc 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -151,14 +151,12 @@ app.once('ready', () => { label: 'Electron', submenu: [ { - label: 'About Electron', role: 'about' }, { type: 'separator' }, { - label: 'Services', role: 'services', submenu: [] }, @@ -166,49 +164,36 @@ app.once('ready', () => { type: 'separator' }, { - label: 'Hide Electron', - accelerator: 'Command+H', role: 'hide' }, { - label: 'Hide Others', - accelerator: 'Command+Alt+H', role: 'hideothers' }, { - label: 'Show All', role: 'unhide' }, { type: 'separator' }, { - label: 'Quit ' + app.getName(), - accelerator: 'Command+Q', role: 'quit' } ] }) template[3].submenu = [ { - label: 'Close', - accelerator: 'CmdOrCtrl+W', role: 'close' }, { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { - label: 'Zoom', role: 'zoom' }, { type: 'separator' }, { - label: 'Bring All to Front', role: 'front' } ] @@ -219,7 +204,6 @@ app.once('ready', () => { label: 'File', submenu: [ { - label: 'Exit', role: 'quit' } ] diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index c78ff8aed810..a5373c65aca2 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -67,6 +67,35 @@ const roles = { }, window: { label: 'Window' + }, + services: { + label: 'Services' + }, + zoom: { + label: 'Zoom' + }, + front: { + label: 'Bring All to Front' + }, + about: { + get label () { + const {app} = require('electron') + return `About ${app.getName()}` + } + }, + hide: { + get label () { + const {app} = require('electron') + return return `Hide ${app.getName()}` + }, + accelerator: 'Command+H' + }, + hideothers: { + label: 'Hide Others', + accelerator: 'Command+Alt+H' + }, + unhide: { + label: 'Show All' } } From 653370974a5029ce88bced81a553f155a7eebd2c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 13:15:20 -0700 Subject: [PATCH 069/192] :art: Sort roles alphabetically --- lib/browser/api/menu-item-roles.js | 120 ++++++++++++++--------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index a5373c65aca2..6d6b0371e87c 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -1,23 +1,51 @@ const roles = { - undo: { - label: 'Undo', - accelerator: 'CmdOrCtrl+Z', - method: 'undo' + about: { + get label () { + const {app} = require('electron') + return `About ${app.getName()}` + } }, - redo: { - label: 'Redo', - accelerator: 'Shift+CmdOrCtrl+Z', - method: 'redo' + close: { + label: 'Close', + accelerator: 'CmdOrCtrl+W', + method: 'close' + }, + copy: { + label: 'Copy', + accelerator: 'CmdOrCtrl+C', + method: 'copy' }, cut: { label: 'Cut', accelerator: 'CmdOrCtrl+X', method: 'cut' }, - copy: { - label: 'Copy', - accelerator: 'CmdOrCtrl+C', - method: 'copy' + delete: { + label: 'Delete', + accelerator: 'Delete', + method: 'delete' + }, + front: { + label: 'Bring All to Front' + }, + help: { + label: 'Help' + }, + hide: { + get label () { + const {app} = require('electron') + return `Hide ${app.getName()}` + }, + accelerator: 'Command+H' + }, + hideothers: { + label: 'Hide Others', + accelerator: 'Command+Alt+H' + }, + minimize: { + label: 'Minimize', + accelerator: 'CmdOrCtrl+M', + method: 'minimize' }, paste: { label: 'Paste', @@ -29,26 +57,6 @@ const roles = { accelerator: 'Shift+Command+V', method: 'pasteAndMatchStyle' }, - selectall: { - label: 'Select All', - accelerator: 'CmdOrCtrl+A', - method: 'selectAll' - }, - minimize: { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', - method: 'minimize' - }, - close: { - label: 'Close', - accelerator: 'CmdOrCtrl+W', - method: 'close' - }, - delete: { - label: 'Delete', - accelerator: 'Delete', - method: 'delete' - }, quit: { get label () { const {app} = require('electron') @@ -57,45 +65,37 @@ const roles = { accelerator: process.platform === 'win32' ? null : 'Command+Q', method: 'quit' }, + redo: { + label: 'Redo', + accelerator: 'Shift+CmdOrCtrl+Z', + method: 'redo' + }, + selectall: { + label: 'Select All', + accelerator: 'CmdOrCtrl+A', + method: 'selectAll' + }, + services: { + label: 'Services' + }, togglefullscreen: { label: 'Toggle Full Screen', accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', method: 'toggleFullScreen' }, - help: { - label: 'Help' + undo: { + label: 'Undo', + accelerator: 'CmdOrCtrl+Z', + method: 'undo' + }, + unhide: { + label: 'Show All' }, window: { label: 'Window' }, - services: { - label: 'Services' - }, zoom: { label: 'Zoom' - }, - front: { - label: 'Bring All to Front' - }, - about: { - get label () { - const {app} = require('electron') - return `About ${app.getName()}` - } - }, - hide: { - get label () { - const {app} = require('electron') - return return `Hide ${app.getName()}` - }, - accelerator: 'Command+H' - }, - hideothers: { - label: 'Hide Others', - accelerator: 'Command+Alt+H' - }, - unhide: { - label: 'Show All' } } From 66f2fb2fe4ae6c0b39991db4078097067a3ae048 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 13:48:26 -0700 Subject: [PATCH 070/192] Add execute helper to roles file --- lib/browser/api/menu-item-roles.js | 57 +++++++++++++++++++++++------- lib/browser/api/menu-item.js | 49 +++---------------------- 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index 6d6b0371e87c..947bf4d28c1f 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -8,22 +8,22 @@ const roles = { close: { label: 'Close', accelerator: 'CmdOrCtrl+W', - method: 'close' + windowMethod: 'close' }, copy: { label: 'Copy', accelerator: 'CmdOrCtrl+C', - method: 'copy' + webContentsMethod: 'copy' }, cut: { label: 'Cut', accelerator: 'CmdOrCtrl+X', - method: 'cut' + webContentsMethod: 'cut' }, delete: { label: 'Delete', accelerator: 'Delete', - method: 'delete' + webContentsMethod: 'delete' }, front: { label: 'Bring All to Front' @@ -45,17 +45,17 @@ const roles = { minimize: { label: 'Minimize', accelerator: 'CmdOrCtrl+M', - method: 'minimize' + windowMethod: 'minimize' }, paste: { label: 'Paste', accelerator: 'CmdOrCtrl+V', - method: 'paste' + webContentsMethod: 'paste' }, pasteandmatchstyle: { label: 'Paste and Match Style', accelerator: 'Shift+Command+V', - method: 'pasteAndMatchStyle' + webContentsMethod: 'pasteAndMatchStyle' }, quit: { get label () { @@ -63,17 +63,17 @@ const roles = { return process.platform === 'win32' ? 'Exit' : `Quit ${app.getName()}` }, accelerator: process.platform === 'win32' ? null : 'Command+Q', - method: 'quit' + appMethod: 'quit' }, redo: { label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', - method: 'redo' + webContentsMethod: 'redo' }, selectall: { label: 'Select All', accelerator: 'CmdOrCtrl+A', - method: 'selectAll' + webContentsMethod: 'selectAll' }, services: { label: 'Services' @@ -81,12 +81,14 @@ const roles = { togglefullscreen: { label: 'Toggle Full Screen', accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', - method: 'toggleFullScreen' + windowMethod: function (window) { + window.setFullScreen(!window.isFullScreen()) + } }, undo: { label: 'Undo', accelerator: 'CmdOrCtrl+Z', - method: 'undo' + webContentsMethod: 'undo' }, unhide: { label: 'Show All' @@ -110,3 +112,34 @@ exports.getDefaultLabel = function (role) { exports.getDefaultAccelerator = function (role) { if (roles.hasOwnProperty(role)) return roles[role].accelerator } + +exports.execute = function (role, focusedWindow) { + if (!roles.hasOwnProperty(role)) return false + if (process.platform === 'darwin') return false + + const {appMethod, webContentsMethod, windowMethod} = roles[role] + + if (appMethod) { + app[appMethod]() + return true + } + + if (focusedWindow != null) { + if (windowMethod) { + if (typeof windowMethod === 'function') { + windowMethod(focusedWindow) + } else { + focusedWindow[windowMethod]() + } + return true + } else if (webContentsMethod) { + const {webContents} = focusedWindow + if (webContents) { + webContents[webContentsMethod]() + } + return true + } + } + + return false +} diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index ba980960b559..d2d8e651980c 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -4,35 +4,6 @@ const roles = require('./menu-item-roles') let nextCommandId = 0 -// Maps role to methods of webContents -const rolesMap = { - undo: 'undo', - redo: 'redo', - cut: 'cut', - copy: 'copy', - paste: 'paste', - pasteandmatchstyle: 'pasteAndMatchStyle', - selectall: 'selectAll', - minimize: 'minimize', - close: 'close', - delete: 'delete', - quit: 'quit', - togglefullscreen: 'toggleFullScreen' -} - -// Maps methods that should be called directly on the BrowserWindow instance -const methodInBrowserWindow = { - minimize: true, - close: true, - toggleFullScreen: function (window) { - window.setFullScreen(!window.isFullScreen()) - } -} - -const methodInApp = { - quit: true -} - const MenuItem = function (options) { const {app, Menu} = require('electron') @@ -83,22 +54,12 @@ const MenuItem = function (options) { this.checked = !this.checked } - if (this.role && rolesMap[this.role] && process.platform !== 'darwin' && focusedWindow != null) { - const methodName = rolesMap[this.role] - if (methodInApp[methodName]) { - return app[methodName]() - } else if (typeof methodInBrowserWindow[methodName] === 'function') { - return methodInBrowserWindow[methodName](focusedWindow) - } else if (methodInBrowserWindow[methodName]) { - return focusedWindow[methodName]() - } else { - const {webContents} = focusedWindow - return webContents != null ? webContents[methodName]() : void 0 + if (!roles.execute(this.role)) { + if (typeof click === 'function') { + click(this, focusedWindow, event) + } else if (typeof this.selector === 'string' && process.platform === 'darwin') { + Menu.sendActionToFirstResponder(this.selector) } - } else if (typeof click === 'function') { - return click(this, focusedWindow, event) - } else if (typeof this.selector === 'string' && process.platform === 'darwin') { - return Menu.sendActionToFirstResponder(this.selector) } } } From 30e3a6ed8363ff99b5c301c2b866e981b25d8b89 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 13:59:49 -0700 Subject: [PATCH 071/192] Add app require --- lib/browser/api/menu-item-roles.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index 947bf4d28c1f..a6a985a5e2b4 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -120,6 +120,7 @@ exports.execute = function (role, focusedWindow) { const {appMethod, webContentsMethod, windowMethod} = roles[role] if (appMethod) { + const {app} = require('electron') app[appMethod]() return true } From c6dc6a8905e3db5bf99035094d42d399325a21d9 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 14:02:31 -0700 Subject: [PATCH 072/192] Specify focused window to execute call --- lib/browser/api/menu-item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index d2d8e651980c..2f96fdd316a6 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -54,7 +54,7 @@ const MenuItem = function (options) { this.checked = !this.checked } - if (!roles.execute(this.role)) { + if (!roles.execute(this.role, focusedWindow)) { if (typeof click === 'function') { click(this, focusedWindow, event) } else if (typeof this.selector === 'string' && process.platform === 'darwin') { From 5096d7835f71f5d79bd6df4edeadadb489669d17 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 14:07:46 -0700 Subject: [PATCH 073/192] Add spec for default role label/accelerator --- spec/api-menu-spec.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index d6ac56475f66..a6c56a9a40db 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -395,4 +395,28 @@ describe('menu module', function () { }, /Invalid submenu/) }) }) + + describe('MenuItem role', function () { + it('includes a default label and accelerator', function () { + var item = new MenuItem({role: 'close'}) + assert.equal(item.label, 'Close') + assert.equal(item.accelerator, 'CmdOrCtrl+W') + + var item = new MenuItem({role: 'close', label: 'Other'}) + assert.equal(item.label, 'Other') + assert.equal(item.accelerator, 'CmdOrCtrl+W') + + var item = new MenuItem({role: 'close', accelerator: 'D'}) + assert.equal(item.label, 'Close') + assert.equal(item.accelerator, 'D') + + var item = new MenuItem({role: 'close', label: 'C', accelerator: 'D'}) + assert.equal(item.label, 'C') + assert.equal(item.accelerator, 'D') + + var item = new MenuItem({role: 'help'}) + assert.equal(item.label, 'Help') + assert.equal(item.accelerator, undefined) + }) + }) }) From c98f419bc805a4feddaccd299191e721a900b757 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 14:10:38 -0700 Subject: [PATCH 074/192] Doc role defaults --- docs/api/menu-item.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 0d05d81bcd00..3a9a565a9dcb 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -39,6 +39,9 @@ It is best to specify `role` for any menu item that matches a standard role, rather than trying to manually implement the behavior in a `click` function. The built-in `role` behavior will give the best native experience. +The `label` and `accelerator` are optional when using a `role` and will default +to appropriate values for each platform. + The `role` property can have following values: * `undo` From 58c1d38c96499e4e4f3b498704d88b3feb29a004 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 14:13:12 -0700 Subject: [PATCH 075/192] Remove lint errors --- default_app/main.js | 2 +- lib/browser/api/menu-item.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/default_app/main.js b/default_app/main.js index a484c030dbfc..dd887e0def1e 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -91,7 +91,7 @@ app.once('ready', () => { } }, { - role: 'togglefullscreen', + role: 'togglefullscreen' }, { label: 'Toggle Developer Tools', diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 2f96fdd316a6..8fb1b51d1e65 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -5,7 +5,7 @@ const roles = require('./menu-item-roles') let nextCommandId = 0 const MenuItem = function (options) { - const {app, Menu} = require('electron') + const {Menu} = require('electron') this.selector = options.selector this.type = options.type From ece319a6871bc76c358db8b1a033ac878cb1e5a9 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 14:14:32 -0700 Subject: [PATCH 076/192] :art: --- lib/browser/api/menu-item-roles.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index a6a985a5e2b4..aaebd56223b4 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -125,21 +125,21 @@ exports.execute = function (role, focusedWindow) { return true } - if (focusedWindow != null) { - if (windowMethod) { - if (typeof windowMethod === 'function') { - windowMethod(focusedWindow) - } else { - focusedWindow[windowMethod]() - } - return true - } else if (webContentsMethod) { - const {webContents} = focusedWindow - if (webContents) { - webContents[webContentsMethod]() - } - return true + if (windowMethod && focusedWindow != null) { + if (typeof windowMethod === 'function') { + windowMethod(focusedWindow) + } else { + focusedWindow[windowMethod]() } + return true + } + + if (webContentsMethod && focusedWindow != null) { + const {webContents} = focusedWindow + if (webContents) { + webContents[webContentsMethod]() + } + return true } return false From b7afe44a5c16b1e876b08a32cca731c0c777d5e0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 14:22:15 -0700 Subject: [PATCH 077/192] Add assert for role with app name in label --- spec/api-menu-spec.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index a6c56a9a40db..7d1044b23244 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -417,6 +417,10 @@ describe('menu module', function () { var item = new MenuItem({role: 'help'}) assert.equal(item.label, 'Help') assert.equal(item.accelerator, undefined) + + var item = new MenuItem({role: 'about'}) + assert.equal(item.label, 'About Electron Test') + assert.equal(item.accelerator, undefined) }) }) }) From 4dbdcad05ea841021a906b305161beaced5c0042 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 14:37:16 -0700 Subject: [PATCH 078/192] Remove label/accelerators with role defaults --- docs/api/menu.md | 39 ++------------------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/docs/api/menu.md b/docs/api/menu.md index 7dea2c6b4cdd..d54469545e6d 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -39,45 +39,30 @@ const template = [ label: 'Edit', submenu: [ { - label: 'Undo', - accelerator: 'CmdOrCtrl+Z', role: 'undo' }, { - label: 'Redo', - accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, { type: 'separator' }, { - label: 'Cut', - accelerator: 'CmdOrCtrl+X', role: 'cut' }, { - label: 'Copy', - accelerator: 'CmdOrCtrl+C', role: 'copy' }, { - label: 'Paste', - accelerator: 'CmdOrCtrl+V', role: 'paste' }, { - label: 'Paste and Match Style', - accelerator: 'Shift+Command+V', role: 'pasteandmatchstyle' }, { - label: 'Delete', role: 'delete' }, { - label: 'Select All', - accelerator: 'CmdOrCtrl+A', role: 'selectall' }, ] @@ -93,12 +78,7 @@ const template = [ } }, { - label: 'Toggle Full Screen', - accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', - click(item, focusedWindow) { - if (focusedWindow) - focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); - } + role: 'togglefullscreen' }, { label: 'Toggle Developer Tools', @@ -111,23 +91,17 @@ const template = [ ] }, { - label: 'Window', role: 'window', submenu: [ { - label: 'Minimize', - accelerator: 'CmdOrCtrl+M', role: 'minimize' }, { - label: 'Close', - accelerator: 'CmdOrCtrl+W', role: 'close' }, ] }, { - label: 'Help', role: 'help', submenu: [ { @@ -144,14 +118,12 @@ if (process.platform === 'darwin') { label: name, submenu: [ { - label: 'About ' + name, role: 'about' }, { type: 'separator' }, { - label: 'Services', role: 'services', submenu: [] }, @@ -159,26 +131,19 @@ if (process.platform === 'darwin') { type: 'separator' }, { - label: 'Hide ' + name, - accelerator: 'Command+H', role: 'hide' }, { - label: 'Hide Others', - accelerator: 'Command+Alt+H', role: 'hideothers' }, { - label: 'Show All', role: 'unhide' }, { type: 'separator' }, { - label: 'Quit', - accelerator: 'Command+Q', - click() { app.quit(); } + role: 'quit' }, ] }); From be642612c02b8e7fba92b2417a135a7b092d939d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 15:21:45 -0700 Subject: [PATCH 079/192] Export app before requiring modules --- lib/browser/api/app.js | 12 ++++++------ lib/browser/api/menu-item-roles.js | 6 ++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/browser/api/app.js b/lib/browser/api/app.js index 09835a792e0e..34210f42090f 100644 --- a/lib/browser/api/app.js +++ b/lib/browser/api/app.js @@ -1,12 +1,15 @@ 'use strict' +const bindings = process.atomBinding('app') +const {app} = bindings + +// Only one app object permitted. +module.exports = app + const electron = require('electron') const {deprecate, Menu} = electron const {EventEmitter} = require('events') -const bindings = process.atomBinding('app') -const {app} = bindings - Object.setPrototypeOf(app, EventEmitter.prototype) let appPath = null @@ -67,6 +70,3 @@ process.atomBinding('download_item')._setWrapDownloadItem((downloadItem) => { // downloadItem is an EventEmitter. Object.setPrototypeOf(downloadItem, EventEmitter.prototype) }) - -// Only one App object pemitted. -module.exports = app diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index aaebd56223b4..0df6126a15f4 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -1,7 +1,8 @@ +const {app} = require('electron') + const roles = { about: { get label () { - const {app} = require('electron') return `About ${app.getName()}` } }, @@ -33,7 +34,6 @@ const roles = { }, hide: { get label () { - const {app} = require('electron') return `Hide ${app.getName()}` }, accelerator: 'Command+H' @@ -59,7 +59,6 @@ const roles = { }, quit: { get label () { - const {app} = require('electron') return process.platform === 'win32' ? 'Exit' : `Quit ${app.getName()}` }, accelerator: process.platform === 'win32' ? null : 'Command+Q', @@ -120,7 +119,6 @@ exports.execute = function (role, focusedWindow) { const {appMethod, webContentsMethod, windowMethod} = roles[role] if (appMethod) { - const {app} = require('electron') app[appMethod]() return true } From 6165908ba7c0d6712d16939329d96596fbdd4a57 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 15:26:17 -0700 Subject: [PATCH 080/192] Incorporate review feedback --- lib/browser/api/menu-item-roles.js | 33 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index 0df6126a15f4..232c3e05b673 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -3,27 +3,26 @@ const {app} = require('electron') const roles = { about: { get label () { - return `About ${app.getName()}` + return process.platform === 'linux' ? 'About' : `About ${app.getName()}` } }, close: { label: 'Close', - accelerator: 'CmdOrCtrl+W', + accelerator: 'CommandOrControl+W', windowMethod: 'close' }, copy: { label: 'Copy', - accelerator: 'CmdOrCtrl+C', + accelerator: 'CommandOrControl+C', webContentsMethod: 'copy' }, cut: { label: 'Cut', - accelerator: 'CmdOrCtrl+X', + accelerator: 'CommandOrControl+X', webContentsMethod: 'cut' }, delete: { label: 'Delete', - accelerator: 'Delete', webContentsMethod: 'delete' }, front: { @@ -44,34 +43,38 @@ const roles = { }, minimize: { label: 'Minimize', - accelerator: 'CmdOrCtrl+M', + accelerator: 'CommandOrControl+M', windowMethod: 'minimize' }, paste: { label: 'Paste', - accelerator: 'CmdOrCtrl+V', + accelerator: 'CommandOrControl+V', webContentsMethod: 'paste' }, pasteandmatchstyle: { label: 'Paste and Match Style', - accelerator: 'Shift+Command+V', + accelerator: 'Shift+CommandOrControl+V', webContentsMethod: 'pasteAndMatchStyle' }, quit: { get label () { - return process.platform === 'win32' ? 'Exit' : `Quit ${app.getName()}` + switch (process.platform) { + case 'darwin': return `Quit ${app.getName()}` + case 'win32': return 'Exit' + default: return 'Quit' + } }, accelerator: process.platform === 'win32' ? null : 'Command+Q', appMethod: 'quit' }, redo: { label: 'Redo', - accelerator: 'Shift+CmdOrCtrl+Z', + accelerator: 'Shift+CommandOrControl+Z', webContentsMethod: 'redo' }, selectall: { label: 'Select All', - accelerator: 'CmdOrCtrl+A', + accelerator: 'CommandOrControl+A', webContentsMethod: 'selectAll' }, services: { @@ -86,7 +89,7 @@ const roles = { }, undo: { label: 'Undo', - accelerator: 'CmdOrCtrl+Z', + accelerator: 'CommandOrControl+Z', webContentsMethod: 'undo' }, unhide: { @@ -100,7 +103,7 @@ const roles = { } } -exports.getDefaultLabel = function (role) { +exports.getDefaultLabel = (role) => { if (roles.hasOwnProperty(role)) { return roles[role].label } else { @@ -108,11 +111,11 @@ exports.getDefaultLabel = function (role) { } } -exports.getDefaultAccelerator = function (role) { +exports.getDefaultAccelerator = (role) => { if (roles.hasOwnProperty(role)) return roles[role].accelerator } -exports.execute = function (role, focusedWindow) { +exports.execute = (role, focusedWindow) => { if (!roles.hasOwnProperty(role)) return false if (process.platform === 'darwin') return false From 813e528350a78730375f26d10d6aa1c0659f2f3d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 15:35:58 -0700 Subject: [PATCH 081/192] Update expected accelerator --- spec/api-menu-spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/api-menu-spec.js b/spec/api-menu-spec.js index 7d1044b23244..d78b5b4e36a0 100644 --- a/spec/api-menu-spec.js +++ b/spec/api-menu-spec.js @@ -400,11 +400,11 @@ describe('menu module', function () { it('includes a default label and accelerator', function () { var item = new MenuItem({role: 'close'}) assert.equal(item.label, 'Close') - assert.equal(item.accelerator, 'CmdOrCtrl+W') + assert.equal(item.accelerator, 'CommandOrControl+W') var item = new MenuItem({role: 'close', label: 'Other'}) assert.equal(item.label, 'Other') - assert.equal(item.accelerator, 'CmdOrCtrl+W') + assert.equal(item.accelerator, 'CommandOrControl+W') var item = new MenuItem({role: 'close', accelerator: 'D'}) assert.equal(item.label, 'Close') @@ -418,9 +418,9 @@ describe('menu module', function () { assert.equal(item.label, 'Help') assert.equal(item.accelerator, undefined) - var item = new MenuItem({role: 'about'}) - assert.equal(item.label, 'About Electron Test') - assert.equal(item.accelerator, undefined) + var item = new MenuItem({role: 'hide'}) + assert.equal(item.label, 'Hide Electron Test') + assert.equal(item.accelerator, 'Command+H') }) }) }) From e9222583cbd354559ec0ab0bb1170567c2ec9d73 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 15:46:22 -0700 Subject: [PATCH 082/192] toPng/toJpeg -> toPNG/toJPEG --- atom/common/api/atom_api_native_image.cc | 9 ++++++--- docs/api/native-image.md | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index c0b51ba2af28..b666f86ba3cb 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -344,14 +344,17 @@ mate::Handle NativeImage::CreateFromDataURL( void NativeImage::BuildPrototype( v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) - .SetMethod("toPng", &NativeImage::ToPNG) - .SetMethod("toJpeg", &NativeImage::ToJPEG) + .SetMethod("toPNG", &NativeImage::ToPNG) + .SetMethod("toJPEG", &NativeImage::ToJPEG) .SetMethod("getNativeHandle", &NativeImage::GetNativeHandle) .SetMethod("toDataURL", &NativeImage::ToDataURL) .SetMethod("isEmpty", &NativeImage::IsEmpty) .SetMethod("getSize", &NativeImage::GetSize) .SetMethod("setTemplateImage", &NativeImage::SetTemplateImage) - .SetMethod("isTemplateImage", &NativeImage::IsTemplateImage); + .SetMethod("isTemplateImage", &NativeImage::IsTemplateImage) + // TODO(kevinsawicki): Remove in 2.0, deprecate before then with warnings + .SetMethod("toPng", &NativeImage::ToPNG) + .SetMethod("toJpeg", &NativeImage::ToJPEG); } } // namespace api diff --git a/docs/api/native-image.md b/docs/api/native-image.md index d2a2926ca6d9..f03178577b86 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -127,11 +127,11 @@ Creates a new `nativeImage` instance from `dataURL`. The following methods are available on instances of `nativeImage`: -### `image.toPng()` +### `image.toPNG()` Returns a [Buffer][buffer] that contains the image's `PNG` encoded data. -### `image.toJpeg(quality)` +### `image.toJPEG(quality)` * `quality` Integer (**required**) - Between 0 - 100. From 233355ed0999050850e2fddb38142db45cb5ec00 Mon Sep 17 00:00:00 2001 From: Jhen Date: Wed, 22 Jun 2016 19:47:07 +0800 Subject: [PATCH 083/192] Fix chrome.runtime.sendMessage add a case of not provide extension-id: (message, responseCallback) --- lib/renderer/chrome-api.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/renderer/chrome-api.js b/lib/renderer/chrome-api.js index 6c28c98edf44..b49d7382c041 100644 --- a/lib/renderer/chrome-api.js +++ b/lib/renderer/chrome-api.js @@ -119,7 +119,13 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) { if (args.length === 1) { message = args[0] } else if (args.length === 2) { - [targetExtensionId, message] = args + // A case of not provide extension-id: (message, responseCallback) + if (typeof args[1] === 'function') { + console.error('responseCallback is not supported') + message = args[0] + } else { + [targetExtensionId, message] = args + } } else { console.error('options and responseCallback are not supported') } From c6869972091c305c288c780a9722ae27f10aa433 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 16:56:45 -0700 Subject: [PATCH 084/192] Ctrl -> Control for consistency --- lib/browser/api/menu-item-roles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index 232c3e05b673..082099818fdc 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -82,7 +82,7 @@ const roles = { }, togglefullscreen: { label: 'Toggle Full Screen', - accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', + accelerator: process.platform === 'darwin' ? 'Control+Command+F' : 'F11', windowMethod: function (window) { window.setFullScreen(!window.isFullScreen()) } From 5a122b5ea03e8220fb0d221a111a44fd0c6e4f42 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 22 Jun 2016 18:51:39 -0700 Subject: [PATCH 085/192] Eagerly require protocol --- lib/browser/init.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/browser/init.js b/lib/browser/init.js index 924ad1420c2e..6326b8e80e8b 100644 --- a/lib/browser/init.js +++ b/lib/browser/init.js @@ -173,6 +173,9 @@ require('./chrome-extension') // Load internal desktop-capturer module. require('./desktop-capturer') +// Load protocol module to ensure it is populated on app ready +require('./api/protocol') + // Set main startup script of the app. var mainStartupScript = packageJson.main || 'index.js' From 7fa4a7889b7b22da84522cb0fbc446f999831295 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jun 2016 12:05:10 +0900 Subject: [PATCH 086/192] Update brightray for #6178 --- vendor/brightray | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/brightray b/vendor/brightray index 8244628f0c1d..25d39ad99a77 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 8244628f0c1d0eb15c659d42e882fb5d447c77ba +Subproject commit 25d39ad99a7738385af0947804261608d99765ab From 7f3a296050b409346eae5adbbd73e49b5a5f9a88 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jun 2016 13:00:14 +0900 Subject: [PATCH 087/192] mac: Avoid calling setTitleVisibility for OS X 10.9 --- atom/browser/native_window_mac.mm | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 004d2cad23de..1ef003ab2aa9 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -205,6 +205,7 @@ bool ScopedDisableResize::disable_resize_ = false; // have to set one, because title bar is visible here. NSWindow* window = shell_->GetNativeWindow(); if ((shell_->transparent() || !shell_->has_frame()) && + base::mac::IsOSYosemiteOrLater() && // FIXME(zcbenz): Showing titlebar for hiddenInset window is weird under // fullscreen mode. shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET) { @@ -229,6 +230,7 @@ bool ScopedDisableResize::disable_resize_ = false; // Restore the titlebar visibility. NSWindow* window = shell_->GetNativeWindow(); if ((shell_->transparent() || !shell_->has_frame()) && + base::mac::IsOSYosemiteOrLater() && shell_->title_bar_style() != atom::NativeWindowMac::HIDDEN_INSET) { [window setTitleVisibility:NSWindowTitleHidden]; } @@ -532,8 +534,10 @@ NativeWindowMac::NativeWindowMac( [window_ setDisableKeyOrMainWindow:YES]; if (transparent() || !has_frame()) { - // Don't show title bar. - [window_ setTitleVisibility:NSWindowTitleHidden]; + if (base::mac::IsOSYosemiteOrLater()) { + // Don't show title bar. + [window_ setTitleVisibility:NSWindowTitleHidden]; + } // Remove non-transparent corners, see http://git.io/vfonD. [window_ setOpaque:NO]; } @@ -858,6 +862,11 @@ void NativeWindowMac::Center() { } void NativeWindowMac::SetTitle(const std::string& title) { + // For macOS <= 10.9, the setTitleVisibility API is not available, we have + // to avoid calling setTitle for frameless window. + if (!base::mac::IsOSYosemiteOrLater() && (transparent() || !has_frame())) + return; + [window_ setTitle:base::SysUTF8ToNSString(title)]; } From 71ab69314cc41706ce5ef880891df9157ec6207f Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jun 2016 13:11:19 +0900 Subject: [PATCH 088/192] spec: Put protocol registration in beforeEach --- spec/api-session-spec.js | 47 +++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 25a09ba313b7..874726db9f8c 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -16,10 +16,6 @@ describe('session module', function () { var fixtures = path.resolve(__dirname, 'fixtures') var w = null var url = 'http://127.0.0.1' - var partitionName = 'temp' - var protocolName = 'sp' - const partitionProtocol = session.fromPartition(partitionName).protocol - const protocol = session.defaultSession.protocol beforeEach(function () { if (w != null) { @@ -274,18 +270,25 @@ describe('session module', function () { }) describe('session.protocol', function () { - beforeEach(function () { - if (w != null) { - w.destroy() - } + const partitionName = 'temp' + const protocolName = 'sp' + const partitionProtocol = session.fromPartition(partitionName).protocol + const protocol = session.defaultSession.protocol + const handler = function (error, callback) { + callback({data: 'test'}) + } + + beforeEach(function (done) { + if (w != null) w.destroy() w = new BrowserWindow({ show: false, - width: 400, - height: 400, webPreferences: { partition: partitionName } }) + partitionProtocol.registerStringProtocol(protocolName, handler, function (error) { + done(error ? error : undefined) + }) }) afterEach(function (done) { @@ -293,24 +296,14 @@ describe('session module', function () { }) it('handles requests from a partition', function (done) { - var handler = function (error, callback) { - callback({ - data: 'test' - }) - } - partitionProtocol.registerStringProtocol(protocolName, handler, function (error) { - if (error) { - return done(error) - } - protocol.isProtocolHandled(protocolName, function (result) { - assert.equal(result, false) - partitionProtocol.isProtocolHandled(protocolName, function (result) { - assert.equal(result, true) - w.webContents.on('did-finish-load', function () { - done() - }) - w.loadURL(protocolName + "://fake-host") + protocol.isProtocolHandled(protocolName, function (result) { + assert.equal(result, false) + partitionProtocol.isProtocolHandled(protocolName, function (result) { + assert.equal(result, true) + w.webContents.on('did-finish-load', function () { + done() }) + w.loadURL(protocolName + "://fake-host") }) }) }) From a04c0ce196504db97d286b6d5eb7ae46e774f7f1 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jun 2016 13:14:33 +0900 Subject: [PATCH 089/192] spec: Split the session.protocol test into multiple tests --- spec/api-session-spec.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 874726db9f8c..f634f1ce6345 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -295,17 +295,21 @@ describe('session module', function () { partitionProtocol.unregisterProtocol(protocolName, () => done()) }) - it('handles requests from a partition', function (done) { + it('does not affect defaultSession', function (done) { protocol.isProtocolHandled(protocolName, function (result) { assert.equal(result, false) partitionProtocol.isProtocolHandled(protocolName, function (result) { assert.equal(result, true) - w.webContents.on('did-finish-load', function () { - done() - }) - w.loadURL(protocolName + "://fake-host") + done() }) }) }) + + it('handles requests from partition', function (done) { + w.webContents.on('did-finish-load', function () { + done() + }) + w.loadURL(`${protocolName}://fake-host`) + }) }) }) From 2893974c05969df16a4067c3903772aa52ed4e4a Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jun 2016 13:29:18 +0900 Subject: [PATCH 090/192] spec: Set mimeType for protocol's handler --- spec/api-session-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index f634f1ce6345..71a51e6759ea 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -275,7 +275,7 @@ describe('session module', function () { const partitionProtocol = session.fromPartition(partitionName).protocol const protocol = session.defaultSession.protocol const handler = function (error, callback) { - callback({data: 'test'}) + callback({data: 'test', mimeType: 'text/html'}) } beforeEach(function (done) { From a00c5e3363c7ea89701ea7323fbf8001b20596bf Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jun 2016 14:37:58 +0900 Subject: [PATCH 091/192] Bump v1.2.5 --- atom/browser/resources/mac/Info.plist | 4 ++-- atom/browser/resources/win/atom.rc | 8 ++++---- atom/common/atom_version.h | 2 +- electron.gyp | 2 +- package.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 41d8a27ea8d7..33cfc66b2718 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile electron.icns CFBundleVersion - 1.2.4 + 1.2.5 CFBundleShortVersionString - 1.2.4 + 1.2.5 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 370a1e8f3d46..91101128feda 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,4,0 - PRODUCTVERSION 1,2,4,0 + FILEVERSION 1,2,5,0 + PRODUCTVERSION 1,2,5,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "1.2.4" + VALUE "FileVersion", "1.2.5" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "1.2.4" + VALUE "ProductVersion", "1.2.5" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 7bf5a0970244..7a61d6bc72bd 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 1 #define ATOM_MINOR_VERSION 2 -#define ATOM_PATCH_VERSION 4 +#define ATOM_PATCH_VERSION 5 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/electron.gyp b/electron.gyp index d0657df31d70..a4c412fc9877 100644 --- a/electron.gyp +++ b/electron.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '1.2.4', + 'version%': '1.2.5', }, 'includes': [ 'filenames.gypi', diff --git a/package.json b/package.json index bce8c02dfc2c..87782e100dea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "1.2.4", + "version": "1.2.5", "devDependencies": { "asar": "^0.11.0", "request": "*", From ed6213e9514b9dc5a1fee10d29fb192259a4bffa Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 23 Jun 2016 20:32:19 +0900 Subject: [PATCH 092/192] Do not emit did-fail-load for canceled requests --- atom/browser/api/atom_api_web_contents.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 58b6a4729a84..45719d95199b 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -607,7 +607,10 @@ void WebContents::DidFailProvisionalLoad( bool was_ignored_by_handler) { bool is_main_frame = !render_frame_host->GetParent(); Emit("did-fail-provisional-load", code, description, url, is_main_frame); - Emit("did-fail-load", code, description, url, is_main_frame); + + // Do not emit "did-fail-load" for canceled requests. + if (code != net::ERR_ABORTED) + Emit("did-fail-load", code, description, url, is_main_frame); } void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host, From cfebf7aa6bae16d1b12f2461df7ecafea58c3202 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Thu, 23 Jun 2016 10:36:16 -0700 Subject: [PATCH 093/192] fix list indentation --- docs/api/web-contents.md | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 610fa2aa6d73..ce0948745b00 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -360,25 +360,27 @@ Returns: able to perform the corresponding action. See more about this below. The `mediaFlags` is an object with the following properties: - * `inError` Boolean - Whether the media element has crashed. - * `isPaused` Boolean - Whether the media element is paused. - * `isMuted` Boolean - Whether the media element is muted. - * `hasAudio` Boolean - Whether the media element has audio. - * `isLooping` Boolean - Whether the media element is looping. - * `isControlsVisible` Boolean - Whether the media element's controls are - visible. - * `canToggleControls` Boolean - Whether the media element's controls are - toggleable. - * `canRotate` Boolean - Whether the media element can be rotated. + +* `inError` Boolean - Whether the media element has crashed. +* `isPaused` Boolean - Whether the media element is paused. +* `isMuted` Boolean - Whether the media element is muted. +* `hasAudio` Boolean - Whether the media element has audio. +* `isLooping` Boolean - Whether the media element is looping. +* `isControlsVisible` Boolean - Whether the media element's controls are + visible. +* `canToggleControls` Boolean - Whether the media element's controls are + toggleable. +* `canRotate` Boolean - Whether the media element can be rotated. The `editFlags` is an object with the following properties: - * `canUndo` Boolean - Whether the renderer believes it can undo. - * `canRedo` Boolean - Whether the renderer believes it can redo. - * `canCut` Boolean - Whether the renderer believes it can cut. - * `canCopy` Boolean - Whether the renderer believes it can copy - * `canPaste` Boolean - Whether the renderer believes it can paste. - * `canDelete` Boolean - Whether the renderer believes it can delete. - * `canSelectAll` Boolean - Whether the renderer believes it can select all. + +* `canUndo` Boolean - Whether the renderer believes it can undo. +* `canRedo` Boolean - Whether the renderer believes it can redo. +* `canCut` Boolean - Whether the renderer believes it can cut. +* `canCopy` Boolean - Whether the renderer believes it can copy +* `canPaste` Boolean - Whether the renderer believes it can paste. +* `canDelete` Boolean - Whether the renderer believes it can delete. +* `canSelectAll` Boolean - Whether the renderer believes it can select all. Emitted when there is a new context menu that needs to be handled. From 3dcbbe9f2013746565becbbc2e1e7826a74522e6 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Thu, 23 Jun 2016 14:13:03 -0700 Subject: [PATCH 094/192] remove parens from type --- docs/api/browser-window.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 22b01244422c..169ab0f6facc 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -945,7 +945,7 @@ convey some sort of application status or to passively notify the user. ### `win.setHasShadow(hasShadow)` _macOS_ -* `hasShadow` (Boolean) +* `hasShadow` Boolean Sets whether the window should have a shadow. On Windows and Linux does nothing. From 5e389021ebf95dbd09e49b4830003bcf8fc1bac5 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jun 2016 14:19:23 -0700 Subject: [PATCH 095/192] Use upstream node-pre-gyp --- docs/tutorial/debugging-main-process.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/debugging-main-process.md b/docs/tutorial/debugging-main-process.md index 714b7100cf47..f4b783a9def1 100644 --- a/docs/tutorial/debugging-main-process.md +++ b/docs/tutorial/debugging-main-process.md @@ -31,10 +31,10 @@ with node-inspector, and the main process will crash if you inspect the $ npm install node-inspector ``` -### 3. Install a patched version of `node-pre-gyp` +### 3. Install [node-pre-gyp][node-pre-gyp] ```bash -$ npm install git+https://git@github.com/enlight/node-pre-gyp.git#detect-electron-runtime-in-find +$ npm install node-pre-gyp ``` ### 4. Recompile the `node-inspector` `v8` modules for electron (change the target to your electron version number) @@ -73,5 +73,6 @@ browser. You may have to click pause if starting with debug-brk to see the entry line. [node-inspector]: https://github.com/node-inspector/node-inspector +[node-pre-gyp]: https://github.com/mapbox/node-pre-gyp [node-gyp-required-tools]: https://github.com/nodejs/node-gyp#installation [how-to-install-native-modules]: using-native-node-modules.md#how-to-install-native-modules From eb8c1b1a50d7cb86d59201d5c0fa22c69cfd4e38 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jun 2016 14:20:52 -0700 Subject: [PATCH 096/192] Make update number message a note --- docs/tutorial/debugging-main-process.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/tutorial/debugging-main-process.md b/docs/tutorial/debugging-main-process.md index f4b783a9def1..ec2ac422d253 100644 --- a/docs/tutorial/debugging-main-process.md +++ b/docs/tutorial/debugging-main-process.md @@ -37,11 +37,13 @@ $ npm install node-inspector $ npm install node-pre-gyp ``` -### 4. Recompile the `node-inspector` `v8` modules for electron (change the target to your electron version number) +### 4. Recompile the `node-inspector` `v8` modules for Electron + +**Note:** Update the target argument to be your Electron version number ```bash -$ node_modules/.bin/node-pre-gyp --target=0.36.11 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall -$ node_modules/.bin/node-pre-gyp --target=0.36.11 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall +$ node_modules/.bin/node-pre-gyp --target=1.2.5 --runtime=electron --fallback-to-build --directory node_modules/v8-debug/ --dist-url=https://atom.io/download/atom-shell reinstall +$ node_modules/.bin/node-pre-gyp --target=1.2.5 --runtime=electron --fallback-to-build --directory node_modules/v8-profiler/ --dist-url=https://atom.io/download/atom-shell reinstall ``` See also [How to install native modules][how-to-install-native-modules]. From adc8c54a81b7628d4cf38b96513dc73925f51cb2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jun 2016 14:23:34 -0700 Subject: [PATCH 097/192] Minor formatting updates --- docs/tutorial/debugging-main-process.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/tutorial/debugging-main-process.md b/docs/tutorial/debugging-main-process.md index ec2ac422d253..3c153061a7e8 100644 --- a/docs/tutorial/debugging-main-process.md +++ b/docs/tutorial/debugging-main-process.md @@ -11,7 +11,7 @@ Use the following command line switches to debug Electron's main process: ### `--debug=[port]` When this switch is used Electron will listen for V8 debugger protocol -messages on `port`. The default `port` is `5858`. +messages on the `port`. The default `port` is `5858`. ### `--debug-brk=[port]` @@ -19,11 +19,11 @@ Like `--debug` but pauses the script on the first line. ## Use node-inspector for Debugging -**Note:** Electron doesn't currently work very well -with node-inspector, and the main process will crash if you inspect the -`process` object under node-inspector's console. +**Note:** Electron doesn't currently work very well with node-inspector, and the +main process will crash if you inspect the `process` object under +node-inspector's console. -### 1. Make sure you have [node-gyp required tools][node-gyp-required-tools] installed +### 1. Install the [node-gyp required tools][node-gyp-required-tools] ### 2. Install [node-inspector][node-inspector] @@ -62,7 +62,7 @@ or, to pause your script on the first line: $ electron --debug-brk=5858 your/app ``` -### 6. Start the [node-inspector][node-inspector] server using electron +### 6. Start the [node-inspector][node-inspector] server using Electron ```bash $ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin/inspector.js @@ -71,7 +71,7 @@ $ ELECTRON_RUN_AS_NODE=true path/to/electron.exe node_modules/node-inspector/bin ### 7. Load the debugger UI Open http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 in the Chrome -browser. You may have to click pause if starting with debug-brk to see the +browser. You may have to click pause if starting with `debug-brk` to see the entry line. [node-inspector]: https://github.com/node-inspector/node-inspector From eed240be1c4c0d10bacd17a266bd21f8c2ae6a60 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jun 2016 15:20:04 -0700 Subject: [PATCH 098/192] Add specs for Module._nodeModulesPath --- spec/modules-spec.js | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/spec/modules-spec.js b/spec/modules-spec.js index fbc66a082512..c3ad4bc70515 100644 --- a/spec/modules-spec.js +++ b/spec/modules-spec.js @@ -1,4 +1,5 @@ const assert = require('assert') +const Module = require('module') const path = require('path') const temp = require('temp') @@ -47,3 +48,50 @@ describe('third-party module', function () { }) }) }) + +describe('Module._nodeModulePaths', function () { + describe('when the path is inside the resources path', function () { + it('does not include paths outside of the resources path', function () { + let modulePath = process.resourcesPath + assert.deepEqual(Module._nodeModulePaths(modulePath), [ + path.join(process.resourcesPath, 'node_modules') + ]) + + modulePath = path.join(process.resourcesPath, 'foo') + assert.deepEqual(Module._nodeModulePaths(modulePath), [ + path.join(process.resourcesPath, 'foo', 'node_modules'), + path.join(process.resourcesPath, 'node_modules') + ]) + + modulePath = path.join(process.resourcesPath, 'node_modules', 'foo') + assert.deepEqual(Module._nodeModulePaths(modulePath), [ + path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'), + path.join(process.resourcesPath, 'node_modules') + ]) + + modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'bar') + assert.deepEqual(Module._nodeModulePaths(modulePath), [ + path.join(process.resourcesPath, 'node_modules', 'foo', 'bar', 'node_modules'), + path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'), + path.join(process.resourcesPath, 'node_modules') + ]) + + modulePath = path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar') + assert.deepEqual(Module._nodeModulePaths(modulePath), [ + path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules', 'bar', 'node_modules'), + path.join(process.resourcesPath, 'node_modules', 'foo', 'node_modules'), + path.join(process.resourcesPath, 'node_modules') + ]) + }) + }) + + describe('when the path is outside the resources path', function () { + it('includes paths outside of the resources path', function () { + let modulePath = path.resolve('/foo') + assert.deepEqual(Module._nodeModulePaths(modulePath), [ + path.join(modulePath, 'node_modules'), + path.join('node_modules') + ]) + }) + }) +}) From b273b70eee6a15808faad6f3fd5ad5d85d36e4d4 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jun 2016 15:27:45 -0700 Subject: [PATCH 099/192] Filter existing search paths instead reimplementing --- lib/common/reset-search-paths.js | 29 +++++++++-------------------- spec/modules-spec.js | 3 +-- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/lib/common/reset-search-paths.js b/lib/common/reset-search-paths.js index 4d09f6c89ca6..8e217012f582 100644 --- a/lib/common/reset-search-paths.js +++ b/lib/common/reset-search-paths.js @@ -9,30 +9,19 @@ module.paths = [] module.parent.paths = [] // Prevent Node from adding paths outside this app to search paths. +const originalNodeModulePaths = Module._nodeModulePaths Module._nodeModulePaths = function (from) { - from = path.resolve(from) + const paths = originalNodeModulePaths(from) + const rootPath = process.resourcesPath // If "from" is outside the app then we do nothing. - const skipOutsidePaths = from.startsWith(process.resourcesPath) - - // Following logic is copied from module.js. - const splitRe = process.platform === 'win32' ? /[\/\\]/ : /\// - const paths = [] - const parts = from.split(splitRe) - - let tip - let i - for (tip = i = parts.length - 1; i >= 0; tip = i += -1) { - const part = parts[tip] - if (part === 'node_modules') { - continue - } - const dir = parts.slice(0, tip + 1).join(path.sep) - if (skipOutsidePaths && !dir.startsWith(process.resourcesPath)) { - break - } - paths.push(path.join(dir, 'node_modules')) + const skipOutsidePaths = path.resolve(from).startsWith(rootPath) + if (skipOutsidePaths) { + return paths.filter(function (candidate) { + return candidate.startsWith(rootPath) + }) } + return paths } diff --git a/spec/modules-spec.js b/spec/modules-spec.js index c3ad4bc70515..87396ac764f8 100644 --- a/spec/modules-spec.js +++ b/spec/modules-spec.js @@ -89,8 +89,7 @@ describe('Module._nodeModulePaths', function () { it('includes paths outside of the resources path', function () { let modulePath = path.resolve('/foo') assert.deepEqual(Module._nodeModulePaths(modulePath), [ - path.join(modulePath, 'node_modules'), - path.join('node_modules') + path.join(modulePath, 'node_modules') ]) }) }) From c6906deef20ca3631862b84d1556cfcf8e4479de Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jun 2016 15:39:21 -0700 Subject: [PATCH 100/192] Add failing spec for trailing separator bug --- spec/modules-spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/modules-spec.js b/spec/modules-spec.js index 87396ac764f8..f2c45b0a71b4 100644 --- a/spec/modules-spec.js +++ b/spec/modules-spec.js @@ -57,6 +57,11 @@ describe('Module._nodeModulePaths', function () { path.join(process.resourcesPath, 'node_modules') ]) + modulePath = process.resourcesPath + '-foo' + let nodeModulePaths = Module._nodeModulePaths(modulePath) + assert(nodeModulePaths.includes(path.join(modulePath, 'node_modules'))) + assert(nodeModulePaths.includes(path.join(modulePath, '..', 'node_modules'))) + modulePath = path.join(process.resourcesPath, 'foo') assert.deepEqual(Module._nodeModulePaths(modulePath), [ path.join(process.resourcesPath, 'foo', 'node_modules'), From 905e9e96451e84fab6203ed8d122cae3ed8e8d1e Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jun 2016 15:45:08 -0700 Subject: [PATCH 101/192] Include trailing separator in comparisons --- lib/common/reset-search-paths.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/common/reset-search-paths.js b/lib/common/reset-search-paths.js index 8e217012f582..5cec77b606aa 100644 --- a/lib/common/reset-search-paths.js +++ b/lib/common/reset-search-paths.js @@ -12,10 +12,11 @@ module.parent.paths = [] const originalNodeModulePaths = Module._nodeModulePaths Module._nodeModulePaths = function (from) { const paths = originalNodeModulePaths(from) - const rootPath = process.resourcesPath // If "from" is outside the app then we do nothing. - const skipOutsidePaths = path.resolve(from).startsWith(rootPath) + const rootPath = process.resourcesPath + path.sep + const fromPath = path.resolve(from) + path.sep + const skipOutsidePaths = fromPath.startsWith(rootPath) if (skipOutsidePaths) { return paths.filter(function (candidate) { return candidate.startsWith(rootPath) From bac4d51169b5b8167e90b60bd8107324de459186 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 23 Jun 2016 15:56:29 -0700 Subject: [PATCH 102/192] Reuse root path variable --- lib/common/reset-search-paths.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/common/reset-search-paths.js b/lib/common/reset-search-paths.js index 5cec77b606aa..924cb388d4d9 100644 --- a/lib/common/reset-search-paths.js +++ b/lib/common/reset-search-paths.js @@ -9,21 +9,19 @@ module.paths = [] module.parent.paths = [] // Prevent Node from adding paths outside this app to search paths. +const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep const originalNodeModulePaths = Module._nodeModulePaths Module._nodeModulePaths = function (from) { const paths = originalNodeModulePaths(from) - - // If "from" is outside the app then we do nothing. - const rootPath = process.resourcesPath + path.sep const fromPath = path.resolve(from) + path.sep - const skipOutsidePaths = fromPath.startsWith(rootPath) - if (skipOutsidePaths) { + // If "from" is outside the app then we do nothing. + if (fromPath.startsWith(resourcesPathWithTrailingSlash)) { return paths.filter(function (candidate) { - return candidate.startsWith(rootPath) + return candidate.startsWith(resourcesPathWithTrailingSlash) }) + } else { + return paths } - - return paths } // Patch Module._resolveFilename to always require the Electron API when From 38592aaef77b0b163a8ac9889e1381540616425b Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 24 Jun 2016 10:45:37 +0900 Subject: [PATCH 103/192] Add details to docs/styleguide.md --- docs/styleguide.md | 259 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 198 insertions(+), 61 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index 21d7c63b3455..932a7b01d7af 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -1,66 +1,169 @@ # Electron Documentation Styleguide -Find the appropriate section for your task: [reading Electron documentation](#reading-electron-documentation) -or [writing Electron documentation](#writing-electron-documentation). - -## Writing Electron Documentation - These are the ways that we construct the Electron documentation. -- Maximum one `h1` title per page. -- Use `bash` instead of `cmd` in code blocks (because of syntax highlighter). -- Doc `h1` titles should match object name (i.e. `browser-window` → - `BrowserWindow`). - - Hyphen separated filenames, however, are fine. -- No headers following headers, add at least a one-sentence description. -- Methods headers are wrapped in `code` ticks. -- Event headers are wrapped in single 'quotation' marks. -- No nesting lists more than 2 levels (unfortunately because of markdown +## Titles + +Each page has only one `#`-level title on the top, following chapters in the +same page must have `##`-level titles, and sub-chapters need to increase the +number of `#` in title according to its nesting depth. + +For the page's title, all words must be capitalized, for other chapters, only +the first word has to be capitalized. + +Using `Quick Start` as example: + +```markdown +# Quick Start + +... + +## Main process + +... + +## Renderer process + +... + +## Run your app + +... + +### Run as a distribution + +... + +### Manually downloaded Electron binary + +... +``` + +For API references, there are exceptions to this rule. + +## Markdown rules + +* Use `bash` instead of `cmd` in code blocks (because of syntax highlighter). +* Line length is 80-column wrapped. +* No nesting lists more than 2 levels (unfortunately because of markdown renderer). -- Add section titles: Events, Class Methods and Instance Methods. -- Use 'will' over 'would' when describing outcomes. -- Events and methods are `h3` headers. -- Optional arguments written as `function (required[, optional])`. -- Optional arguments are denoted when called out in list. -- Line length is 80-column wrapped. -- Platform specific methods are noted in italics following method header. - - ```### `method(foo, bar)` _macOS_``` -- Prefer 'in the ___ process' over 'on' -### Documentation Translations +## Picking words -Translations of the Electron docs are located within the `docs-translations` -directory. +* Use "will" over "would" when describing outcomes. +* Prefer "in the ___ process" over "on". -To add another set (or partial set): +## API references -- Create a subdirectory named by language abbreviation. -- Within that subdirectory, duplicate the `docs` directory, keeping the - names of directories and files same. -- Translate the files. -- Update the `README.md` within your language directory to link to the files - you have translated. -- Add a link to your translation directory on the main Electron [README](https://github.com/electron/electron#documentation-translations). +Following rules only apply to documentations of APIs. -## Reading Electron Documentation +### Page title -Here are some tips for understanding Electron documentation syntax. +Each page must use the actual object name returned by `require('electron')` +as name, like `BrowserWindow`, `autoUpdater`, `session`. + +Under the page tile must be a one-line description starting with `>`. + +Using `session` as example: + +```markdown +# session + +> Manage browser sessions, cookies, cache, proxy settings, etc. + +``` + +### Module methods and events + +For modules that are not classes, their methods and events must be listed under +`## Methods` and `## Events` chapters. + +Using `autoUpdater` as example: + +```markdown +# autoUpdater + +## Events + +### Event: 'error' + +## Methods + +### `autoUpdater.setFeedURL(url[, requestHeaders])` +``` + +### Classes + +For APIs that exists as classes, or for classes that are parts of modules, they +must listed under `## Class: TheClassName` chapter. One page can have multiple +classes. + +If the class has constructors, they must be listed with `###`-level titles. + +The methods, events and properties of a class must be listed under +`### Instance Methods`, `### Instance Events` and `Instance Properties` +chapters. + +Using `Session`, `Cookies` classes as example: + +```markdown +# session + +## Methods + +### session.fromPartition(partition) + +## Properties + +### session.defaultSession + +## Class: Session + +### Instance Events + +#### Event: 'will-download' + +### Instance Methods + +#### `ses.getCacheSize(callback)` + +### Instance Properties + +#### `ses.cookies` + +## Class: Cookies + +### Instance Methods + +#### `cookies.get(filter, callback)` +``` ### Methods -An example of [method](https://developer.mozilla.org/en-US/docs/Glossary/Method) -documentation: +The chapter of method must be in the following form: + +```markdown +### `objectName.methodName(required[, optional]))` + +* `required` String +* `optional` Integer (optional) + +... ``` -`methodName(required[, optional]))` -* `required` String (**required**) -* `optional` Integer -``` +The title can be `###` or `####`-levels depending on whether it is a method of +module or class. -The method name is followed by the arguments it takes. Optional arguments are -notated by brackets surrounding the optional argument as well as the comma -required if this optional argument follows another argument. +For modules, the `objectName` is the module's name, for classes, it must be the +name of instance of the class, and must not be same with the module's name. + +For example, the methods of `Session` class under `session` module must use +`ses` as `objectName`. + +The optional arguments are notated by brackets surrounding the optional argument +as well as the comma required if this optional argument follows another +argument. Below the method is more detailed information on each of the arguments. The type of argument is notated by either the common types: @@ -68,35 +171,69 @@ of argument is notated by either the common types: [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object), [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) -or a custom type like Electron's [`webContent`](api/web-content.md). +or a custom type like Electron's [`WebContent`](api/web-content.md). -If an argument is unique to certain platforms, those platforms are denoted -using a space-delimited italicized list following the datatype. Values can be -`macOS`, `Windows`, or `Linux`. +If an argument or a method is unique to certain platforms, those platforms are +denoted using a space-delimited italicized list following the datatype. Values +can be `macOS`, `Windows`, or `Linux`. -``` +``` markdown * `animate` Boolean (optional) _macOS_ _Windows_ ``` +For argument with `Array` type, it must be classified what elements the array +may include in the description below. + +For argument with `Function` type, the description should make it clear how it +may be called and list the types of the arguments that passed to it. + ### Events -An example of [event](https://developer.mozilla.org/en-US/docs/Web/API/Event) -documentation: +The chapter of event must be in following form: -``` -Event: 'wake-up' +```markdown +### Event: 'wake-up' Returns: * `time` String + +... + ``` -The event is a string that is used after a `.on` listener method. If it returns -a value it and its type is noted below. If you were to listen and respond to -this event it might look something like this: +The title can be `###` or `####`-levels depending on whether it is an event of +module or class. + +The arguments of an event have the same rules with methods. + +### Properties + +The chapter of property must be in following form: + +```markdown +### session.defaultSession + +... -```javascript -Alarm.on('wake-up', (time) => { - console.log(time) -}) ``` + +The title can be `###` or `####`-levels depending on whether it is a property of +module or class. + +## Documentation Translations + +Translations of the Electron docs are located within the `docs-translations` +directory. + +To add another set (or partial set): + +* Create a subdirectory named by language abbreviation. +* Translate the files. +* Update the `README.md` within your language directory to link to the files + you have translated. +* Add a link to your translation directory on the main Electron + [README](https://github.com/electron/electron#documentation-translations). + +Note that the files under `docs-translations` must only include the translated +ones, the original English files should not be copied there. From f792b6c752fbe0d8e9b35afc7de6a14aba5dcc41 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 24 Jun 2016 11:21:32 +0900 Subject: [PATCH 104/192] Remove callbackId when callback is removed from registry --- lib/common/api/callbacks-registry.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/common/api/callbacks-registry.js b/lib/common/api/callbacks-registry.js index 7a9bf534f72d..459c392bc188 100644 --- a/lib/common/api/callbacks-registry.js +++ b/lib/common/api/callbacks-registry.js @@ -55,7 +55,11 @@ class CallbacksRegistry { } remove (id) { - return delete this.callbacks[id] + const callback = this.callbacks[id] + if (callback) { + v8Util.deleteHiddenValue(callback, 'callbackId') + delete this.callbacks[id] + } } } From ee28f4fc32c9c859a0e258f3a26813c030757399 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 24 Jun 2016 14:45:31 +0900 Subject: [PATCH 105/192] Handle v8::MicrotasksScope in the main process --- atom/browser/atom_browser_main_parts.cc | 4 ++++ atom/browser/javascript_environment.cc | 9 ++++++++ atom/browser/javascript_environment.h | 3 +++ atom/common/api/event_emitter_caller.cc | 7 ++----- atom/common/native_mate_converters/callback.h | 21 ++++++------------- atom/common/node_bindings.cc | 6 ++---- vendor/native_mate | 2 +- 7 files changed, 27 insertions(+), 25 deletions(-) diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index 75f71d42314f..b933d1073bb6 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -124,6 +124,8 @@ void AtomBrowserMainParts::PostEarlyInitialization() { } void AtomBrowserMainParts::PreMainMessageLoopRun() { + js_env_->OnMessageLoopCreated(); + // Run user's main script before most things get initialized, so we can have // a chance to setup everything. node_bindings_->PrepareMessageLoop(); @@ -169,6 +171,8 @@ void AtomBrowserMainParts::PostMainMessageLoopStart() { void AtomBrowserMainParts::PostMainMessageLoopRun() { brightray::BrowserMainParts::PostMainMessageLoopRun(); + js_env_->OnMessageLoopDestroying(); + #if defined(OS_MACOSX) FreeAppDelegate(); #endif diff --git a/atom/browser/javascript_environment.cc b/atom/browser/javascript_environment.cc index 970132b47c15..0f87a2ca32fd 100644 --- a/atom/browser/javascript_environment.cc +++ b/atom/browser/javascript_environment.cc @@ -7,6 +7,7 @@ #include #include "base/command_line.h" +#include "base/message_loop/message_loop.h" #include "content/public/common/content_switches.h" #include "gin/array_buffer.h" #include "gin/v8_initializer.h" @@ -23,6 +24,14 @@ JavascriptEnvironment::JavascriptEnvironment() context_scope_(v8::Local::New(isolate_, context_)) { } +void JavascriptEnvironment::OnMessageLoopCreated() { + isolate_holder_.AddRunMicrotasksObserver(); +} + +void JavascriptEnvironment::OnMessageLoopDestroying() { + isolate_holder_.RemoveRunMicrotasksObserver(); +} + bool JavascriptEnvironment::Initialize() { auto cmd = base::CommandLine::ForCurrentProcess(); if (cmd->HasSwitch("debug-brk")) { diff --git a/atom/browser/javascript_environment.h b/atom/browser/javascript_environment.h index 07cd602cf00d..1f4d2f453478 100644 --- a/atom/browser/javascript_environment.h +++ b/atom/browser/javascript_environment.h @@ -14,6 +14,9 @@ class JavascriptEnvironment { public: JavascriptEnvironment(); + void OnMessageLoopCreated(); + void OnMessageLoopDestroying(); + v8::Isolate* isolate() const { return isolate_; } v8::Local context() const { return v8::Local::New(isolate_, context_); diff --git a/atom/common/api/event_emitter_caller.cc b/atom/common/api/event_emitter_caller.cc index 271ea705e09c..ac6c9c213f7b 100644 --- a/atom/common/api/event_emitter_caller.cc +++ b/atom/common/api/event_emitter_caller.cc @@ -16,11 +16,8 @@ v8::Local CallEmitWithArgs(v8::Isolate* isolate, v8::Local obj, ValueVector* args) { // Perform microtask checkpoint after running JavaScript. - std::unique_ptr script_scope( - Locker::IsBrowserProcess() ? - nullptr : - new v8::MicrotasksScope(isolate, - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope( + isolate, v8::MicrotasksScope::kRunMicrotasks); // Use node::MakeCallback to call the callback, and it will also run pending // tasks in Node.js. return node::MakeCallback( diff --git a/atom/common/native_mate_converters/callback.h b/atom/common/native_mate_converters/callback.h index 43a1baf25fa8..decc36eb5767 100644 --- a/atom/common/native_mate_converters/callback.h +++ b/atom/common/native_mate_converters/callback.h @@ -48,11 +48,8 @@ struct V8FunctionInvoker(ArgTypes...)> { v8::EscapableHandleScope handle_scope(isolate); if (!function.IsAlive()) return v8::Null(isolate); - std::unique_ptr script_scope( - Locker::IsBrowserProcess() ? - nullptr : - new v8::MicrotasksScope(isolate, - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope(isolate, + v8::MicrotasksScope::kRunMicrotasks); v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); @@ -71,11 +68,8 @@ struct V8FunctionInvoker { v8::HandleScope handle_scope(isolate); if (!function.IsAlive()) return; - std::unique_ptr script_scope( - Locker::IsBrowserProcess() ? - nullptr : - new v8::MicrotasksScope(isolate, - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope(isolate, + v8::MicrotasksScope::kRunMicrotasks); v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); @@ -94,11 +88,8 @@ struct V8FunctionInvoker { ReturnType ret = ReturnType(); if (!function.IsAlive()) return ret; - std::unique_ptr script_scope( - Locker::IsBrowserProcess() ? - nullptr : - new v8::MicrotasksScope(isolate, - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope(isolate, + v8::MicrotasksScope::kRunMicrotasks); v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index ed2ea01675a1..40fa6bdb6e28 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -226,10 +226,8 @@ void NodeBindings::UvRunOnce() { v8::Context::Scope context_scope(env->context()); // Perform microtask checkpoint after running JavaScript. - std::unique_ptr script_scope(is_browser_ ? - nullptr : - new v8::MicrotasksScope(env->isolate(), - v8::MicrotasksScope::kRunMicrotasks)); + v8::MicrotasksScope script_scope(env->isolate(), + v8::MicrotasksScope::kRunMicrotasks); // Deal with uv events. int r = uv_run(uv_loop_, UV_RUN_NOWAIT); diff --git a/vendor/native_mate b/vendor/native_mate index e75f2aa087db..a1efa285204c 160000 --- a/vendor/native_mate +++ b/vendor/native_mate @@ -1 +1 @@ -Subproject commit e75f2aa087db346efc4b530f9e1ce7d3a72a3434 +Subproject commit a1efa285204cb2fbbed450c317fb535a38ea8480 From 0e1caa5961f8bbb7d4b5bd6d6e00d1ca1be2e82a Mon Sep 17 00:00:00 2001 From: Sudar Abisheck Date: Fri, 24 Jun 2016 11:29:55 +0530 Subject: [PATCH 106/192] Update quick-start.md Corrected the anchor text --- docs/tutorial/quick-start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/quick-start.md b/docs/tutorial/quick-start.md index 59bfb4bb26b7..59a019596ba9 100644 --- a/docs/tutorial/quick-start.md +++ b/docs/tutorial/quick-start.md @@ -211,7 +211,7 @@ and then executing the packaged app. ### Try this Example -Clone and run the code in this tutorial by using the [`atom/electron-quick-start`](https://github.com/electron/electron-quick-start) +Clone and run the code in this tutorial by using the [`electron/electron-quick-start`](https://github.com/electron/electron-quick-start) repository. **Note**: Running this requires [Git](https://git-scm.com) and [Node.js](https://nodejs.org/en/download/) (which includes [npm](https://npmjs.org)) on your system. From dfc2f3f0c64d750de6ca0467ee40fb5630933fc8 Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Fri, 24 Jun 2016 17:51:31 +0200 Subject: [PATCH 107/192] Fix uninitialized member variable in CrashReporterWin --- atom/common/crash_reporter/crash_reporter_win.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/atom/common/crash_reporter/crash_reporter_win.cc b/atom/common/crash_reporter/crash_reporter_win.cc index 4264f6af9db4..7ce61d69719c 100644 --- a/atom/common/crash_reporter/crash_reporter_win.cc +++ b/atom/common/crash_reporter/crash_reporter_win.cc @@ -137,7 +137,8 @@ void UnregisterNonABICompliantCodeRange(void* start) { } // namespace -CrashReporterWin::CrashReporterWin() { +CrashReporterWin::CrashReporterWin() + : skip_system_crash_handler_(false) { } CrashReporterWin::~CrashReporterWin() { From 5dd7bf67e7792cfecd428102d3965885774e5b3f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 12:28:41 -0700 Subject: [PATCH 108/192] Minor formatting and wording tweaks --- docs/styleguide.md | 67 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index 932a7b01d7af..34d20df1bba2 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -1,12 +1,12 @@ # Electron Documentation Styleguide -These are the ways that we construct the Electron documentation. +These are the guidelines for writing Electron documentation. ## Titles Each page has only one `#`-level title on the top, following chapters in the same page must have `##`-level titles, and sub-chapters need to increase the -number of `#` in title according to its nesting depth. +number of `#` in the title according to its nesting depth. For the page's title, all words must be capitalized, for other chapters, only the first word has to be capitalized. @@ -43,10 +43,9 @@ For API references, there are exceptions to this rule. ## Markdown rules -* Use `bash` instead of `cmd` in code blocks (because of syntax highlighter). -* Line length is 80-column wrapped. -* No nesting lists more than 2 levels (unfortunately because of markdown - renderer). +* Use `bash` instead of `cmd` in code blocks (due to the syntax highlighter). +* Lines should be wrapped at 80 columns. +* No nesting lists more than 2 levels (due to the markdown renderer). ## Picking words @@ -55,12 +54,12 @@ For API references, there are exceptions to this rule. ## API references -Following rules only apply to documentations of APIs. +The following rules only apply to the documentation of APIs. ### Page title Each page must use the actual object name returned by `require('electron')` -as name, like `BrowserWindow`, `autoUpdater`, `session`. +as the title, such as `BrowserWindow`, `autoUpdater`, `session`, etc. Under the page tile must be a one-line description starting with `>`. @@ -78,7 +77,7 @@ Using `session` as example: For modules that are not classes, their methods and events must be listed under `## Methods` and `## Events` chapters. -Using `autoUpdater` as example: +Using `autoUpdater` as an example: ```markdown # autoUpdater @@ -94,17 +93,17 @@ Using `autoUpdater` as example: ### Classes -For APIs that exists as classes, or for classes that are parts of modules, they -must listed under `## Class: TheClassName` chapter. One page can have multiple +For APIs that exists as classes, or for classes that are part of modules, they +must listed under a `## Class: TheClassName` chapter. One page can have multiple classes. If the class has constructors, they must be listed with `###`-level titles. The methods, events and properties of a class must be listed under -`### Instance Methods`, `### Instance Events` and `Instance Properties` +`### Instance Methods`, `### Instance Events`, and `Instance Properties` chapters. -Using `Session`, `Cookies` classes as example: +Using `Session`, `Cookies` classes as an example: ```markdown # session @@ -140,7 +139,7 @@ Using `Session`, `Cookies` classes as example: ### Methods -The chapter of method must be in the following form: +The methods chapter must be in the following form: ```markdown ### `objectName.methodName(required[, optional]))` @@ -153,13 +152,14 @@ The chapter of method must be in the following form: ``` The title can be `###` or `####`-levels depending on whether it is a method of -module or class. +a module or a class. For modules, the `objectName` is the module's name, for classes, it must be the -name of instance of the class, and must not be same with the module's name. +name of the instance of the class, and must not be the same as the module's +name. -For example, the methods of `Session` class under `session` module must use -`ses` as `objectName`. +For example, the methods of the `Session` class under the `session` module must +use `ses` as the `objectName`. The optional arguments are notated by brackets surrounding the optional argument as well as the comma required if this optional argument follows another @@ -167,29 +167,30 @@ argument. Below the method is more detailed information on each of the arguments. The type of argument is notated by either the common types: -[`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), -[`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), -[`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object), -[`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) -or a custom type like Electron's [`WebContent`](api/web-content.md). + +- [`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) +- [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) +- [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) +- [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) +- Or a custom type like Electron's [`WebContent`](api/web-content.md) If an argument or a method is unique to certain platforms, those platforms are denoted using a space-delimited italicized list following the datatype. Values can be `macOS`, `Windows`, or `Linux`. -``` markdown +```markdown * `animate` Boolean (optional) _macOS_ _Windows_ ``` -For argument with `Array` type, it must be classified what elements the array -may include in the description below. +For any argument with `Array` type, it must be classified what elements the +array may include in the description below. -For argument with `Function` type, the description should make it clear how it -may be called and list the types of the arguments that passed to it. +For any argument with `Function` type, the description should make it clear how +it may be called and list the types of the arguments that will be passed to it. ### Events -The chapter of event must be in following form: +The events chapter must be in following form: ```markdown ### Event: 'wake-up' @@ -203,13 +204,13 @@ Returns: ``` The title can be `###` or `####`-levels depending on whether it is an event of -module or class. +a module or a class. -The arguments of an event have the same rules with methods. +The arguments of an event have the same rules as methods. ### Properties -The chapter of property must be in following form: +The properties chapter must be in following form: ```markdown ### session.defaultSession @@ -219,7 +220,7 @@ The chapter of property must be in following form: ``` The title can be `###` or `####`-levels depending on whether it is a property of -module or class. +a module or a class. ## Documentation Translations From 47504c5a53d21c2e4961fd3dca29c97bc6dc9bf8 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 12:40:55 -0700 Subject: [PATCH 109/192] Use more lists --- docs/styleguide.md | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index 34d20df1bba2..0d93df5e6d89 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -4,12 +4,12 @@ These are the guidelines for writing Electron documentation. ## Titles -Each page has only one `#`-level title on the top, following chapters in the -same page must have `##`-level titles, and sub-chapters need to increase the -number of `#` in the title according to its nesting depth. - -For the page's title, all words must be capitalized, for other chapters, only -the first word has to be capitalized. +* Each page must have a single `#`-level title at the top. +* Chapters in the same page must have `##`-level titles. +* Sub-chapters need to increase the number of `#` in the title according to its + nesting depth. +* All words in the page's title must be capitalized. +* Only the first word of a chapter title must be capitalized. Using `Quick Start` as example: @@ -59,7 +59,7 @@ The following rules only apply to the documentation of APIs. ### Page title Each page must use the actual object name returned by `require('electron')` -as the title, such as `BrowserWindow`, `autoUpdater`, `session`, etc. +as the title, such as `BrowserWindow`, `autoUpdater`, and `session`. Under the page tile must be a one-line description starting with `>`. @@ -75,7 +75,7 @@ Using `session` as example: ### Module methods and events For modules that are not classes, their methods and events must be listed under -`## Methods` and `## Events` chapters. +the `## Methods` and `## Events` chapters. Using `autoUpdater` as an example: @@ -93,17 +93,15 @@ Using `autoUpdater` as an example: ### Classes -For APIs that exists as classes, or for classes that are part of modules, they -must listed under a `## Class: TheClassName` chapter. One page can have multiple -classes. +- API classes or classes that are part of modules must be listed under a + `## Class: TheClassName` chapter. +- One page can have multiple classes. +- The constructors must be listed with `###`-level titles. +- The methods must be listed under a `### Instance Methods` chapter. +- The events must be listed under a `### Instance Events` chapter. +- The properties must be listed under a `Instance Properties` chapter. -If the class has constructors, they must be listed with `###`-level titles. - -The methods, events and properties of a class must be listed under -`### Instance Methods`, `### Instance Events`, and `Instance Properties` -chapters. - -Using `Session`, `Cookies` classes as an example: +Using the `Session` and `Cookies` classes as an example: ```markdown # session @@ -182,11 +180,11 @@ can be `macOS`, `Windows`, or `Linux`. * `animate` Boolean (optional) _macOS_ _Windows_ ``` -For any argument with `Array` type, it must be classified what elements the -array may include in the description below. +`Array` type arguments must be classified what elements the array may include in +the description below. -For any argument with `Function` type, the description should make it clear how -it may be called and list the types of the arguments that will be passed to it. +The description for `Function` type arguments should make it clear how it may be +called and list the types of the parameters that will be passed to it. ### Events @@ -206,7 +204,7 @@ Returns: The title can be `###` or `####`-levels depending on whether it is an event of a module or a class. -The arguments of an event have the same rules as methods. +The arguments of an event follow the same rules as methods. ### Properties From 63e2ee8e105df6d0254220d3542481b8897a81c0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 12:45:22 -0700 Subject: [PATCH 110/192] Add link to Boolean --- docs/styleguide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/styleguide.md b/docs/styleguide.md index 0d93df5e6d89..21af8a0fe02e 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -170,6 +170,7 @@ of argument is notated by either the common types: - [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) - [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) - [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) +- [`Boolean`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean) - Or a custom type like Electron's [`WebContent`](api/web-content.md) If an argument or a method is unique to certain platforms, those platforms are From 271808b2780feb0872e29bc8ac47457cb671fc5d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:08:12 -0700 Subject: [PATCH 111/192] Support reading/writing bookmarks to clipboard --- atom/common/api/atom_api_clipboard.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/atom/common/api/atom_api_clipboard.cc b/atom/common/api/atom_api_clipboard.cc index cb413800be77..bda3b0c38c57 100644 --- a/atom/common/api/atom_api_clipboard.cc +++ b/atom/common/api/atom_api_clipboard.cc @@ -122,6 +122,23 @@ void WriteHtml(const base::string16& html, mate::Arguments* args) { writer.WriteHTML(html, std::string()); } +v8::Local ReadBookmark(mate::Arguments* args) { + base::string16 title; + std::string url; + mate::Dictionary dict = mate::Dictionary::CreateEmpty(args->isolate()); + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); + clipboard->ReadBookmark(&title, &url); + dict.Set("title", title); + dict.Set("url", url); + return dict.GetHandle(); +} + +void WriteBookmark(const base::string16& title, const std::string& url, + mate::Arguments* args) { + ui::ScopedClipboardWriter writer(GetClipboardType(args)); + writer.WriteBookmark(title, url); +} + gfx::Image ReadImage(mate::Arguments* args) { ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); SkBitmap bitmap = clipboard->ReadImage(GetClipboardType(args)); @@ -150,6 +167,8 @@ void Initialize(v8::Local exports, v8::Local unused, dict.SetMethod("writeRTF", &WriteRtf); dict.SetMethod("readHTML", &ReadHtml); dict.SetMethod("writeHTML", &WriteHtml); + dict.SetMethod("readBookmark", &ReadBookmark); + dict.SetMethod("writeBookmark", &WriteBookmark); dict.SetMethod("readImage", &ReadImage); dict.SetMethod("writeImage", &WriteImage); dict.SetMethod("clear", &Clear); From 358bf1bf69104e38cfd0cf7f7d893c7b003dcd95 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:10:32 -0700 Subject: [PATCH 112/192] Add read/writeBookmark spec --- spec/api-clipboard-spec.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/api-clipboard-spec.js b/spec/api-clipboard-spec.js index 344e01452fba..1c3682ba2b9f 100644 --- a/spec/api-clipboard-spec.js +++ b/spec/api-clipboard-spec.js @@ -41,6 +41,16 @@ describe('clipboard module', function () { }) }) + describe('clipboard.readBookmark', function () { + it('returns title and url', function () { + clipboard.writeBookmark('a title', 'http://electron.atom.io') + assert.deepEqual(clipboard.readBookmark(), { + title: 'a title', + url: 'https://electron.atom.io' + }) + }) + }) + describe('clipboard.write()', function () { it('returns data correctly', function () { var text = 'test' From e802d0e4a098d97a91ef247f395e9fa0ca1551c7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:14:28 -0700 Subject: [PATCH 113/192] Add bookmark key to clipboard.write --- atom/common/api/atom_api_clipboard.cc | 8 ++++++-- spec/api-clipboard-spec.js | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/atom/common/api/atom_api_clipboard.cc b/atom/common/api/atom_api_clipboard.cc index bda3b0c38c57..01e670be9261 100644 --- a/atom/common/api/atom_api_clipboard.cc +++ b/atom/common/api/atom_api_clipboard.cc @@ -54,12 +54,16 @@ std::string Read(const std::string& format_string, void Write(const mate::Dictionary& data, mate::Arguments* args) { ui::ScopedClipboardWriter writer(GetClipboardType(args)); - base::string16 text, html; + base::string16 text, html, bookmark; gfx::Image image; - if (data.Get("text", &text)) + if (data.Get("text", &text)) { writer.WriteText(text); + if (data.Get("bookmark", &bookmark)) + writer.WriteBookmark(bookmark, base::UTF16ToUTF8(text)); + } + if (data.Get("rtf", &text)) { std::string rtf = base::UTF16ToUTF8(text); writer.WriteRTF(rtf); diff --git a/spec/api-clipboard-spec.js b/spec/api-clipboard-spec.js index 1c3682ba2b9f..3915b550e241 100644 --- a/spec/api-clipboard-spec.js +++ b/spec/api-clipboard-spec.js @@ -58,16 +58,19 @@ describe('clipboard module', function () { var p = path.join(fixtures, 'assets', 'logo.png') var i = nativeImage.createFromPath(p) var markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi' + var bookmark = {title: 'a title', url: 'test'} clipboard.write({ text: 'test', html: 'Hi', rtf: '{\\rtf1\\utf8 text}', + bookmark: 'a title', image: p }) assert.equal(clipboard.readText(), text) assert.equal(clipboard.readHTML(), markup) assert.equal(clipboard.readRTF(), rtf) assert.equal(clipboard.readImage().toDataURL(), i.toDataURL()) + assert.deepEqual(clipboard.readBookmark(), bookmark) }) }) }) From 9fab641ecb13a94177e1eb4ea30761757f7647a7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:16:05 -0700 Subject: [PATCH 114/192] Add missing rtf key --- docs/api/clipboard.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index c2a16a79b9fb..47fd0564ac98 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -111,6 +111,7 @@ Reads `data` from the clipboard. * `text` String * `html` String * `image` [NativeImage](native-image.md) + * `rtf` String * `type` String (optional) ```javascript From a2ba778adcf6b776bc7122ba174a611c498990c2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:20:49 -0700 Subject: [PATCH 115/192] Document clipboard bookmark support --- docs/api/clipboard.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index 47fd0564ac98..ecd73a71ccb2 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -75,6 +75,21 @@ Returns the content in the clipboard as RTF. Writes the `text` into the clipboard in RTF. +### `clipboard.readBookmark([type])` + +* `type` String (optional) + +Returns an Object containing `title` and `url` string keys representing the +bookmark in clipboard. + +### `clipboard.writeBookmark(title, url[, type])` + +* `title` String +* `url` String +* `type` String (optional) + +Writes the `title` and `url` into the clipboard as a bookmark. + ### `clipboard.clear([type])` * `type` String (optional) @@ -112,6 +127,7 @@ Reads `data` from the clipboard. * `html` String * `image` [NativeImage](native-image.md) * `rtf` String + * `bookmark` String - The title of the url at `text`. * `type` String (optional) ```javascript From 30a628b46f0e6932ee164988cf38e41e7c02c8f1 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:29:39 -0700 Subject: [PATCH 116/192] Remove unsupported type --- docs/api/clipboard.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index ecd73a71ccb2..9ac11706b944 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -75,9 +75,7 @@ Returns the content in the clipboard as RTF. Writes the `text` into the clipboard in RTF. -### `clipboard.readBookmark([type])` - -* `type` String (optional) +### `clipboard.readBookmark()` Returns an Object containing `title` and `url` string keys representing the bookmark in clipboard. From 6588b85ac951cf6ed24d89efdfaf5d5857c8324d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:31:15 -0700 Subject: [PATCH 117/192] Mention empty strings --- docs/api/clipboard.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index 9ac11706b944..a8c4605efa9c 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -78,7 +78,8 @@ Writes the `text` into the clipboard in RTF. ### `clipboard.readBookmark()` Returns an Object containing `title` and `url` string keys representing the -bookmark in clipboard. +bookmark in the clipboard. The `title` and `url` values will be empty strings +when the bookmark is unavailable. ### `clipboard.writeBookmark(title, url[, type])` From 5fbba2d27b98df0fd34deea563e85729383d3b43 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:32:52 -0700 Subject: [PATCH 118/192] Add assertions for no available bookmark --- spec/api-clipboard-spec.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/api-clipboard-spec.js b/spec/api-clipboard-spec.js index 3915b550e241..5116bd80c421 100644 --- a/spec/api-clipboard-spec.js +++ b/spec/api-clipboard-spec.js @@ -46,7 +46,13 @@ describe('clipboard module', function () { clipboard.writeBookmark('a title', 'http://electron.atom.io') assert.deepEqual(clipboard.readBookmark(), { title: 'a title', - url: 'https://electron.atom.io' + url: 'http://electron.atom.io' + }) + + clipboard.writeText('no bookmark') + assert.deepEqual(clipboard.readBookmark(), { + title: '', + url: '' }) }) }) From 9479536bb8cc8d15e043de0b07eee3d5d83b141d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 15:35:30 -0700 Subject: [PATCH 119/192] Tweak return value description --- docs/api/clipboard.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index a8c4605efa9c..c4ae96c4b1c6 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -77,9 +77,9 @@ Writes the `text` into the clipboard in RTF. ### `clipboard.readBookmark()` -Returns an Object containing `title` and `url` string keys representing the -bookmark in the clipboard. The `title` and `url` values will be empty strings -when the bookmark is unavailable. +Returns an Object containing `title` and `url` keys representing the bookmark in +the clipboard. The `title` and `url` values will be empty strings when the +bookmark is unavailable. ### `clipboard.writeBookmark(title, url[, type])` From aee111bf2f372afe98c5643a2a916f40e7187701 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Fri, 24 Jun 2016 15:59:30 -0700 Subject: [PATCH 120/192] minor wording changes and tweaks --- docs/styleguide.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index 21af8a0fe02e..e0080ba08cb2 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -6,7 +6,7 @@ These are the guidelines for writing Electron documentation. * Each page must have a single `#`-level title at the top. * Chapters in the same page must have `##`-level titles. -* Sub-chapters need to increase the number of `#` in the title according to its +* Sub-chapters need to increase the number of `#` in the title according to their nesting depth. * All words in the page's title must be capitalized. * Only the first word of a chapter title must be capitalized. @@ -69,7 +69,6 @@ Using `session` as example: # session > Manage browser sessions, cookies, cache, proxy settings, etc. - ``` ### Module methods and events @@ -97,9 +96,9 @@ Using `autoUpdater` as an example: `## Class: TheClassName` chapter. - One page can have multiple classes. - The constructors must be listed with `###`-level titles. -- The methods must be listed under a `### Instance Methods` chapter. -- The events must be listed under a `### Instance Events` chapter. -- The properties must be listed under a `Instance Properties` chapter. +- The methods must be listed under an `### Instance Methods` chapter. +- The events must be listed under an `### Instance Events` chapter. +- The properties must be listed under an `### Instance Properties` chapter. Using the `Session` and `Cookies` classes as an example: @@ -146,22 +145,25 @@ The methods chapter must be in the following form: * `optional` Integer (optional) ... - ``` The title can be `###` or `####`-levels depending on whether it is a method of a module or a class. -For modules, the `objectName` is the module's name, for classes, it must be the +For modules, the `objectName` is the module's name. For classes, it must be the name of the instance of the class, and must not be the same as the module's name. For example, the methods of the `Session` class under the `session` module must use `ses` as the `objectName`. -The optional arguments are notated by brackets surrounding the optional argument +The optional arguments are notated by square brackets `[]` surrounding the optional argument as well as the comma required if this optional argument follows another -argument. +argument: + +``` +required[, optional] +``` Below the method is more detailed information on each of the arguments. The type of argument is notated by either the common types: @@ -181,7 +183,7 @@ can be `macOS`, `Windows`, or `Linux`. * `animate` Boolean (optional) _macOS_ _Windows_ ``` -`Array` type arguments must be classified what elements the array may include in +`Array` type arguments must specify what elements the array may include in the description below. The description for `Function` type arguments should make it clear how it may be @@ -199,7 +201,6 @@ Returns: * `time` String ... - ``` The title can be `###` or `####`-levels depending on whether it is an event of @@ -215,7 +216,6 @@ The properties chapter must be in following form: ### session.defaultSession ... - ``` The title can be `###` or `####`-levels depending on whether it is a property of From f90ef128145600899427ef8c5629fa0cd794232d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 24 Jun 2016 17:16:38 -0700 Subject: [PATCH 121/192] read/writeBookmark is not implemented on Linux --- docs/api/clipboard.md | 4 ++-- spec/api-clipboard-spec.js | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/api/clipboard.md b/docs/api/clipboard.md index c4ae96c4b1c6..85712bc651d9 100644 --- a/docs/api/clipboard.md +++ b/docs/api/clipboard.md @@ -75,13 +75,13 @@ Returns the content in the clipboard as RTF. Writes the `text` into the clipboard in RTF. -### `clipboard.readBookmark()` +### `clipboard.readBookmark()` _macOS_ _Windows_ Returns an Object containing `title` and `url` keys representing the bookmark in the clipboard. The `title` and `url` values will be empty strings when the bookmark is unavailable. -### `clipboard.writeBookmark(title, url[, type])` +### `clipboard.writeBookmark(title, url[, type])` _macOS_ _Windows_ * `title` String * `url` String diff --git a/spec/api-clipboard-spec.js b/spec/api-clipboard-spec.js index 5116bd80c421..25b65b4784d0 100644 --- a/spec/api-clipboard-spec.js +++ b/spec/api-clipboard-spec.js @@ -43,6 +43,8 @@ describe('clipboard module', function () { describe('clipboard.readBookmark', function () { it('returns title and url', function () { + if (process.platform === 'linux') return + clipboard.writeBookmark('a title', 'http://electron.atom.io') assert.deepEqual(clipboard.readBookmark(), { title: 'a title', @@ -76,7 +78,10 @@ describe('clipboard module', function () { assert.equal(clipboard.readHTML(), markup) assert.equal(clipboard.readRTF(), rtf) assert.equal(clipboard.readImage().toDataURL(), i.toDataURL()) - assert.deepEqual(clipboard.readBookmark(), bookmark) + + if (process.platform !== 'linux') { + assert.deepEqual(clipboard.readBookmark(), bookmark) + } }) }) }) From 4d55175649f1fb8d90349d2941997ede547995d8 Mon Sep 17 00:00:00 2001 From: Florent Delayen Date: Sat, 25 Jun 2016 16:51:43 +0200 Subject: [PATCH 122/192] Execute click handlers for top level menu items --- atom/browser/ui/views/menu_bar.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/atom/browser/ui/views/menu_bar.cc b/atom/browser/ui/views/menu_bar.cc index 796e6ef80457..0538421bfe64 100644 --- a/atom/browser/ui/views/menu_bar.cc +++ b/atom/browser/ui/views/menu_bar.cc @@ -145,8 +145,10 @@ void MenuBar::OnMenuButtonClicked(views::MenuButton* source, int id = source->tag(); ui::MenuModel::ItemType type = menu_model_->GetTypeAt(id); - if (type != ui::MenuModel::TYPE_SUBMENU) + if (type != ui::MenuModel::TYPE_SUBMENU) { + menu_model_->ActivatedAt(id, 0); return; + } MenuDelegate menu_delegate(this); menu_delegate.RunMenu(menu_model_->GetSubmenuModelAt(id), source); From e1ad64013ecbaf1f4a3f957a37ca2fdb8ab14c02 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 26 Jun 2016 11:46:40 +0900 Subject: [PATCH 123/192] Cleanup code of beginFrameSubscription --- atom/browser/api/atom_api_web_contents.cc | 11 +++++------ atom/browser/api/frame_subscriber.cc | 16 ++++++++++------ atom/browser/api/frame_subscriber.h | 7 ++++--- docs/api/web-contents.md | 2 +- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index f19ac34a5d64..e6959c887111 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1181,15 +1181,14 @@ void WebContents::SendInputEvent(v8::Isolate* isolate, isolate, "Invalid event object"))); } -void WebContents::BeginFrameSubscription( - mate::Arguments* args) { - FrameSubscriber::FrameCaptureCallback callback; +void WebContents::BeginFrameSubscription(mate::Arguments* args) { bool only_dirty = false; + FrameSubscriber::FrameCaptureCallback callback; + args->GetNext(&only_dirty); if (!args->GetNext(&callback)) { - args->GetNext(&only_dirty); - if (!args->GetNext(&callback)) - args->ThrowTypeError("'callback' must be defined"); + args->ThrowError(); + return; } const auto view = web_contents()->GetRenderWidgetHostView(); diff --git a/atom/browser/api/frame_subscriber.cc b/atom/browser/api/frame_subscriber.cc index 91b6765db189..d6d2cf759e14 100644 --- a/atom/browser/api/frame_subscriber.cc +++ b/atom/browser/api/frame_subscriber.cc @@ -5,8 +5,8 @@ #include "atom/browser/api/frame_subscriber.h" #include "base/bind.h" -#include "atom/common/node_includes.h" #include "atom/common/native_mate_converters/gfx_converter.h" +#include "atom/common/node_includes.h" #include "content/public/browser/render_widget_host.h" namespace atom { @@ -17,8 +17,11 @@ FrameSubscriber::FrameSubscriber(v8::Isolate* isolate, content::RenderWidgetHostView* view, const FrameCaptureCallback& callback, bool only_dirty) - : isolate_(isolate), view_(view), callback_(callback), - only_dirty_(only_dirty), weak_factory_(this) { + : isolate_(isolate), + view_(view), + callback_(callback), + only_dirty_(only_dirty), + weak_factory_(this) { } bool FrameSubscriber::ShouldCaptureFrame( @@ -48,8 +51,9 @@ bool FrameSubscriber::ShouldCaptureFrame( } void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback, - const gfx::Rect& damage_rect, const SkBitmap& bitmap, - content::ReadbackResponse response) { + const gfx::Rect& damage_rect, + const SkBitmap& bitmap, + content::ReadbackResponse response) { if (response != content::ReadbackResponse::READBACK_SUCCESS) return; @@ -67,7 +71,7 @@ void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback, rgb_arr_size); v8::Local damage = - mate::Converter::ToV8(isolate_, damage_rect); + mate::Converter::ToV8(isolate_, damage_rect); callback_.Run(buffer.ToLocalChecked(), damage); } diff --git a/atom/browser/api/frame_subscriber.h b/atom/browser/api/frame_subscriber.h index b6cff3da81db..0da554ac4795 100644 --- a/atom/browser/api/frame_subscriber.h +++ b/atom/browser/api/frame_subscriber.h @@ -21,7 +21,7 @@ namespace api { class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { public: using FrameCaptureCallback = - base::Callback, v8::Local)>; + base::Callback, v8::Local)>; FrameSubscriber(v8::Isolate* isolate, content::RenderWidgetHostView* view, @@ -35,8 +35,9 @@ class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber { private: void OnFrameDelivered(const FrameCaptureCallback& callback, - const gfx::Rect& damage_rect, const SkBitmap& bitmap, - content::ReadbackResponse response); + const gfx::Rect& damage_rect, + const SkBitmap& bitmap, + content::ReadbackResponse response); v8::Isolate* isolate_; content::RenderWidgetHostView* view_; diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index c25b640f91e5..913504cd3745 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -921,7 +921,7 @@ For the `mouseWheel` event, the `event` object also have following properties: ### `webContents.beginFrameSubscription([onlyDirty ,]callback)` -* `onlyDirty` Boolean +* `onlyDirty` Boolean (optional) - Defaults to `false` * `callback` Function Begin subscribing for presentation events and captured frames, the `callback` From 090c8b89bb6e98a5e6d6e1968d5b8eef0cca04d8 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 26 Jun 2016 11:53:58 +0900 Subject: [PATCH 124/192] spec: Make beginFrameSubscription test more reliable --- spec/api-browser-window-spec.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index b5f55905589a..25b852946ff4 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -44,7 +44,10 @@ describe('browser-window module', function () { w = new BrowserWindow({ show: false, width: 400, - height: 400 + height: 400, + webPreferences: { + backgroundThrottling: false + } }) }) @@ -603,12 +606,20 @@ describe('browser-window module', function () { }) describe('beginFrameSubscription method', function () { - it('subscribes to frame updates', function (done) { - this.timeout(20000) + // This test is too slow, only test it on CI. + if (!isCI) return + this.timeout(20000) + + it('subscribes to frame updates', function (done) { + let called = false w.loadURL('file://' + fixtures + '/api/frame-subscriber.html') w.webContents.on('dom-ready', function () { w.webContents.beginFrameSubscription(function (data) { + // This callback might be called twice. + if (called) return + called = true + assert.notEqual(data.length, 0) w.webContents.endFrameSubscription() done() @@ -617,11 +628,14 @@ describe('browser-window module', function () { }) it('subscribes to frame updates (only dirty rectangle)', function (done) { - this.timeout(20000) - + let called = false w.loadURL('file://' + fixtures + '/api/frame-subscriber.html') w.webContents.on('dom-ready', function () { w.webContents.beginFrameSubscription(true, function (data) { + // This callback might be called twice. + if (called) return + called = true + assert.notEqual(data.length, 0) w.webContents.endFrameSubscription() done() @@ -630,10 +644,8 @@ describe('browser-window module', function () { }) it('throws error when subscriber is not well defined', function (done) { - this.timeout(20000) - w.loadURL('file://' + fixtures + '/api/frame-subscriber.html') - try{ + try { w.webContents.beginFrameSubscription(true, true) } catch(e) { done() From 4e747b56b03bf47ab1f353e7222bd8c4ab64985c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 26 Jun 2016 13:36:05 +0900 Subject: [PATCH 125/192] Update docs-translations/zh-CN/api/shell.md Close #6099. --- docs-translations/zh-CN/api/shell.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-translations/zh-CN/api/shell.md b/docs-translations/zh-CN/api/shell.md index a30334dcdc9c..17ebe5511a37 100644 --- a/docs-translations/zh-CN/api/shell.md +++ b/docs-translations/zh-CN/api/shell.md @@ -6,7 +6,7 @@ 在用户默认浏览器中打开URL的示例: ```javascript -var shell = require('shell'); +const {shell} = require('electron'); shell.openExternal('https://github.com'); ``` From e457207b20f6e5c330bb0218f45503eb42b4bc0b Mon Sep 17 00:00:00 2001 From: Emerson Thompson Date: Mon, 27 Jun 2016 20:19:46 -0300 Subject: [PATCH 126/192] Portuguese error --- docs-translations/pt-BR/api/auto-updater.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-translations/pt-BR/api/auto-updater.md b/docs-translations/pt-BR/api/auto-updater.md index f7c49db8ff50..ff4db562a0c9 100644 --- a/docs-translations/pt-BR/api/auto-updater.md +++ b/docs-translations/pt-BR/api/auto-updater.md @@ -40,7 +40,7 @@ Emitido quando está verificando se uma atualização foi inicializada. ### Evento: 'update-available' -Emitido quando há uma atualização disponível. A autalização é baixada automaticamente. +Emitido quando há uma atualização disponível. A atualização é baixada automaticamente. ### Evento: 'update-not-available' From 3da41e03aadc26b519fc31b9f5a3ab9cb0063f82 Mon Sep 17 00:00:00 2001 From: Emerson Thompson Date: Mon, 27 Jun 2016 20:27:09 -0300 Subject: [PATCH 127/192] broken link (404) Broken link (404) to DesktopCapturer --- docs-translations/pt-BR/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-translations/pt-BR/README.md b/docs-translations/pt-BR/README.md index 8498ca420fc8..ba6abc39aa0f 100644 --- a/docs-translations/pt-BR/README.md +++ b/docs-translations/pt-BR/README.md @@ -64,7 +64,7 @@ Existem muitas perguntas comuns que são feitas, verifique antes de criar uma is ### Módulos para o Processo Renderizador: -* [DesktopCapturer](../../docs/api/desktop-capturer) +* [DesktopCapturer](../../docs/api/desktop-capturer.md) * [ipcRenderer](../../docs/api/ipc-renderer.md) * [remote](../../docs/api/remote.md) * [webFrame](../../docs/api/web-frame.md) From 393829b6745589c5647da52b52b4e5d4be85f112 Mon Sep 17 00:00:00 2001 From: Emerson Thompson Date: Mon, 27 Jun 2016 20:48:58 -0300 Subject: [PATCH 128/192] Translated: power-monitor.md --- docs-translations/pt-BR/api/power-monitor.md | 35 ++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs-translations/pt-BR/api/power-monitor.md diff --git a/docs-translations/pt-BR/api/power-monitor.md b/docs-translations/pt-BR/api/power-monitor.md new file mode 100644 index 000000000000..007560129354 --- /dev/null +++ b/docs-translations/pt-BR/api/power-monitor.md @@ -0,0 +1,35 @@ +# powerMonitor + +> Monitorar as mudanças de estado de energia. + +Você só pode usá-lo no processo principal. Você não deve usar este módulo até que o evento `ready` do modulo `app` seja emitido. + +Por exemplo: + +```javascript +app.on('ready', () => { + require('electron').powerMonitor.on('suspend', () => { + console.log('O sistema está indo dormir'); + }); +}); +``` + +## Eventos + +O módulo `power-monitor` emite os seguintes eventos: + +### Evento: 'suspend' + +Emitido quando o sistema está a suspender. + +### Evento: 'resume' + +Emitido quando o sistema está a retomar. + +### Evento: 'on-ac' _Windows_ + +Emitido quando o sistema muda para energia AC. + +### Evento: 'on-battery' _Windows_ + +Emitido quando o sistema muda para a energia da bateria. From d6ab3534387e4be2cea0309663f2586941f2f61c Mon Sep 17 00:00:00 2001 From: Ian Ornelas Date: Tue, 28 Jun 2016 06:02:25 -0300 Subject: [PATCH 129/192] Add chrome.storage.local --- lib/renderer/extensions/storage.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/renderer/extensions/storage.js b/lib/renderer/extensions/storage.js index 7b880f08310e..b0941d98c8ce 100644 --- a/lib/renderer/extensions/storage.js +++ b/lib/renderer/extensions/storage.js @@ -1,5 +1,5 @@ -const getStorage = () => { - const data = window.localStorage.getItem('__chrome.storage.sync__') +const getStorage = (storageType) => { + const data = window.localStorage.getItem(`__chrome.storage.${storageType}__`) if (data != null) { return JSON.parse(data) } else { @@ -7,15 +7,15 @@ const getStorage = () => { } } -const setStorage = (storage) => { +const setStorage = (storageType, storage) => { const json = JSON.stringify(storage) - window.localStorage.setItem('__chrome.storage.sync__', json) + window.localStorage.setItem(`__chrome.storage.${storageType}__`, json) } -module.exports = { - sync: { +const getStorageManager = (storageType) => { + return { get (keys, callback) { - const storage = getStorage() + const storage = getStorage(storageType) if (keys == null) return storage let defaults = {} @@ -45,15 +45,20 @@ module.exports = { }, set (items, callback) { - const storage = getStorage() + const storage = getStorage(storageType) Object.keys(items).forEach(function (name) { storage[name] = items[name] }) - setStorage(storage) + setStorage(storageType, storage) setTimeout(callback) } } } + +module.exports = { + sync: getStorageManager('sync'), + local: getStorageManager('local') +} From 918f25b76c194325c9d3a0958fff8c142c2e91a7 Mon Sep 17 00:00:00 2001 From: Emerson Thompson Date: Tue, 28 Jun 2016 09:22:53 -0300 Subject: [PATCH 130/192] link correction --- docs-translations/pt-BR/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-translations/pt-BR/README.md b/docs-translations/pt-BR/README.md index ba6abc39aa0f..0c442ef89d5c 100644 --- a/docs-translations/pt-BR/README.md +++ b/docs-translations/pt-BR/README.md @@ -52,7 +52,7 @@ Existem muitas perguntas comuns que são feitas, verifique antes de criar uma is * [contentTracing](../../docs/api/content-tracing.md) * [dialog](../../docs/api/dialog.md) * [globalShortcut](../../docs/api/global-shortcut.md) -* [ipcMain](../../docs/api/ipc-main-process.md) +* [ipcMain](../../docs/api/ipc-main.md) * [Menu](../../docs/api/menu.md) * [MenuItem](../../docs/api/menu-item.md) * [powerMonitor](../../docs/api/power-monitor.md) From 8faf2f10e1ff35b63cadb348cd6089d321c0ddb5 Mon Sep 17 00:00:00 2001 From: Emerson Thompson Date: Tue, 28 Jun 2016 09:38:02 -0300 Subject: [PATCH 131/192] link to the translation --- docs-translations/pt-BR/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-translations/pt-BR/README.md b/docs-translations/pt-BR/README.md index ba6abc39aa0f..e1682d27d2d6 100644 --- a/docs-translations/pt-BR/README.md +++ b/docs-translations/pt-BR/README.md @@ -55,7 +55,7 @@ Existem muitas perguntas comuns que são feitas, verifique antes de criar uma is * [ipcMain](../../docs/api/ipc-main-process.md) * [Menu](../../docs/api/menu.md) * [MenuItem](../../docs/api/menu-item.md) -* [powerMonitor](../../docs/api/power-monitor.md) +* [powerMonitor](api/power-monitor.md) * [powerSaveBlocker](../../docs/api/power-save-blocker.md) * [protocol](../../docs/api/protocol.md) * [session](../../docs/api/session.md) From 1551a96223612aeb2bcb57778199f69e8d818fa1 Mon Sep 17 00:00:00 2001 From: Emerson Thompson Date: Tue, 28 Jun 2016 09:43:10 -0300 Subject: [PATCH 132/192] word correction --- docs-translations/pt-BR/tutorial/devtools-extension.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-translations/pt-BR/tutorial/devtools-extension.md b/docs-translations/pt-BR/tutorial/devtools-extension.md index 958b70c2e1e6..c488cee959c0 100644 --- a/docs-translations/pt-BR/tutorial/devtools-extension.md +++ b/docs-translations/pt-BR/tutorial/devtools-extension.md @@ -41,6 +41,6 @@ Atualmente o Electron não suporta páginas em segundo plano nas extensões do C Algumas extensões do Chrome podem usar a API `chrome.*`. Apesar de um esforço na implementação destas APIs no Electron, elas ainda não estão finalizadas. -Dado que nem todas as funções `chrome.*` esstão implementadas, algumas extensões que utilizam `chrome.devtools.*` podem não funcionar. Você pode reportar este erro no issue tracker para que possamos adicionar suporte a essas APIs. +Dado que nem todas as funções `chrome.*` estão implementadas, algumas extensões que utilizam `chrome.devtools.*` podem não funcionar. Você pode reportar este erro no issue tracker para que possamos adicionar suporte a essas APIs. [devtools-extension]: https://developer.chrome.com/extensions/devtools From a88626151868a55925d613ccdd21fe3191d368a5 Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Mon, 27 Jun 2016 14:38:12 +0200 Subject: [PATCH 133/192] Fix crash in ReadImageSkiaFromICO when CreateSkBitmapFromHICON fails --- atom/common/api/atom_api_native_image.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index b666f86ba3cb..14500496ebf3 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -160,10 +160,14 @@ base::win::ScopedHICON ReadICOFromPath(int size, const base::FilePath& path) { LR_LOADFROMFILE))); } -void ReadImageSkiaFromICO(gfx::ImageSkia* image, HICON icon) { +bool ReadImageSkiaFromICO(gfx::ImageSkia* image, HICON icon) { // Convert the icon from the Windows specific HICON to gfx::ImageSkia. std::unique_ptr bitmap(IconUtil::CreateSkBitmapFromHICON(icon)); + if (!bitmap) + return false; + image->AddRepresentation(gfx::ImageSkiaRep(*bitmap, 1.0f)); + return true; } #endif From 8fb86e10fc9b236488dcb1759e10d2c867502e6d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 28 Jun 2016 11:46:04 -0700 Subject: [PATCH 134/192] Add assert for non-existent .ico --- spec/api-native-image-spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/api-native-image-spec.js b/spec/api-native-image-spec.js index 4ada22af9ed1..e8dda25348e7 100644 --- a/spec/api-native-image-spec.js +++ b/spec/api-native-image-spec.js @@ -9,6 +9,7 @@ describe('nativeImage module', () => { it('returns an empty image for invalid paths', () => { assert(nativeImage.createFromPath('').isEmpty()) assert(nativeImage.createFromPath('does-not-exist.png').isEmpty()) + assert(nativeImage.createFromPath('does-not-exist.ico').isEmpty()) }) it('loads images from paths relative to the current working directory', () => { From 14df90fd19b9081cf3c3964c1ebfd03166e27970 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 28 Jun 2016 11:58:34 -0700 Subject: [PATCH 135/192] Add spec for valid .ico file --- spec/api-native-image-spec.js | 10 ++++++++++ spec/fixtures/assets/icon.ico | Bin 0 -> 77776 bytes 2 files changed, 10 insertions(+) create mode 100644 spec/fixtures/assets/icon.ico diff --git a/spec/api-native-image-spec.js b/spec/api-native-image-spec.js index e8dda25348e7..7e49cbf78e51 100644 --- a/spec/api-native-image-spec.js +++ b/spec/api-native-image-spec.js @@ -48,5 +48,15 @@ describe('nativeImage module', () => { // If all bytes are null, that's Bad assert.equal(nsimage.reduce((acc, x) => acc || (x !== 0), false), true) }) + + it('loads images from .ico files on Windows', () => { + if (process.platform !== 'win32') return + + const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico') + const image = nativeImage.createFromPath(imagePath) + assert(!image.isEmpty()) + assert.equal(image.getSize().height, 256) + assert.equal(image.getSize().width, 256) + }) }) }) diff --git a/spec/fixtures/assets/icon.ico b/spec/fixtures/assets/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..aa09177554651fb38078881c4dd73f7d1d17ae7f GIT binary patch literal 77776 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 iF#f-jZVT#QG|utqfSRlZZU^A$P?S~sRQ=I3 Date: Tue, 28 Jun 2016 13:01:51 -0700 Subject: [PATCH 136/192] Add failing spec for no specified storage keys --- spec/api-browser-window-spec.js | 7 +++++-- spec/fixtures/devtools-extensions/foo/index.html | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 25b852946ff4..b7b647e1c4ee 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1025,7 +1025,7 @@ describe('browser-window module', function () { }, /Unexpected token }/) }) - describe('when the devtools is docked', function () { + describe.only('when the devtools is docked', function () { it('creates the extension', function (done) { w.webContents.openDevTools({mode: 'bottom'}) @@ -1033,7 +1033,10 @@ describe('browser-window module', function () { assert.equal(message.runtimeId, 'foo') assert.equal(message.tabId, w.webContents.id) assert.equal(message.i18nString, 'foo - bar (baz)') - assert.deepEqual(message.storageItems, {foo: 'bar'}) + assert.deepEqual(message.storageItems, { + local: {hello: 'world'}, + sync: {foo: 'bar'} + }) done() }) }) diff --git a/spec/fixtures/devtools-extensions/foo/index.html b/spec/fixtures/devtools-extensions/foo/index.html index b10288227aca..70db43a37c2c 100644 --- a/spec/fixtures/devtools-extensions/foo/index.html +++ b/spec/fixtures/devtools-extensions/foo/index.html @@ -6,16 +6,25 @@ ' diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 44f8878dd7b9..1adb2ed229e8 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -285,7 +285,7 @@ describe('session module', function () { const protocolName = 'sp' const partitionProtocol = session.fromPartition(partitionName).protocol const protocol = session.defaultSession.protocol - const handler = function (error, callback) { + const handler = function (_, callback) { callback({data: 'test', mimeType: 'text/html'}) } @@ -298,7 +298,7 @@ describe('session module', function () { } }) partitionProtocol.registerStringProtocol(protocolName, handler, function (error) { - done(error ? error : undefined) + done(error != null ? error : undefined) }) }) diff --git a/spec/api-system-preferences-spec.js b/spec/api-system-preferences-spec.js index 333c4dbacb4e..be7009ae6bf1 100644 --- a/spec/api-system-preferences-spec.js +++ b/spec/api-system-preferences-spec.js @@ -18,5 +18,4 @@ describe('systemPreferences module', function () { assert(languages.length > 0) }) }) - }) diff --git a/spec/api-web-request-spec.js b/spec/api-web-request-spec.js index 211c69a14765..7b1c1556ecbc 100644 --- a/spec/api-web-request-spec.js +++ b/spec/api-web-request-spec.js @@ -7,7 +7,7 @@ const session = remote.session describe('webRequest module', function () { var ses = session.defaultSession var server = http.createServer(function (req, res) { - if (req.url == '/serverRedirect') { + if (req.url === '/serverRedirect') { res.statusCode = 301 res.setHeader('Location', 'http://' + req.rawHeaders[1]) res.end() @@ -308,7 +308,7 @@ describe('webRequest module', function () { ses.webRequest.onHeadersReceived(function (details, callback) { var responseHeaders = details.responseHeaders callback({ - responseHeaders: responseHeaders, + responseHeaders: responseHeaders }) }) $.ajax({ @@ -328,7 +328,7 @@ describe('webRequest module', function () { var responseHeaders = details.responseHeaders callback({ responseHeaders: responseHeaders, - statusLine: "HTTP/1.1 404 Not Found" + statusLine: 'HTTP/1.1 404 Not Found' }) }) $.ajax({ diff --git a/spec/asar-spec.js b/spec/asar-spec.js index ac0779d2f266..376e5ef0805f 100644 --- a/spec/asar-spec.js +++ b/spec/asar-spec.js @@ -1,5 +1,5 @@ const assert = require('assert') -const child_process = require('child_process') +const ChildProcess = require('child_process') const fs = require('fs') const path = require('path') @@ -536,7 +536,7 @@ describe('asar package', function () { describe('child_process.fork', function () { it('opens a normal js file', function (done) { - var child = child_process.fork(path.join(fixtures, 'asar', 'a.asar', 'ping.js')) + var child = ChildProcess.fork(path.join(fixtures, 'asar', 'a.asar', 'ping.js')) child.on('message', function (msg) { assert.equal(msg, 'message') done() @@ -546,7 +546,7 @@ describe('asar package', function () { it('supports asar in the forked js', function (done) { var file = path.join(fixtures, 'asar', 'a.asar', 'file1') - var child = child_process.fork(path.join(fixtures, 'module', 'asar.js')) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'asar.js')) child.on('message', function (content) { assert.equal(content, fs.readFileSync(file).toString()) done() @@ -556,11 +556,10 @@ describe('asar package', function () { }) describe('child_process.exec', function () { - var child_process = require('child_process'); var echo = path.join(fixtures, 'asar', 'echo.asar', 'echo') it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) { - child_process.exec('echo ' + echo + ' foo bar', function (error, stdout) { + ChildProcess.exec('echo ' + echo + ' foo bar', function (error, stdout) { assert.equal(error, null) assert.equal(stdout.toString().replace(/\r/g, ''), echo + ' foo bar\n') done() @@ -569,24 +568,22 @@ describe('asar package', function () { }) describe('child_process.execSync', function () { - var child_process = require('child_process'); var echo = path.join(fixtures, 'asar', 'echo.asar', 'echo') it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) { - var stdout = child_process.execSync('echo ' + echo + ' foo bar') + var stdout = ChildProcess.execSync('echo ' + echo + ' foo bar') assert.equal(stdout.toString().replace(/\r/g, ''), echo + ' foo bar\n') done() }) }) describe('child_process.execFile', function () { - var echo, execFile, execFileSync, ref2 + var echo, execFile, execFileSync if (process.platform !== 'darwin') { return } - ref2 = require('child_process') - execFile = ref2.execFile - execFileSync = ref2.execFileSync + execFile = ChildProcess.execFile + execFileSync = ChildProcess.execFileSync echo = path.join(fixtures, 'asar', 'echo.asar', 'echo') it('executes binaries', function (done) { @@ -785,7 +782,7 @@ describe('asar package', function () { }) it('is available in forked scripts', function (done) { - var child = child_process.fork(path.join(fixtures, 'module', 'original-fs.js')) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'original-fs.js')) child.on('message', function (msg) { assert.equal(msg, 'object') done() diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 68836fb927ed..e682773c4309 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -235,10 +235,11 @@ describe('chromium feature', function () { it('defines a window.location getter', function (done) { var b, targetURL - if (process.platform == 'win32') + if (process.platform === 'win32') { targetURL = 'file:///' + fixtures.replace(/\\/g, '/') + '/pages/base-page.html' - else + } else { targetURL = 'file://' + fixtures + '/pages/base-page.html' + } b = window.open(targetURL) webContents.fromId(b.guestId).once('did-finish-load', function () { assert.equal(b.location, targetURL) diff --git a/spec/fixtures/api/relaunch/main.js b/spec/fixtures/api/relaunch/main.js index 74cafc6f0d53..27a1e07efa63 100644 --- a/spec/fixtures/api/relaunch/main.js +++ b/spec/fixtures/api/relaunch/main.js @@ -1,9 +1,7 @@ -const {app, dialog} = require('electron') +const {app} = require('electron') const net = require('net') -const socketPath = process.platform === 'win32' ? - '\\\\.\\pipe\\electron-app-relaunch' : - '/tmp/electron-app-relaunch' +const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch' process.on('uncaughtException', () => { app.exit(1) diff --git a/spec/node-spec.js b/spec/node-spec.js index 93905b49d7e6..19375008ab1e 100644 --- a/spec/node-spec.js +++ b/spec/node-spec.js @@ -1,5 +1,5 @@ const assert = require('assert') -const child_process = require('child_process') +const ChildProcess = require('child_process') const fs = require('fs') const path = require('path') const os = require('os') @@ -13,7 +13,7 @@ describe('node feature', function () { describe('child_process', function () { describe('child_process.fork', function () { it('works in current process', function (done) { - var child = child_process.fork(path.join(fixtures, 'module', 'ping.js')) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'ping.js')) child.on('message', function (msg) { assert.equal(msg, 'message') done() @@ -23,7 +23,7 @@ describe('node feature', function () { it('preserves args', function (done) { var args = ['--expose_gc', '-test', '1'] - var child = child_process.fork(path.join(fixtures, 'module', 'process_args.js'), args) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'process_args.js'), args) child.on('message', function (msg) { assert.deepEqual(args, msg.slice(2)) done() @@ -32,7 +32,7 @@ describe('node feature', function () { }) it('works in forked process', function (done) { - var child = child_process.fork(path.join(fixtures, 'module', 'fork_ping.js')) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js')) child.on('message', function (msg) { assert.equal(msg, 'message') done() @@ -41,7 +41,7 @@ describe('node feature', function () { }) it('works in forked process when options.env is specifed', function (done) { - var child = child_process.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], { + var child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], { path: process.env['PATH'] }) child.on('message', function (msg) { @@ -62,7 +62,7 @@ describe('node feature', function () { }) it('has String::localeCompare working in script', function (done) { - var child = child_process.fork(path.join(fixtures, 'module', 'locale-compare.js')) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'locale-compare.js')) child.on('message', function (msg) { assert.deepEqual(msg, [0, -1, 1]) done() @@ -71,7 +71,7 @@ describe('node feature', function () { }) it('has setImmediate working in script', function (done) { - var child = child_process.fork(path.join(fixtures, 'module', 'set-immediate.js')) + var child = ChildProcess.fork(path.join(fixtures, 'module', 'set-immediate.js')) child.on('message', function (msg) { assert.equal(msg, 'ok') done() @@ -80,7 +80,7 @@ describe('node feature', function () { }) it('pipes stdio', function (done) { - let child = child_process.fork(path.join(fixtures, 'module', 'process-stdout.js'), {silent: true}) + let child = ChildProcess.fork(path.join(fixtures, 'module', 'process-stdout.js'), {silent: true}) let data = '' child.stdout.on('data', (chunk) => { data += String(chunk) @@ -183,7 +183,7 @@ describe('node feature', function () { it('emit error when connect to a socket path without listeners', function (done) { var socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock') var script = path.join(fixtures, 'module', 'create_socket.js') - var child = child_process.fork(script, [socketPath]) + var child = ChildProcess.fork(script, [socketPath]) child.on('exit', function (code) { assert.equal(code, 0) var client = require('net').connect(socketPath) @@ -213,8 +213,10 @@ describe('node feature', function () { }) it('does not crash when creating large Buffers', function () { - new Buffer(new Array(4096).join(' ')); - new Buffer(new Array(4097).join(' ')); + var buffer = new Buffer(new Array(4096).join(' ')) + assert.equal(buffer.length, 4096) + buffer = new Buffer(new Array(4097).join(' ')) + assert.equal(buffer.length, 4097) }) }) diff --git a/spec/static/main.js b/spec/static/main.js index 33c3bb48136a..5104a0f2525a 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -95,7 +95,7 @@ app.on('ready', function () { width: 800, height: 600, webPreferences: { - backgroundThrottling: false, + backgroundThrottling: false } }) window.loadURL(url.format({ @@ -119,9 +119,9 @@ app.on('ready', function () { // For session's download test, listen 'will-download' event in browser, and // reply the result to renderer for verifying var downloadFilePath = path.join(__dirname, '..', 'fixtures', 'mock.pdf') - ipcMain.on('set-download-option', function (event, need_cancel, prevent_default) { + ipcMain.on('set-download-option', function (event, needCancel, preventDefault) { window.webContents.session.once('will-download', function (e, item) { - if (prevent_default) { + if (preventDefault) { e.preventDefault() const url = item.getURL() const filename = item.getFilename() @@ -144,7 +144,7 @@ app.on('ready', function () { item.getContentDisposition(), item.getFilename()) }) - if (need_cancel) item.cancel() + if (needCancel) item.cancel() } }) event.returnValue = 'done' diff --git a/spec/webview-spec.js b/spec/webview-spec.js index be0271e85aa6..8e4779d80992 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -40,7 +40,7 @@ describe(' tag', function () { webPreferences: { nodeIntegration: false, preload: path.join(fixtures, 'module', 'preload-webview.js') - }, + } }) ipcMain.once('webview', function (event, type) { if (type === 'undefined') { @@ -776,13 +776,12 @@ describe(' tag', function () { }) describe('permission-request event', function () { - function setUpRequestHandler (webview, requested_permission, completed) { + function setUpRequestHandler (webview, requestedPermission, completed) { var listener = function (webContents, permission, callback) { if (webContents.getId() === webview.getId()) { - assert.equal(permission, requested_permission) + assert.equal(permission, requestedPermission) callback(false) - if (completed) - completed() + if (completed) completed() } } session.fromPartition(webview.partition).setPermissionRequestHandler(listener) @@ -855,7 +854,7 @@ describe(' tag', function () { 'did-get-response-details.html': 'mainFrame', 'logo.png': 'image' } - var responses = 0; + var responses = 0 webview.addEventListener('did-get-response-details', function (event) { responses++ var fileName = event.newURL.slice(event.newURL.lastIndexOf('/') + 1) From 8b35814d108706196957aaf230505cfa892039a2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 29 Jun 2016 09:42:12 -0700 Subject: [PATCH 148/192] :art: --- spec/api-browser-window-spec.js | 4 ++-- spec/api-session-spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 588ff9023ad5..2bc55484dbcc 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1004,7 +1004,7 @@ describe('browser-window module', function () { var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id WebInspector.inspectorView.showPanel(lastPanelId) } - w.devToolsWebContents.executeJavaScript('(' + showLastPanel.toString() + ')()') + w.devToolsWebContents.executeJavaScript(`(${showLastPanel})()`) } else { clearInterval(showPanelIntevalId) } @@ -1080,7 +1080,7 @@ describe('browser-window module', function () { var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id WebInspector.inspectorView.showPanel(lastPanelId) } - w.devToolsWebContents.executeJavaScript('(' + showLastPanel.toString() + ')()') + w.devToolsWebContents.executeJavaScript(`(${showLastPanel})()`) } else { clearInterval(showPanelIntevalId) } diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 1adb2ed229e8..a7af6d7bdbb0 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -285,7 +285,7 @@ describe('session module', function () { const protocolName = 'sp' const partitionProtocol = session.fromPartition(partitionName).protocol const protocol = session.defaultSession.protocol - const handler = function (_, callback) { + const handler = function (ignoredError, callback) { callback({data: 'test', mimeType: 'text/html'}) } From 03497fb984636ca3f7155468202422e0c4e4341d Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 29 Jun 2016 09:44:38 -0700 Subject: [PATCH 149/192] Update buffer length asserts --- spec/node-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/node-spec.js b/spec/node-spec.js index 19375008ab1e..9382c4c9154b 100644 --- a/spec/node-spec.js +++ b/spec/node-spec.js @@ -214,9 +214,9 @@ describe('node feature', function () { it('does not crash when creating large Buffers', function () { var buffer = new Buffer(new Array(4096).join(' ')) - assert.equal(buffer.length, 4096) + assert.equal(buffer.length, 4095) buffer = new Buffer(new Array(4097).join(' ')) - assert.equal(buffer.length, 4097) + assert.equal(buffer.length, 4096) }) }) From 4a40369b6d0e5e80641e6ab9d193dad8e12c7c34 Mon Sep 17 00:00:00 2001 From: Plusb Preco Date: Thu, 30 Jun 2016 02:50:41 +0900 Subject: [PATCH 150/192] :memo: Update Korean docs as upstream [ci skip] --- docs-translations/ko-KR/api/browser-window.md | 6 +++--- docs-translations/ko-KR/api/clipboard.md | 7 +++---- docs-translations/ko-KR/api/session.md | 12 ++++++------ docs-translations/ko-KR/api/tray.md | 2 +- docs-translations/ko-KR/api/web-contents.md | 7 +++---- docs-translations/ko-KR/styleguide.md | 6 +++--- 6 files changed, 19 insertions(+), 21 deletions(-) diff --git a/docs-translations/ko-KR/api/browser-window.md b/docs-translations/ko-KR/api/browser-window.md index e303a0c30851..7adbc08a72da 100644 --- a/docs-translations/ko-KR/api/browser-window.md +++ b/docs-translations/ko-KR/api/browser-window.md @@ -997,10 +997,10 @@ Linux 플랫폼에선 Unity 데스크톱 환경만 지원합니다. 그리고 ### `win.setContentProtection(enable)` _macOS_ _Windows_ -Prevents the window contents from being captured by other apps. +윈도우 콘텐츠가 외부 어플리케이션에 의해 캡쳐되는 것을 막습니다. -On macOS it sets the NSWindow's sharingType to NSWindowSharingNone. -On Windows it calls SetWindowDisplayAffinity with WDA_MONITOR. +macOS에선 NSWindow의 sharingType을 NSWindowSharingNone로 설정합니다. +Windows에선 WDA_MONITOR와 함께 SetWindowDisplayAffinity를 호출합니다. ### `win.setFocusable(focusable)` _Windows_ diff --git a/docs-translations/ko-KR/api/clipboard.md b/docs-translations/ko-KR/api/clipboard.md index de0807cb2049..6536bf922bca 100644 --- a/docs-translations/ko-KR/api/clipboard.md +++ b/docs-translations/ko-KR/api/clipboard.md @@ -78,9 +78,8 @@ console.log(clipboard.readText('selection')); ### `clipboard.readBookmark()` _macOS_ _Windows_ -Returns an Object containing `title` and `url` keys representing the bookmark in -the clipboard. The `title` and `url` values will be empty strings when the -bookmark is unavailable. +클립보드로부터 북마크 형식으로 표현된 `title`와 `url` 키를 담은 객체를 반환합니다. +`title`과 `url` 값들은 북마크를 사용할 수 없을 때 빈 문자열을 포함합니다. ### `clipboard.writeBookmark(title, url[, type])` _macOS_ _Windows_ @@ -88,7 +87,7 @@ bookmark is unavailable. * `url` String * `type` String (optional) -Writes the `title` and `url` into the clipboard as a bookmark. +`title`과 `url`을 클립보드에 북마크 형식으로 씁니다. ### `clipboard.clear([type])` diff --git a/docs-translations/ko-KR/api/session.md b/docs-translations/ko-KR/api/session.md index d685b6c569ad..c2e3ca923dc0 100644 --- a/docs-translations/ko-KR/api/session.md +++ b/docs-translations/ko-KR/api/session.md @@ -344,17 +344,17 @@ session.defaultSession.allowNTLMCredentialsForDomains('*') * `userAgent` String * `acceptLanguages` String (optional) -Overrides the `userAgent` and `acceptLanguages` for this session. +현재 세션에 대해 `userAgent`와 `acceptLanguages`를 덮어씁니다. -The `acceptLanguages` must a comma separated ordered list of language codes, for -example `"en-US,fr,de,ko,zh-CN,ja"`. +`acceptLanguages`는 반드시 쉼표로 구분된 순서에 맞춘 언어 코드의 리스트여야 하며 +예를 들면 `"en-US,fr,de,ko,zh-CN,ja"` 입니다. -This doesn't affect existing `WebContents`, and each `WebContents` can use -`webContents.setUserAgent` to override the session-wide user agent. +이는 현재 존재하는 `WebContents`에 적용되지 않으며 각 `WebContents`는 +`webContents.setUserAgent`를 사용하여 세션 전체의 유저 에이전트를 덮어쓸 수 있습니다. #### `ses.getUserAgent()` -Returns a `String` representing the user agent for this session. +현재 세션에 대한 유저 에이전트를 표현하는 `String`을 반환합니다. #### `ses.webRequest` diff --git a/docs-translations/ko-KR/api/tray.md b/docs-translations/ko-KR/api/tray.md index 06862cada2ee..46f790cd7651 100644 --- a/docs-translations/ko-KR/api/tray.md +++ b/docs-translations/ko-KR/api/tray.md @@ -202,7 +202,7 @@ appIcon.setContextMenu(contextMenu) ### `tray.getBounds()` _macOS_ _Windows_ -Returns the `bounds` of this tray icon as `Object`. +이 트레이 아이콘의 `bounds`를 `Object` 형식으로 반환합니다. * `bounds` Object * `x` Integer diff --git a/docs-translations/ko-KR/api/web-contents.md b/docs-translations/ko-KR/api/web-contents.md index 87fe9ebd4210..ba53f3dbd8ff 100644 --- a/docs-translations/ko-KR/api/web-contents.md +++ b/docs-translations/ko-KR/api/web-contents.md @@ -921,10 +921,9 @@ Input `event`를 웹 페이지로 전송합니다. 프로세서에선 little-endian 방식을 사용하므로 위의 포맷을 그대로 표현합니다. 하지만 몇몇 프로세서는 big-endian 방식을 사용하는데, 이 경우 32비트 ARGB 포맷을 사용합니다) -The `dirtyRect` is an object with `x, y, width, height` properties that -describes which part of the page was repainted. If `onlyDirty` is set to -`true`, `frameBuffer` will only contain the repainted area. `onlyDirty` -defaults to `false`. +`dirtyRect`는 페이지의 어떤 부분이 다시 그려졌는지를 표현하는 `x, y, width, height` +속성을 포함하는 객체입니다. 만약 `onlyDirty`가 `true`로 지정되어 있으면, +`frameBuffer`가 다시 그려진 부분만 포함합니다. `onlyDirty`의 기본값은 `false`입니다. ### `webContents.endFrameSubscription()` diff --git a/docs-translations/ko-KR/styleguide.md b/docs-translations/ko-KR/styleguide.md index cc1e76766b05..584d3245bdb5 100644 --- a/docs-translations/ko-KR/styleguide.md +++ b/docs-translations/ko-KR/styleguide.md @@ -71,9 +71,9 @@ Electron 문서 구조를 이해하는 데 참고할 수 있는 유용한 도움 와 같은 일반적으로 쓰이는 타입 중 하나를 받거나 Electron의 [`webContent`](api/web-content.md) 같은 커스텀 타입을 받습니다. -If an argument is unique to certain platforms, those platforms are denoted -using a space-delimited italicized list following the datatype. Values can be -`OS X`, `Windows`, or `Linux`. +만약 인수가 특정 플랫폼에 대해서만 적용된다면, 해당하는 플랫폼들을 공백-구분된 이텔릭체 +리스트를 데이터 타입 뒤에 표기합니다. 값은 `OS X`, `Windows` 또는 `Linux`가 될 수 +있습니다. ``` * `animate` Boolean (optional) _OS X_ _Windows_ From b22ce934775537c18a953f40efb7a9526df285fe Mon Sep 17 00:00:00 2001 From: Jonas Schwabe Date: Sun, 26 Jun 2016 01:55:24 +0200 Subject: [PATCH 151/192] Implement libunity launcher badge counter to be set via electron, fixes #4193 --- atom/browser/api/atom_api_app.cc | 10 ++++++++++ atom/browser/browser.h | 11 ++++++++++- atom/browser/browser_linux.cc | 14 ++++++++++++++ lib/browser/api/app.js | 7 +++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 265ed8f36b7d..ce6a7d33c6a4 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -614,6 +614,16 @@ void Initialize(v8::Local exports, v8::Local unused, dict.SetMethod("dockSetMenu", &DockSetMenu); dict.SetMethod("dockSetIcon", base::Bind(&Browser::DockSetIcon, browser)); #endif + +#if defined(OS_LINUX) + auto browser = base::Unretained(Browser::Get()); + dict.SetMethod("unityLauncherAvailable", + base::Bind(&Browser::UnityLauncherAvailable, browser)); + dict.SetMethod("unityLauncherSetBadgeCount", + base::Bind(&Browser::UnityLauncherSetBadgeCount, browser)); + dict.SetMethod("unityLauncherGetBadgeCount", + base::Bind(&Browser::UnityLauncherGetBadgeCount, browser)); +#endif } } // namespace diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 2c1678083034..601f40044b2c 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -149,7 +149,14 @@ class Browser : public WindowListObserver { // one from app's name. // The returned string managed by Browser, and should not be modified. PCWSTR GetAppUserModelID(); -#endif +#endif // defined(OS_WIN) + +#if defined(OS_LINUX) + // Set/Get unity dock's badge counter. + bool UnityLauncherAvailable(); + void UnityLauncherSetBadgeCount(int count); + int UnityLauncherGetBadgeCount(); +#endif // defined(OS_LINUX) // Tell the application to open a file. bool OpenFile(const std::string& file_path); @@ -216,6 +223,8 @@ class Browser : public WindowListObserver { std::string version_override_; std::string name_override_; + int current_badge_count_ = 0; + #if defined(OS_WIN) base::string16 app_user_model_id_; #endif diff --git a/atom/browser/browser_linux.cc b/atom/browser/browser_linux.cc index d994bb4109bb..668da661802f 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -10,6 +10,7 @@ #include "atom/browser/window_list.h" #include "atom/common/atom_version.h" #include "brightray/common/application_info.h" +#include "chrome/browser/ui/libgtk2ui/unity_service.h" namespace atom { @@ -54,4 +55,17 @@ std::string Browser::GetExecutableFileProductName() const { return brightray::GetApplicationName(); } +bool Browser::UnityLauncherAvailable() { + return unity::IsRunning(); +} + +void Browser::UnityLauncherSetBadgeCount(int count) { + current_badge_count_ = count; + unity::SetDownloadCount(count); +} + +int Browser::UnityLauncherGetBadgeCount() { + return current_badge_count_; +} + } // namespace atom diff --git a/lib/browser/api/app.js b/lib/browser/api/app.js index 34210f42090f..1fd972c57cae 100644 --- a/lib/browser/api/app.js +++ b/lib/browser/api/app.js @@ -45,6 +45,13 @@ if (process.platform === 'darwin') { } } +if (process.platform === 'linux' && bindings.unityLauncherAvailable()) { + app.unityLauncher = { + setBadgeCount: bindings.unityLauncherSetBadgeCount, + getBadgeCount: bindings.unityLauncherGetBadgeCount + }; +} + app.allowNTLMCredentialsForAllDomains = function (allow) { if (!process.noDeprecations) { deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains') From e3ba6818af8a649e868c1d604aef00f6cd0b4bd9 Mon Sep 17 00:00:00 2001 From: Jonas Schwabe Date: Sun, 26 Jun 2016 02:00:41 +0200 Subject: [PATCH 152/192] Fix linter errors --- atom/browser/browser.h | 4 ++-- lib/browser/api/app.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 601f40044b2c..739a9f5eb03c 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -149,14 +149,14 @@ class Browser : public WindowListObserver { // one from app's name. // The returned string managed by Browser, and should not be modified. PCWSTR GetAppUserModelID(); -#endif // defined(OS_WIN) +#endif // defined(OS_WIN) #if defined(OS_LINUX) // Set/Get unity dock's badge counter. bool UnityLauncherAvailable(); void UnityLauncherSetBadgeCount(int count); int UnityLauncherGetBadgeCount(); -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) // Tell the application to open a file. bool OpenFile(const std::string& file_path); diff --git a/lib/browser/api/app.js b/lib/browser/api/app.js index 1fd972c57cae..602a7a0df51b 100644 --- a/lib/browser/api/app.js +++ b/lib/browser/api/app.js @@ -49,7 +49,7 @@ if (process.platform === 'linux' && bindings.unityLauncherAvailable()) { app.unityLauncher = { setBadgeCount: bindings.unityLauncherSetBadgeCount, getBadgeCount: bindings.unityLauncherGetBadgeCount - }; + } } app.allowNTLMCredentialsForAllDomains = function (allow) { From 336a55cb23502b89dc3cdcdca914bb2fb935e170 Mon Sep 17 00:00:00 2001 From: Jonas Schwabe Date: Sun, 26 Jun 2016 02:10:27 +0200 Subject: [PATCH 153/192] :memo: docs for unity launcher badge api --- docs/api/app.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/api/app.md b/docs/api/app.md index c014395eb10c..777f31551fd6 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -647,6 +647,17 @@ Sets the application's [dock menu][dock-menu]. Sets the `image` associated with this dock icon. +### `app.unityLauncher.setBadgeCount(count)` _Ubuntu Unity_ +* `count` Integer + +Sets the number to be displayed next to the app icon in the unity launcher. +Setting count to `0` will hide the badge. + +### `app.unityLauncher.getBadgeCount(count)` _Ubuntu Unity_ + +Returns the current value displayed in the counter badge next to the launcher icon. + + [dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103 [tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx From e2d389fbd45510b424ea178abd22add8747b0d4c Mon Sep 17 00:00:00 2001 From: Jonas Schwabe Date: Sun, 26 Jun 2016 11:48:58 +0200 Subject: [PATCH 154/192] Rename the unityLauncher api to launcher and make it available on linux in general. + Document it only works on ubuntu --- atom/browser/browser_linux.cc | 6 ++++-- docs/api/app.md | 14 ++++++++++++-- lib/browser/api/app.js | 7 ++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/atom/browser/browser_linux.cc b/atom/browser/browser_linux.cc index 668da661802f..8e41ec8c1997 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -60,8 +60,10 @@ bool Browser::UnityLauncherAvailable() { } void Browser::UnityLauncherSetBadgeCount(int count) { - current_badge_count_ = count; - unity::SetDownloadCount(count); + if (UnityLauncherAvailable()) { + current_badge_count_ = count; + unity::SetDownloadCount(count); + } } int Browser::UnityLauncherGetBadgeCount() { diff --git a/docs/api/app.md b/docs/api/app.md index 777f31551fd6..da14a504451d 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -647,16 +647,26 @@ Sets the application's [dock menu][dock-menu]. Sets the `image` associated with this dock icon. -### `app.unityLauncher.setBadgeCount(count)` _Ubuntu Unity_ +### `app.launcher.setBadgeCount(count)` _Linux_ * `count` Integer Sets the number to be displayed next to the app icon in the unity launcher. Setting count to `0` will hide the badge. -### `app.unityLauncher.getBadgeCount(count)` _Ubuntu Unity_ +**Note:** This feature is currently only supported on Ubuntu Unity. Calling this function has no effect, when the application is running in a different environment. + +**Note:** you need to specify the .desktop file name to the desktopName field in package.json. By default, it will assume app.getName().desktop in packaged apps. + +### `app.launcher.getBadgeCount(count)` _Linux_ Returns the current value displayed in the counter badge next to the launcher icon. +**Note:** As `setBadgeCount` only supports Ubuntu Unity, the value will be 0 when the application is running in a different environment. + +### `app.launcher.isCounterBadgeAvailable()` _Linux_ + +This method checks if the current desktop environment supports an app icon counter badge and returns `true` in this case. + [dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103 [tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks diff --git a/lib/browser/api/app.js b/lib/browser/api/app.js index 602a7a0df51b..466d2dd791ca 100644 --- a/lib/browser/api/app.js +++ b/lib/browser/api/app.js @@ -45,10 +45,11 @@ if (process.platform === 'darwin') { } } -if (process.platform === 'linux' && bindings.unityLauncherAvailable()) { - app.unityLauncher = { +if (process.platform === 'linux') { + app.launcher = { setBadgeCount: bindings.unityLauncherSetBadgeCount, - getBadgeCount: bindings.unityLauncherGetBadgeCount + getBadgeCount: bindings.unityLauncherGetBadgeCount, + isCounterBadgeAvailable: bindings.unityLauncherAvailable } } From 8ac205685d2cf2323bb5b72fd4ad37c2dd95b5ef Mon Sep 17 00:00:00 2001 From: Jonas Schwabe Date: Wed, 29 Jun 2016 19:23:00 +0200 Subject: [PATCH 155/192] Add ifdef for property, which should only be availabe on Linux --- atom/browser/browser.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 739a9f5eb03c..c9155d94b4e3 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -223,7 +223,9 @@ class Browser : public WindowListObserver { std::string version_override_; std::string name_override_; +#if defined(OS_LINUX) int current_badge_count_ = 0; +#endif #if defined(OS_WIN) base::string16 app_user_model_id_; From 768ff7af5afb18a53479c6b0be32f01c0316a1c0 Mon Sep 17 00:00:00 2001 From: Jonas Schwabe Date: Wed, 29 Jun 2016 19:23:56 +0200 Subject: [PATCH 156/192] Add additional method isUnityRunning, fix multiple typos in docs --- docs/api/app.md | 14 +++++++++----- lib/browser/api/app.js | 3 ++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index da14a504451d..260e64533b15 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -650,19 +650,23 @@ Sets the `image` associated with this dock icon. ### `app.launcher.setBadgeCount(count)` _Linux_ * `count` Integer -Sets the number to be displayed next to the app icon in the unity launcher. -Setting count to `0` will hide the badge. +Sets the number to be displayed next to the app icon in the Unity launcher. +Setting the count to `0` will hide the badge. -**Note:** This feature is currently only supported on Ubuntu Unity. Calling this function has no effect, when the application is running in a different environment. +**Note:** This feature is currently only supported on Ubuntu Unity. Calling this function has no effect when the application is running in a different environment. -**Note:** you need to specify the .desktop file name to the desktopName field in package.json. By default, it will assume app.getName().desktop in packaged apps. +**Note:** You need to specify the .desktop file name to the `desktopName` field in package.json. By default, it will assume `app.getName().desktop` in packaged apps. -### `app.launcher.getBadgeCount(count)` _Linux_ +### `app.launcher.getBadgeCount()` _Linux_ Returns the current value displayed in the counter badge next to the launcher icon. **Note:** As `setBadgeCount` only supports Ubuntu Unity, the value will be 0 when the application is running in a different environment. +### `app.launcher.isUnityRunning()` _Linux_ + +This method checks if the current desktop environment is Unity and returns `true` in this case. + ### `app.launcher.isCounterBadgeAvailable()` _Linux_ This method checks if the current desktop environment supports an app icon counter badge and returns `true` in this case. diff --git a/lib/browser/api/app.js b/lib/browser/api/app.js index 466d2dd791ca..0ea788b94a71 100644 --- a/lib/browser/api/app.js +++ b/lib/browser/api/app.js @@ -49,7 +49,8 @@ if (process.platform === 'linux') { app.launcher = { setBadgeCount: bindings.unityLauncherSetBadgeCount, getBadgeCount: bindings.unityLauncherGetBadgeCount, - isCounterBadgeAvailable: bindings.unityLauncherAvailable + isCounterBadgeAvailable: bindings.unityLauncherAvailable, + isUnityRunning: bindings.unityLauncherAvailable } } From 90048abd0ab6101392d7b659f14e8a175d525378 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 29 Jun 2016 11:22:50 -0700 Subject: [PATCH 157/192] Update renamed nativeImage APIs --- docs-translations/jp/api/native-image.md | 4 ++-- docs-translations/ko-KR/api/native-image.md | 4 ++-- docs-translations/zh-CN/api/native-image.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs-translations/jp/api/native-image.md b/docs-translations/jp/api/native-image.md index a819123707c2..1c0c2ff9c6dc 100644 --- a/docs-translations/jp/api/native-image.md +++ b/docs-translations/jp/api/native-image.md @@ -112,11 +112,11 @@ const nativeImage = require('electron').nativeImage; var image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); ``` -### `image.toPng()` +### `image.toPNG()` `PNG`エンコードされた画像を含む[Buffer][buffer]を返します。 -### `image.toJpeg(quality)` +### `image.toJPEG(quality)` * `quality` Integer (**required**) - Between 0 - 100. diff --git a/docs-translations/ko-KR/api/native-image.md b/docs-translations/ko-KR/api/native-image.md index d44f000a1c90..dfdc180b6c78 100644 --- a/docs-translations/ko-KR/api/native-image.md +++ b/docs-translations/ko-KR/api/native-image.md @@ -125,11 +125,11 @@ let image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); `nativeImage` 인스턴스 객체에서 사용할 수 있는 메서드입니다. -### `image.toPng()` +### `image.toPNG()` `PNG` 이미지를 인코딩한 데이터를 [Buffer][buffer]로 반환합니다. -### `image.toJpeg(quality)` +### `image.toJPEG(quality)` * `quality` Integer (**required**) 0 - 100 사이의 값 diff --git a/docs-translations/zh-CN/api/native-image.md b/docs-translations/zh-CN/api/native-image.md index aa544d07dc33..fc5955cd21ac 100644 --- a/docs-translations/zh-CN/api/native-image.md +++ b/docs-translations/zh-CN/api/native-image.md @@ -107,11 +107,11 @@ const nativeImage = require('electron').nativeImage; var image = nativeImage.createFromPath('/Users/somebody/images/icon.png'); ``` -### `image.toPng()` +### `image.toPNG()` 返回一个 [Buffer][buffer] ,它包含了图片的 `PNG` 编码数据. -### `image.toJpeg(quality)` +### `image.toJPEG(quality)` * `quality` Integer (**必须**) - 在 0 - 100 之间. From 9aa8807f83ac3eb16d82552ed0ebfe509838164c Mon Sep 17 00:00:00 2001 From: Jonas Schwabe Date: Wed, 29 Jun 2016 19:47:20 +0200 Subject: [PATCH 158/192] add specs for app.launcher api fix linter errors --- spec/api-app-spec.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 6cda0e11bc06..214a084f2ec7 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -284,4 +284,37 @@ describe('app module', function () { }) }) }) + + describe('app.launcher API', function () { + it('should be available on linux', function () { + if (process.platform !== 'linux') { + assert.equal(app.launcher, undefined) + } else { + assert.notEqual(app.launcher, undefined) + } + }) + + it('should be possible to set a badge count on supported environments', function () { + if (process.platform === 'linux' && + app.launcher.isCounterBadgeAvailable()) { + app.launcher.setBadgeCount(42) + assert.equal(app.launcher.getBadgeCount(), 42) + } + }) + + it('should be possible to set a badge count on unity', function () { + if (process.platform === 'linux' && + app.launcher.isUnityRunning()) { + assert.equal(app.launcher.isCounterBadgeAvailable(), true) + } + }) + + it('should not be possible to set a badge counter on unsupported environments', function () { + if (process.platform === 'linux' && + !app.launcher.isCounterBadgeAvailable()) { + app.launcher.setBadgeCount(42) + assert.equal(app.launcher.getBadgeCount(), 0) + } + }) + }) }) From e797eb5ca31865eb36ad2065b8a8040b4ef223f6 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 28 Jun 2016 15:10:32 -0700 Subject: [PATCH 159/192] Use set context menu when param is not specified --- atom/browser/ui/tray_icon_cocoa.mm | 2 +- atom/browser/ui/win/notify_icon.cc | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/atom/browser/ui/tray_icon_cocoa.mm b/atom/browser/ui/tray_icon_cocoa.mm index 8b25aa3540ff..2510d3160cc6 100644 --- a/atom/browser/ui/tray_icon_cocoa.mm +++ b/atom/browser/ui/tray_icon_cocoa.mm @@ -263,7 +263,7 @@ const CGFloat kVerticalTitleMargin = 2; } if (menuController_ && ![menuController_ isMenuOpen]) { - // Redraw the dray icon to show highlight if it is enabled. + // Redraw the tray icon to show highlight if it is enabled. [self setNeedsDisplay:YES]; [statusItem_ popUpStatusItemMenu:[menuController_ menu]]; // The popUpStatusItemMenu returns only after the showing menu is closed. diff --git a/atom/browser/ui/win/notify_icon.cc b/atom/browser/ui/win/notify_icon.cc index 0ccf46b83212..a360c92cc4b9 100644 --- a/atom/browser/ui/win/notify_icon.cc +++ b/atom/browser/ui/win/notify_icon.cc @@ -134,8 +134,9 @@ void NotifyIcon::DisplayBalloon(HICON icon, void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) { // Returns if context menu isn't set. - if (!menu_model) + if (!menu_model && !menu_model_) return; + // Set our window as the foreground window, so the context menu closes when // we click away from it. if (!SetForegroundWindow(window_)) @@ -147,7 +148,7 @@ void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, rect.set_origin(gfx::Screen::GetScreen()->GetCursorScreenPoint()); views::MenuRunner menu_runner( - menu_model, + menu_model ? menu_model : menu_model_, views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS); ignore_result(menu_runner.RunMenuAt( NULL, NULL, rect, views::MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_MOUSE)); From 45dbbfdfe9799259da2bd3616a1f857bba885290 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 28 Jun 2016 15:28:27 -0700 Subject: [PATCH 160/192] Use nullptr comparisons --- atom/browser/ui/win/notify_icon.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atom/browser/ui/win/notify_icon.cc b/atom/browser/ui/win/notify_icon.cc index a360c92cc4b9..a82ac36c7acb 100644 --- a/atom/browser/ui/win/notify_icon.cc +++ b/atom/browser/ui/win/notify_icon.cc @@ -134,7 +134,7 @@ void NotifyIcon::DisplayBalloon(HICON icon, void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, ui::SimpleMenuModel* menu_model) { // Returns if context menu isn't set. - if (!menu_model && !menu_model_) + if (menu_model == nullptr && menu_model_ == nullptr) return; // Set our window as the foreground window, so the context menu closes when @@ -148,7 +148,7 @@ void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, rect.set_origin(gfx::Screen::GetScreen()->GetCursorScreenPoint()); views::MenuRunner menu_runner( - menu_model ? menu_model : menu_model_, + menu_model != nullptr ? menu_model : menu_model_, views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS); ignore_result(menu_runner.RunMenuAt( NULL, NULL, rect, views::MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_MOUSE)); From 21f0e65735c7a20641b22b48fa35a827e09a70d3 Mon Sep 17 00:00:00 2001 From: Plusb Preco Date: Thu, 30 Jun 2016 11:32:14 +0900 Subject: [PATCH 161/192] :memo: Update Korean docs as upstream [ci skip] --- docs-translations/ko-KR/api/app.md | 27 ++ docs-translations/ko-KR/api/browser-window.md | 54 ++++ docs-translations/ko-KR/api/menu-item.md | 38 ++- docs-translations/ko-KR/api/session.md | 285 ++++++++++-------- 4 files changed, 259 insertions(+), 145 deletions(-) diff --git a/docs-translations/ko-KR/api/app.md b/docs-translations/ko-KR/api/app.md index b6695bdf21a6..d8d639c86698 100644 --- a/docs-translations/ko-KR/api/app.md +++ b/docs-translations/ko-KR/api/app.md @@ -647,6 +647,33 @@ dock 아이콘을 표시합니다. dock 아이콘의 `image`를 설정합니다. +### `app.launcher.setBadgeCount(count)` _Linux_ +* `count` Integer + +Unity 런처에서 앱 아이콘 옆에 표시될 숫자를 설정합니다. `0`으로 설정하면 뱃지를 +숨깁니다. + +**참고:** 이 기능은 현재 Ubuntu Unity에서만 사용할 수 있습니다. 다른 환경에서 이 +함수를 호출하는 것은 아무런 효과가 없습니다. + +**참고:** 이 기능을 사용하려면 `package.json`의 `desktopName` 필드에 `.desktop` 파일 이름을 설정해야 합니다. 기본적으로 패키징된 애플리케이션의 `app.getName().desktop`을 사용합니다. + +### `app.launcher.getBadgeCount()` _Linux_ + +런처 아이콘 옆 배지의 카운터에 표시된 현재 값을 반환합니다. + +**참고:** `setBadgeCount`가 Ubuntu Unity에서만 지원하기 때문에, 애플리케이션이 다른 +플랫폼에서 돌아가고 있다면 이 메서드는 `0`을 반환합니다. + +### `app.launcher.isUnityRunning()` _Linux_ + +현재 데스크톱 환경이 Unity인지 여부를 반환합니다. Unity가 맞다면 `true`를 반환합니다. + +### `app.launcher.isCounterBadgeAvailable()` _Linux_ + +현재 데스크톱 환경이 애플리케이션 아이콘 카운터 뱃지를 사용할 수 있는지 여부를 +반환합니다. 사용할 수 있다면 `true`를 반환합니다. + [dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103 [tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx diff --git a/docs-translations/ko-KR/api/browser-window.md b/docs-translations/ko-KR/api/browser-window.md index 7adbc08a72da..d214a0270d0b 100644 --- a/docs-translations/ko-KR/api/browser-window.md +++ b/docs-translations/ko-KR/api/browser-window.md @@ -62,6 +62,38 @@ win.loadURL('https://github.com') 참고로 `ready-to-show` 이벤트를 사용하더라도 어플리케이션을 네이티브 느낌이 나도록 하기 위해 `backgroundColor`도 같이 설정하는 것을 권장합니다. +## 부모와 자식 윈도우 + +`parent` 옵션을 사용하면 자식 윈도우를 만들 수 있습니다: + +```javascript +let top = new BrowserWindow() +let child = new BrowserWindow({parent: top}) +``` + +`child` 윈도우는 언제나 `top` 윈도우의 상위에 표시됩니다. + +### 모달 윈도우 + +모달 윈도우는 부모 윈도우를 비활성화 시키는 자식 윈도우입니다. 모달 윈도우를 만드려면 `parent`, `modal` 옵션을 동시에 설정해야 합니다: + +```javascript +let child = new BrowserWindow({parent: top, modal: true, show: false}) +child.loadURL('https://github.com') +child.once('ready-to-show', () => { + child.show() +}) +``` + +### 플랫폼별 특이사항 + +* On macOS the child windows will keep the relative position to parent window + when parent window moves, while on Windows and Linux child windows will not + move. +* On Windows it is not supported to change parent window dynamically. +* On Linux the type of modal windows will be changed to `dialog`. +* On Linux many desktop environments do not support hiding a modal window. + ## Class: BrowserWindow `BrowserWindow`는 [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)를 @@ -125,6 +157,9 @@ On Windows it is * `show` Boolean - 윈도우가 생성되면 보여줄지 여부. 기본값은 `true`입니다. * `frame` Boolean - `false`로 지정하면 창을 [Frameless Window](frameless-window.md) 형태로 생성합니다. 기본값은 `true`입니다. +* `parent` BrowserWindow - 부모 윈도우를 설정합니다. 기본 값은 `null`입니다. +* `modal` Boolean - 이 윈도우가 모달 윈도우인지 여부를 설정합니다. 이 옵션은 자식 + 윈도우에서만 작동합니다. 기본값은 `false`입니다. * `acceptFirstMouse` Boolean - 윈도우가 비활성화 상태일 때 내부 콘텐츠 클릭 시 활성화 되는 동시에 단일 mouse-down 이벤트를 발생시킬지 여부. 기본값은 `false`입니다. * `disableAutoHideCursor` Boolean - 타이핑중 자동으로 커서를 숨길지 여부. 기본값은 @@ -528,6 +563,10 @@ let win = new BrowserWindow({width: 800, height: 600}); 윈도우가 사용자에게 표시되고 있는지 여부를 반환합니다. +### `win.isModal()` + +현재 윈도우가 모달 윈도우인지 여부를 반환합니다. + ### `win.maximize()` 윈도우를 최대화 시킵니다. @@ -1008,4 +1047,19 @@ Windows에선 WDA_MONITOR와 함께 SetWindowDisplayAffinity를 호출합니다. 윈도우가 포커스될 수 있는지 여부를 변경합니다. +### `win.setParentWindow(parent)` _Linux_ _macOS_ + +* `parent` BrowserWindow + +`parent` 인수를 현재 윈도우의 부모 윈도우로 설정합니다. `null`로 설정하면 +현재 윈도우를 상위 윈도우로 전환합니다. + +### `win.getParentWindow()` + +모든 부모 윈도우를 반환합니다. + +### `win.getChildWindows()` + +모든 자식 윈도우를 반환합니다. + [blink-feature-string]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.in diff --git a/docs-translations/ko-KR/api/menu-item.md b/docs-translations/ko-KR/api/menu-item.md index 3e011630bc8e..27ad7216c9f8 100644 --- a/docs-translations/ko-KR/api/menu-item.md +++ b/docs-translations/ko-KR/api/menu-item.md @@ -2,7 +2,7 @@ > 네이티브 애플리케이션 메뉴와 컨텍스트 메뉴에 아이템을 추가합니다. -[`menu`](menu.md)에서 예시를 확인할 수 있습니다. +[`Menu`](menu.md)에서 예시를 확인할 수 있습니다. ## Class: MenuItem @@ -12,8 +12,8 @@ * `options` Object * `click` Function - 메뉴 아이템이 클릭될 때 `click(menuItem, browserWindow, - event)` 형태로 호출 되는 콜백 함수 - * `role` String - 메뉴 아이템의 액션을 정의합니다; 이 속성을 지정하면 `click` + event)` 형태로 호출 되는 콜백 함수. + * `role` String - 메뉴 아이템의 액션을 정의합니다. 이 속성을 지정하면 `click` 속성이 무시됩니다. * `type` String - `MenuItem`의 타입 `normal`, `separator`, `submenu`, `checkbox` 또는 `radio`를 사용할 수 있습니다. 만약 값이 `Menu`가 아니면 @@ -72,20 +72,28 @@ macOS에서의 `role`은 다음 값을 추가로 가질 수 있습니다: macOS에서는 `role`을 지정할 때, `label`과 `accelerator`만 MenuItem에 효과가 적용되도록 변경되며, 다른 옵션들은 모두 무시됩니다. -## Instance Properties +### Instance Properties -다음 속성들은 존재하는 `MenuItem`에서 계속 변경될 수 있습니다: +다음은 `MenuItem`의 인스턴스에서 사용할 수 있는 속성입니다: - * `enabled` Boolean - * `visible` Boolean - * `checked` Boolean +#### `menuItem.enabled` -이 속성들의 의미는 위 옵션에서 설명한 것과 같습니다. +아이템이 활성화되어있는지 여부를 표시하는 Boolean 값입니다. 이 속성은 동적으로 변경될 +수 있습니다. -`checkbox` 메뉴 아이템은 선택될 때 해당 아이템의 `checked` 속성을 통해 활성화 그리고 -비활성화 상태인지를 표시합니다. 또한 `click` 함수를 지정하여 추가적인 작업을 할 수도 -있습니다. +#### `menuItem.visible` -`radio` 메뉴 아이템은 클릭되었을 때 `checked` 속성을 활성화 합니다. 그리고 같은 -메뉴의 인접한 모든 다른 아이템은 비활성화됩니다. 또한 `click` 함수를 지정하여 추가적인 -작업을 할 수도 있습니다. +아이템이 보여지고있는지 여부를 표시하는 Boolean 값입니다. 이 속성은 동적으로 변경될 +수 있습니다. + +#### `menuItem.checked` + +아이템이 선택되어있는지 여부를 반환하는 Boolean 값입니다. 이 속성은 동적으로 변경될 +수 있습니다. + +`checkbox` 메뉴 아이템은 선택되면 `checked` 속성을 토글합니다. + +`radio` 메뉴 아이템은 클릭되었을 때 `checked` 속성을 활성화 합니다. 그리고 +같은 메뉴의 모든 인접한 아이템에 대한 속성이 꺼집니다. + +추가적인 작업을 위해 `click` 함수를 추가할 수도 있습니다. diff --git a/docs-translations/ko-KR/api/session.md b/docs-translations/ko-KR/api/session.md index c2e3ca923dc0..626ca240db07 100644 --- a/docs-translations/ko-KR/api/session.md +++ b/docs-translations/ko-KR/api/session.md @@ -4,16 +4,15 @@ `session` 모듈은 새로운 `Session` 객체를 만드는데 사용할 수 있습니다. -또한 존재하는 [`BrowserWindow`](browser-window.md)의 -[`webContents`](web-contents.md)에서 `session` 속성으로 접근할 수도 있습니다. +또한 [`WebContents`](web-contents.md)의 `session` 속성이나 `session` 모듈을 통해 현재 존재하는 페이지의 `session`에 접근할 수 있습니다. ```javascript -const {BrowserWindow} = require('electron'); +const {session, BrowserWindow} = require('electron') -let win = new BrowserWindow({width: 800, height: 600}); -win.loadURL('http://github.com'); +let win = new BrowserWindow({width: 800, height: 600}) +win.loadURL('http://github.com') -let ses = win.webContents.session; +let ses = win.webContents.session ``` ## Methods @@ -75,92 +74,7 @@ session.defaultSession.on('will-download', (event, item, webContents) => { ### Instance Methods -`Session` 객체는 다음과 같은 메서드와 속성을 가지고 있습니다: - -#### `ses.cookies` - -`cookies` 속성은 쿠키를 조작하는 방법을 제공합니다. 예를 들어 다음과 같이 할 수 -있습니다: - -```javascript -// 모든 쿠키를 요청합니다. -session.defaultSession.cookies.get({}, (error, cookies) => { - console.log(cookies); -}); - -// url에 관련된 쿠키를 모두 가져옵니다. -session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => { - console.log(cookies); -}); - -// 지정한 쿠키 데이터를 설정합니다. -// 동일한 쿠키가 있으면 해당 쿠키를 덮어씁니다. -const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'}; -session.defaultSession.cookies.set(cookie, (error) => { - if (error) - console.error(error); -}); -``` - -#### `ses.cookies.get(filter, callback)` - -* `filter` Object - * `url` String (optional) - `url`에 해당하는 쿠키를 취득합니다. 이 속성을 - 생략하면 모든 url에서 찾습니다. - * `name` String (optional) - 쿠키의 이름입니다. - * `domain` String (optional) - 도메인 또는 서브 도메인에 일치하는 쿠키를 - 취득합니다. - * `path` String (optional) - `path`에 일치하는 쿠키를 취득합니다. - * `secure` Boolean (optional) - 보안 속성에 따라 쿠키를 필터링합니다. - * `session` Boolean (optional) - 세션 또는 지속성 쿠키를 필터링합니다. -* `callback` Function - -`details` 객체에서 묘사한 모든 쿠키를 요청합니다. 모든 작업이 끝나면 `callback`이 -`callback(error, cookies)` 형태로 호출됩니다. - -`cookies`는 `cookie` 객체의 배열입니다. - -* `cookie` Object - * `name` String - 쿠키의 이름. - * `value` String - 쿠키의 값. - * `domain` String - 쿠키의 도메인. - * `hostOnly` String - 쿠키가 호스트 전용인가에 대한 여부. - * `path` String - 쿠키의 경로. - * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부. - * `httpOnly` Boolean - 쿠키가 HTTP로만 표시되는지에 대한 여부. - * `session` Boolean - 쿠키가 세션 쿠키 또는 만료일이 있는 영구 쿠키인지에 대한 - 여부. - * `expirationDate` Double - (Option) UNIX 시간으로 표시되는 쿠키의 만료일에 - 대한 초 단위 시간. 세션 쿠키는 지원되지 않음. - -#### `ses.cookies.set(details, callback)` - -* `details` Object - * `url` String - 쿠키에 대한 `url` 링크. - * `name` String - 쿠키의 이름입니다. 기본적으로 비워두면 생략됩니다. - * `value` String - 쿠키의 값입니다. 기본적으로 비워두면 생략됩니다. - * `domain` String - 쿠키의 도메인입니다. 기본적으로 비워두면 생략됩니다. - * `path` String - 쿠키의 경로입니다. 기본적으로 비워두면 생략됩니다. - * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부입니다. 기본값은 - false입니다. - * `session` Boolean - 쿠키가 Http 전용으로 표시되는지에 대한 여부입니다. 기본값은 - false입니다. - * `expirationDate` Double (optional) - UNIX 시간으로 표시되는 쿠키의 만료일에 - 대한 초 단위 시간입니다. 생략되면 쿠키가 세션 쿠기가 되며 세션 사이에 유지되지 - 않게 됩니다. -* `callback` Function - -`details` 객체에 따라 쿠키를 설정합니다. 작업이 완료되면 `callback`이 -`callback(error)` 형태로 호출됩니다. - -#### `ses.cookies.remove(url, name, callback)` - -* `url` String - 쿠키와 관련된 URL입니다. -* `name` String - 지울 쿠키의 이름입니다. -* `callback` Function - -`url`과 `name`에 일치하는 쿠키를 삭제합니다. 작업이 완료되면 `callback`이 -`callback()` 형식으로 호출됩니다. +`Session` 객체는 다음과 같은 메서드를 가지고 있습니다: #### `ses.getCacheSize(callback)` @@ -354,16 +268,141 @@ session.defaultSession.allowNTLMCredentialsForDomains('*') #### `ses.getUserAgent()` -현재 세션에 대한 유저 에이전트를 표현하는 `String`을 반환합니다. +현재 세션의 유저 에이전트를 표현하는 `String`을 반환합니다. + +### Instance Properties + +다음은 `Session` 인스턴스에서 사용할 수 있는 속성들입니다: + +#### `ses.cookies` + +현재 세션의 `Cookies` 클래스 인스턴스를 반환합니다. #### `ses.webRequest` -`webRequest` API는 생명주기의 다양한 단계에 맞춰 요청 콘텐츠를 가로채거나 변경할 수 -있도록 합니다. +현재 세션의 `WebRequest` 클래스 인스턴스를 반환합니다. -각 API는 `filter`와 `listener`를 선택적으로 받을 수 있습니다. `listener`는 API의 -이벤트가 발생했을 때 `listener(details)` 형태로 호출되며 `defails`는 요청을 묘사하는 -객체입니다. `listener`에 `null`을 전달하면 이벤트 수신을 중지합니다. +#### `ses.protocol` + +현재 세션의 [protocol](protocol.md) 모듈 인스턴스를 반환합니다. + +```javascript +const {app, session} = require('electron') +const path = require('path') + +app.on('ready', function () { + const protocol = session.fromPartition(partitionName).protocol + protocol.registerFileProtocol('atom', function (request, callback) { + var url = request.url.substr(7) + callback({path: path.normalize(__dirname + '/' + url)}) + }, function (error) { + if (error) + console.error('Failed to register protocol') + }) +}) +``` + +## Class: Cookies + +`Cookies` 클래스는 쿠키를 탐색하고 조작하는 방법을 제공합니다. `Cookies` 클래스의 +인스턴스는 반드시 `Session` 클래스의 `cookies` 속성에서 접근해야 합니다. + +예를 들어: + +```javascript +// 모든 쿠키를 요청합니다. +session.defaultSession.cookies.get({}, (error, cookies) => { + console.log(cookies); +}); + +// url에 관련된 쿠키를 모두 가져옵니다. +session.defaultSession.cookies.get({url: 'http://www.github.com'}, (error, cookies) => { + console.log(cookies); +}); + +// 지정한 쿠키 데이터를 설정합니다. +// 동일한 쿠키가 있으면 해당 쿠키를 덮어씁니다. +const cookie = {url: 'http://www.github.com', name: 'dummy_name', value: 'dummy'}; +session.defaultSession.cookies.set(cookie, (error) => { + if (error) + console.error(error); +}); +``` + +### Instance Methods + +다음은 `Cookies` 객체에서 사용할 수 있는 메서드들입니다: + +#### `ses.cookies.get(filter, callback)` + +* `filter` Object + * `url` String (optional) - `url`에 해당하는 쿠키를 취득합니다. 이 속성을 + 생략하면 모든 url에서 찾습니다. + * `name` String (optional) - 쿠키의 이름입니다. + * `domain` String (optional) - 도메인 또는 서브 도메인에 일치하는 쿠키를 + 취득합니다. + * `path` String (optional) - `path`에 일치하는 쿠키를 취득합니다. + * `secure` Boolean (optional) - 보안 속성에 따라 쿠키를 필터링합니다. + * `session` Boolean (optional) - 세션 또는 지속성 쿠키를 필터링합니다. +* `callback` Function + +`details` 객체에서 묘사한 모든 쿠키를 요청합니다. 모든 작업이 끝나면 `callback`이 +`callback(error, cookies)` 형태로 호출됩니다. + +`cookies`는 `cookie` 객체의 배열입니다. + +* `cookie` Object + * `name` String - 쿠키의 이름. + * `value` String - 쿠키의 값. + * `domain` String - 쿠키의 도메인. + * `hostOnly` String - 쿠키가 호스트 전용인가에 대한 여부. + * `path` String - 쿠키의 경로. + * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부. + * `httpOnly` Boolean - 쿠키가 HTTP로만 표시되는지에 대한 여부. + * `session` Boolean - 쿠키가 세션 쿠키 또는 만료일이 있는 영구 쿠키인지에 대한 + 여부. + * `expirationDate` Double - (Option) UNIX 시간으로 표시되는 쿠키의 만료일에 + 대한 초 단위 시간. 세션 쿠키는 지원되지 않음. + +#### `ses.cookies.set(details, callback)` + +* `details` Object + * `url` String - 쿠키에 대한 `url` 링크. + * `name` String - 쿠키의 이름입니다. 기본적으로 비워두면 생략됩니다. + * `value` String - 쿠키의 값입니다. 기본적으로 비워두면 생략됩니다. + * `domain` String - 쿠키의 도메인입니다. 기본적으로 비워두면 생략됩니다. + * `path` String - 쿠키의 경로입니다. 기본적으로 비워두면 생략됩니다. + * `secure` Boolean - 쿠키가 안전한 것으로 표시되는지에 대한 여부입니다. 기본값은 + false입니다. + * `httpOnly` Boolean - 쿠키가 Http 전용으로 표시되는지에 대한 여부입니다. 기본값은 + false입니다. + * `expirationDate` Double (optional) - UNIX 시간으로 표시되는 쿠키의 만료일에 + 대한 초 단위 시간입니다. 생략되면 쿠키가 세션 쿠기가 되며 세션 사이에 유지되지 + 않게 됩니다. +* `callback` Function + +`details` 객체에 따라 쿠키를 설정합니다. 작업이 완료되면 `callback`이 +`callback(error)` 형태로 호출됩니다. + +#### `ses.cookies.remove(url, name, callback)` + +* `url` String - 쿠키와 관련된 URL입니다. +* `name` String - 지울 쿠키의 이름입니다. +* `callback` Function + +`url`과 `name`에 일치하는 쿠키를 삭제합니다. 작업이 완료되면 `callback`이 +`callback()` 형식으로 호출됩니다. + +## Class: WebRequest + +`WebRequest` 클래스는 생명 주기의 다양한 단계에서 요청의 콘텐츠를 조작하거나 가로채는 +방법을 제공합니다. `WebRequest` 클래스는 반드시 `Session` 클래스의 `webRequest` +속성에서 접근해야 합니다. + +`WebRequest`의 메서드는 선택적인 `filter`와 `listener` 속성을 허용하며 `listener`는 +API의 이벤트가 발생했을 때 `listener(details)` 형식으로 호출되고, `details`는 요청에 +관한 내용을 표현하는 객체입니다. `listener`에 `null`을 전달하면 이벤트의 구독을 +해제합니다. `filter`는 `urls` 속성을 가진 객체입니다. 이 속성은 URL 규칙의 배열이며 URL 규칙에 일치하지 않는 요청을 모두 거르는데 사용됩니다. 만약 `filter`가 생략되면 모든 요청을 @@ -372,19 +411,25 @@ session.defaultSession.allowNTLMCredentialsForDomains('*') 어떤 `listener`의 이벤트들은 `callback`을 같이 전달하는데, 이벤트 처리시 `listener`의 작업을 완료한 후 `response` 객체를 포함하여 호출해야 합니다. +다음은 요청에 `User-Agent` 헤더를 추가하는 예시입니다: + ```javascript // 다음 url에 대한 User Agent를 조작합니다. const filter = { urls: ['https://*.github.com/*', '*://electron.github.io'] -}; +} session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => { - details.requestHeaders['User-Agent'] = "MyAgent"; - callback({cancel: false, requestHeaders: details.requestHeaders}); -}); + details.requestHeaders['User-Agent'] = "MyAgent" + callback({cancel: false, requestHeaders: details.requestHeaders}) +}) ``` -#### `ses.webRequest.onBeforeRequest([filter, ]listener)` +### Instance Methods + +다음은 `WebRequest` 객체에서 사용할 수 있는 메서드들입니다: + +#### `webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object * `listener` Function @@ -413,7 +458,7 @@ session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback * `redirectURL` String (optional) - 원래 요청은 전송과 완료가 방지되지만 이 속성을 지정하면 해당 URL로 리다이렉트됩니다. -#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` +#### `webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -438,7 +483,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `requestHeaders` Object (optional) - 이 속성이 제공되면, 요청은 이 헤더로 만들어 집니다. -#### `ses.webRequest.onSendHeaders([filter, ]listener)` +#### `webRequest.onSendHeaders([filter, ]listener)` * `filter` Object * `listener` Function @@ -455,7 +500,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `timestamp` Double * `requestHeaders` Object -#### `ses.webRequest.onHeadersReceived([filter, ]listener)` +#### `webRequest.onHeadersReceived([filter, ]listener)` * `filter` Object * `listener` Function @@ -484,7 +529,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener 변경하기 위해 반드시 지정되어야 합니다. 그렇지 않은 경우, 기존의 응답 헤더의 상태가 사용됩니다. -#### `ses.webRequest.onResponseStarted([filter, ]listener)` +#### `webRequest.onResponseStarted([filter, ]listener)` * `filter` Object * `listener` Function @@ -503,7 +548,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` +#### `webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object * `listener` Function @@ -523,7 +568,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `fromCache` Boolean * `responseHeaders` Object -#### `ses.webRequest.onCompleted([filter, ]listener)` +#### `webRequest.onCompleted([filter, ]listener)` * `filter` Object * `listener` Function @@ -541,7 +586,7 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `statusCode` Integer * `statusLine` String -#### `ses.webRequest.onErrorOccurred([filter, ]listener)` +#### `webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object * `listener` Function @@ -556,23 +601,3 @@ HTTP 요청을 보내기 전 요청 헤더를 사용할 수 있을 때 `listener * `timestamp` Double * `fromCache` Boolean * `error` String - 에러 설명. - -#### `ses.protocol` - -현재 세션의 [protocol](protocol.md) 모듈 인스턴스를 반환합니다. - -```javascript -const {app, session} = require('electron') -const path = require('path') - -app.on('ready', function () { - const protocol = session.fromPartition(partitionName).protocol - protocol.registerFileProtocol('atom', function (request, callback) { - var url = request.url.substr(7) - callback({path: path.normalize(__dirname + '/' + url)}) - }, function (error) { - if (error) - console.error('Failed to register protocol') - }) -}) -``` From 76f4bd01eb6e9d31f39ec945a680528b8ec82323 Mon Sep 17 00:00:00 2001 From: Jhen Date: Thu, 30 Jun 2016 15:45:03 +0800 Subject: [PATCH 162/192] Support background.page in extension manifest --- lib/browser/chrome-extension.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/browser/chrome-extension.js b/lib/browser/chrome-extension.js index e3cd4540780e..68b86ee4983c 100644 --- a/lib/browser/chrome-extension.js +++ b/lib/browser/chrome-extension.js @@ -72,10 +72,15 @@ const backgroundPages = {} const startBackgroundPages = function (manifest) { if (backgroundPages[manifest.extensionId] || !manifest.background) return - const scripts = manifest.background.scripts.map((name) => { - return `` - }).join('') - const html = new Buffer(`${scripts}`) + let html + if (manifest.background.page) { + html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page)) + } else { + const scripts = manifest.background.scripts.map((name) => { + return `` + }).join('') + html = new Buffer(`${scripts}`) + } const contents = webContents.create({ isBackgroundPage: true, From 83e9521d1397c661914b82c659d7fd6947de2c8a Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 1 Jul 2016 14:27:56 +0900 Subject: [PATCH 163/192] Update atom-shell-vs-node-webkit.md --- docs/development/atom-shell-vs-node-webkit.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/development/atom-shell-vs-node-webkit.md b/docs/development/atom-shell-vs-node-webkit.md index 3ec11b78b7d1..0912a8d66e31 100644 --- a/docs/development/atom-shell-vs-node-webkit.md +++ b/docs/development/atom-shell-vs-node-webkit.md @@ -47,4 +47,6 @@ By using the [multi-context](http://strongloop.com/strongblog/whats-new-node-js- feature of Node, Electron doesn't introduce a new JavaScript context in web pages. +Note: NW.js has optionally supported multi-context since 0.13. + [node-bindings]: https://github.com/electron/electron/tree/master/atom/common From 1626c3bf0ae20a4eed46d7b329739bce873065e7 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 1 Jul 2016 14:30:55 +0900 Subject: [PATCH 164/192] Use `*` instead of `-` for lists `*` is used in most lists in our docs, let's keep consistency. --- docs/styleguide.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index e0080ba08cb2..36f354b07c14 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -92,13 +92,13 @@ Using `autoUpdater` as an example: ### Classes -- API classes or classes that are part of modules must be listed under a +* API classes or classes that are part of modules must be listed under a `## Class: TheClassName` chapter. -- One page can have multiple classes. -- The constructors must be listed with `###`-level titles. -- The methods must be listed under an `### Instance Methods` chapter. -- The events must be listed under an `### Instance Events` chapter. -- The properties must be listed under an `### Instance Properties` chapter. +* One page can have multiple classes. +* The constructors must be listed with `###`-level titles. +* The methods must be listed under an `### Instance Methods` chapter. +* The events must be listed under an `### Instance Events` chapter. +* The properties must be listed under an `### Instance Properties` chapter. Using the `Session` and `Cookies` classes as an example: @@ -168,12 +168,12 @@ required[, optional] Below the method is more detailed information on each of the arguments. The type of argument is notated by either the common types: -- [`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) -- [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) -- [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) -- [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) -- [`Boolean`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean) -- Or a custom type like Electron's [`WebContent`](api/web-content.md) +* [`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) +* [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number) +* [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) +* [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) +* [`Boolean`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean) +* Or a custom type like Electron's [`WebContent`](api/web-content.md) If an argument or a method is unique to certain platforms, those platforms are denoted using a space-delimited italicized list following the datatype. Values From 39654409529b6a48cb3982bcd7c10c0a1d50920c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 1 Jul 2016 14:33:22 +0900 Subject: [PATCH 165/192] conjunctions don't have to be capitalized --- docs/styleguide.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/styleguide.md b/docs/styleguide.md index 36f354b07c14..ba7d707fd89f 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -6,9 +6,10 @@ These are the guidelines for writing Electron documentation. * Each page must have a single `#`-level title at the top. * Chapters in the same page must have `##`-level titles. -* Sub-chapters need to increase the number of `#` in the title according to their - nesting depth. -* All words in the page's title must be capitalized. +* Sub-chapters need to increase the number of `#` in the title according to + their nesting depth. +* All words in the page's title must be capitalized, except for conjunctions + like "of" and "and" . * Only the first word of a chapter title must be capitalized. Using `Quick Start` as example: From cb00d3cfb3dc488f6a2d161283620f734ae7507b Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 1 Jul 2016 17:39:01 +0900 Subject: [PATCH 166/192] mac: Implement app.setBadgeCount --- atom/browser/api/atom_api_app.cc | 19 +++++++----------- atom/browser/browser.cc | 4 ++++ atom/browser/browser.h | 12 +++++------ atom/browser/browser_linux.cc | 18 ++++++----------- atom/browser/browser_mac.mm | 6 ++++++ atom/browser/browser_win.cc | 4 ++++ spec/api-app-spec.js | 34 ++++---------------------------- 7 files changed, 37 insertions(+), 60 deletions(-) diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index ce6a7d33c6a4..a39f9809ebf0 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -520,6 +520,8 @@ void App::BuildPrototype( base::Bind(&Browser::SetAsDefaultProtocolClient, browser)) .SetMethod("removeAsDefaultProtocolClient", base::Bind(&Browser::RemoveAsDefaultProtocolClient, browser)) + .SetMethod("setBadgeCount", base::Bind(&Browser::SetBadgeCount, browser)) + .SetMethod("getBadgeCount", base::Bind(&Browser::GetBadgeCount, browser)) #if defined(OS_MACOSX) .SetMethod("hide", base::Bind(&Browser::Hide, browser)) .SetMethod("show", base::Bind(&Browser::Show, browser)) @@ -529,8 +531,11 @@ void App::BuildPrototype( base::Bind(&Browser::GetCurrentActivityType, browser)) #endif #if defined(OS_WIN) - .SetMethod("setUserTasks", - base::Bind(&Browser::SetUserTasks, browser)) + .SetMethod("setUserTasks", base::Bind(&Browser::SetUserTasks, browser)) +#endif +#if defined(OS_LINUX) + .SetMethod("isUnityRunning", + base::Bind(&Browser::IsUnityRunning, browser)) #endif .SetMethod("setPath", &App::SetPath) .SetMethod("getPath", &App::GetPath) @@ -614,16 +619,6 @@ void Initialize(v8::Local exports, v8::Local unused, dict.SetMethod("dockSetMenu", &DockSetMenu); dict.SetMethod("dockSetIcon", base::Bind(&Browser::DockSetIcon, browser)); #endif - -#if defined(OS_LINUX) - auto browser = base::Unretained(Browser::Get()); - dict.SetMethod("unityLauncherAvailable", - base::Bind(&Browser::UnityLauncherAvailable, browser)); - dict.SetMethod("unityLauncherSetBadgeCount", - base::Bind(&Browser::UnityLauncherSetBadgeCount, browser)); - dict.SetMethod("unityLauncherGetBadgeCount", - base::Bind(&Browser::UnityLauncherGetBadgeCount, browser)); -#endif } } // namespace diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 8c00c1287376..3267653a7457 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -118,6 +118,10 @@ void Browser::SetName(const std::string& name) { name_override_ = name; } +int Browser::GetBadgeCount() { + return current_badge_count_; +} + bool Browser::OpenFile(const std::string& file_path) { bool prevent_default = false; FOR_EACH_OBSERVER(BrowserObserver, diff --git a/atom/browser/browser.h b/atom/browser/browser.h index c9155d94b4e3..5f496b89eeed 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -87,6 +87,10 @@ class Browser : public WindowListObserver { // Query the current state of default handler for a protocol. bool IsDefaultProtocolClient(const std::string& protocol); + // Set/Get the badge count. + void SetBadgeCount(int count); + int GetBadgeCount(); + #if defined(OS_MACOSX) // Hide the application. void Hide(); @@ -152,10 +156,8 @@ class Browser : public WindowListObserver { #endif // defined(OS_WIN) #if defined(OS_LINUX) - // Set/Get unity dock's badge counter. - bool UnityLauncherAvailable(); - void UnityLauncherSetBadgeCount(int count); - int UnityLauncherGetBadgeCount(); + // Whether Unity launcher is running. + bool IsUnityRunning(); #endif // defined(OS_LINUX) // Tell the application to open a file. @@ -223,9 +225,7 @@ class Browser : public WindowListObserver { std::string version_override_; std::string name_override_; -#if defined(OS_LINUX) int current_badge_count_ = 0; -#endif #if defined(OS_WIN) base::string16 app_user_model_id_; diff --git a/atom/browser/browser_linux.cc b/atom/browser/browser_linux.cc index 8e41ec8c1997..ff48e5bed040 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -47,6 +47,11 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { return false; } +void Browser::SetBadgeCount(int count) { + current_badge_count_ = count; + unity::SetDownloadCount(count); +} + std::string Browser::GetExecutableFileVersion() const { return brightray::GetApplicationVersion(); } @@ -55,19 +60,8 @@ std::string Browser::GetExecutableFileProductName() const { return brightray::GetApplicationName(); } -bool Browser::UnityLauncherAvailable() { +bool Browser::IsUnityRunning() { return unity::IsRunning(); } -void Browser::UnityLauncherSetBadgeCount(int count) { - if (UnityLauncherAvailable()) { - current_badge_count_ = count; - unity::SetDownloadCount(count); - } -} - -int Browser::UnityLauncherGetBadgeCount() { - return current_badge_count_; -} - } // namespace atom diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index a50de2a07f60..ebd225c5996f 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -12,6 +12,7 @@ #include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" #include "base/strings/sys_string_conversions.h" +#include "base/strings/string_number_conversions.h" #include "brightray/common/application_info.h" #include "net/base/mac/url_conversions.h" #include "url/gurl.h" @@ -114,6 +115,11 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { void Browser::SetAppUserModelID(const base::string16& name) { } +void Browser::SetBadgeCount(int count) { + current_badge_count_ = count; + DockSetBadgeText(count == 0 ? base::IntToString(count) : ""); +} + void Browser::SetUserActivity(const std::string& type, const base::DictionaryValue& user_info, mate::Arguments* args) { diff --git a/atom/browser/browser_win.cc b/atom/browser/browser_win.cc index 345e2bcf67ce..e49e3b4cee04 100644 --- a/atom/browser/browser_win.cc +++ b/atom/browser/browser_win.cc @@ -269,6 +269,10 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { } } +void Browser::SetBadgeCount(int count) { + current_badge_count_ = count; +} + PCWSTR Browser::GetAppUserModelID() { if (app_user_model_id_.empty()) { SetAppUserModelID(base::ReplaceStringPlaceholders( diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 214a084f2ec7..4d27755d278b 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -285,36 +285,10 @@ describe('app module', function () { }) }) - describe('app.launcher API', function () { - it('should be available on linux', function () { - if (process.platform !== 'linux') { - assert.equal(app.launcher, undefined) - } else { - assert.notEqual(app.launcher, undefined) - } - }) - - it('should be possible to set a badge count on supported environments', function () { - if (process.platform === 'linux' && - app.launcher.isCounterBadgeAvailable()) { - app.launcher.setBadgeCount(42) - assert.equal(app.launcher.getBadgeCount(), 42) - } - }) - - it('should be possible to set a badge count on unity', function () { - if (process.platform === 'linux' && - app.launcher.isUnityRunning()) { - assert.equal(app.launcher.isCounterBadgeAvailable(), true) - } - }) - - it('should not be possible to set a badge counter on unsupported environments', function () { - if (process.platform === 'linux' && - !app.launcher.isCounterBadgeAvailable()) { - app.launcher.setBadgeCount(42) - assert.equal(app.launcher.getBadgeCount(), 0) - } + describe('app.getBadgeCount API', function () { + it('should set a badge count', function () { + app.setBadgeCount(42) + assert.equal(app.getBadgeCount(), 42) }) }) }) From f8ec3771237915d13badc31f77e042d2176ea933 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 1 Jul 2016 17:44:09 +0900 Subject: [PATCH 167/192] docs: app.setBadgeCount --- docs/api/app.md | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index 260e64533b15..128300f53abe 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -577,6 +577,26 @@ Disables hardware acceleration for current app. This method can only be called before app is ready. +### `app.setBadgeCount(count)` _Linux_ _macOS_ + +* `count` Integer + +Sets the counter badge for current app. Setting the count to `0` will hide the +badge. + +On macOS it shows on the dock icon. On Linux it only works for Unity launcher, + +**Note:** Unity launcher requires the exsistence of `.desktop` file to work, for +more please read [Desktop Environment Integration][unity-requiremnt] + +### `app.getBadgeCount()` _Linux_ _macOS_ + +Returns the current value displayed in the counter badge. + +### `app.isUnityRunning()` _Linux_ + +Returns whether current desktop environment is Unity launcher. + ### `app.commandLine.appendSwitch(switch[, value])` Append a switch (with optional `value`) to Chromium's command line. @@ -647,31 +667,6 @@ Sets the application's [dock menu][dock-menu]. Sets the `image` associated with this dock icon. -### `app.launcher.setBadgeCount(count)` _Linux_ -* `count` Integer - -Sets the number to be displayed next to the app icon in the Unity launcher. -Setting the count to `0` will hide the badge. - -**Note:** This feature is currently only supported on Ubuntu Unity. Calling this function has no effect when the application is running in a different environment. - -**Note:** You need to specify the .desktop file name to the `desktopName` field in package.json. By default, it will assume `app.getName().desktop` in packaged apps. - -### `app.launcher.getBadgeCount()` _Linux_ - -Returns the current value displayed in the counter badge next to the launcher icon. - -**Note:** As `setBadgeCount` only supports Ubuntu Unity, the value will be 0 when the application is running in a different environment. - -### `app.launcher.isUnityRunning()` _Linux_ - -This method checks if the current desktop environment is Unity and returns `true` in this case. - -### `app.launcher.isCounterBadgeAvailable()` _Linux_ - -This method checks if the current desktop environment supports an app icon counter badge and returns `true` in this case. - - [dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103 [tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx @@ -679,3 +674,4 @@ This method checks if the current desktop environment supports an app icon count [LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme [handoff]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html [activity-type]: https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType +[unity-requiremnt]: ../tutorial/desktop-environment-integration.md#unity-launcher-shortcuts-linux From 54f06b0ba95fed5726396ac28471972e07b426e1 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Fri, 1 Jul 2016 23:12:06 +1200 Subject: [PATCH 168/192] Override the Notification API constructor to create silent notifications when the webContents is muted --- lib/renderer/override.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/renderer/override.js b/lib/renderer/override.js index c56838887676..fb81c24e555f 100644 --- a/lib/renderer/override.js +++ b/lib/renderer/override.js @@ -248,3 +248,14 @@ Object.defineProperty(document, 'visibilityState', { return cachedVisibilityState } }) + +// Make notifications silent if the the current webContents is muted +const NativeNotification = window.Notification +class Notification extends NativeNotification { + constructor (title, opts) { + super(title, Object.assign({ + silent: remote.getCurrentWebContents().isAudioMuted() + }, opts)) + } +} +window.Notification = Notification From 1da2a1c2ae39a05ef5bb9e28f93632ede4e6351d Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 1 Jul 2016 22:18:39 +0900 Subject: [PATCH 169/192] app.setBadgeCount should return boolean --- atom/browser/browser.h | 2 +- atom/browser/browser_linux.cc | 11 ++++++++--- atom/browser/browser_mac.mm | 5 +++-- atom/browser/browser_win.cc | 4 ++-- docs/api/app.md | 2 +- spec/api-app-spec.js | 11 +++++++++-- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 5f496b89eeed..bdaed5be0f1a 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -88,7 +88,7 @@ class Browser : public WindowListObserver { bool IsDefaultProtocolClient(const std::string& protocol); // Set/Get the badge count. - void SetBadgeCount(int count); + bool SetBadgeCount(int count); int GetBadgeCount(); #if defined(OS_MACOSX) diff --git a/atom/browser/browser_linux.cc b/atom/browser/browser_linux.cc index ff48e5bed040..e2f968613b36 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -47,9 +47,14 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { return false; } -void Browser::SetBadgeCount(int count) { - current_badge_count_ = count; - unity::SetDownloadCount(count); +bool Browser::SetBadgeCount(int count) { + if (IsUnityRunning()) { + unity::SetDownloadCount(count); + current_badge_count_ = count; + return true; + } else { + return false; + } } std::string Browser::GetExecutableFileVersion() const { diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index ebd225c5996f..a02034097ad5 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -115,9 +115,10 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { void Browser::SetAppUserModelID(const base::string16& name) { } -void Browser::SetBadgeCount(int count) { +bool Browser::SetBadgeCount(int count) { + DockSetBadgeText(count != 0 ? base::IntToString(count) : ""); current_badge_count_ = count; - DockSetBadgeText(count == 0 ? base::IntToString(count) : ""); + return true; } void Browser::SetUserActivity(const std::string& type, diff --git a/atom/browser/browser_win.cc b/atom/browser/browser_win.cc index e49e3b4cee04..5eb9275dcff6 100644 --- a/atom/browser/browser_win.cc +++ b/atom/browser/browser_win.cc @@ -269,8 +269,8 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { } } -void Browser::SetBadgeCount(int count) { - current_badge_count_ = count; +bool Browser::SetBadgeCount(int count) { + return false; } PCWSTR Browser::GetAppUserModelID() { diff --git a/docs/api/app.md b/docs/api/app.md index 128300f53abe..2a640de75e64 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -582,7 +582,7 @@ This method can only be called before app is ready. * `count` Integer Sets the counter badge for current app. Setting the count to `0` will hide the -badge. +badge. Returns `true` when the call succeeded, otherwise returns `false`. On macOS it shows on the dock icon. On Linux it only works for Unity launcher, diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 4d27755d278b..44d4ddd019e4 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -285,10 +285,17 @@ describe('app module', function () { }) }) - describe('app.getBadgeCount API', function () { + describe('app.setBadgeCount API', function () { + const shouldFail = process.platform === 'win32' || + (process.platform === 'linux' && app.isUnityRunning()) + + it('returns false when failed', function () { + assert.equal(app.setBadgeCount(42), !shouldFail) + }) + it('should set a badge count', function () { app.setBadgeCount(42) - assert.equal(app.getBadgeCount(), 42) + assert.equal(app.getBadgeCount(), shouldFail ? 0 : 42) }) }) }) From bb92765ecf79a245c8a471c8a98e9f4b0221c22c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 1 Jul 2016 22:24:30 +0900 Subject: [PATCH 170/192] spec: Suppress the session.protocol test --- spec/api-session-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index a7af6d7bdbb0..0557508e6da8 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -316,7 +316,7 @@ describe('session module', function () { }) }) - it('handles requests from partition', function (done) { + xit('handles requests from partition', function (done) { w.webContents.on('did-finish-load', function () { done() }) From 431ea61ff8bbb4cd77030cc01325dd896247d910 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 1 Jul 2016 22:55:15 +0900 Subject: [PATCH 171/192] Don't use MatchesExtension for matching filters MatchesExtension compares double extensions like .tar.gz, which does not match the filters. --- atom/browser/ui/file_dialog_gtk.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/atom/browser/ui/file_dialog_gtk.cc b/atom/browser/ui/file_dialog_gtk.cc index dcb7bea86b2b..eba1813780ad 100644 --- a/atom/browser/ui/file_dialog_gtk.cc +++ b/atom/browser/ui/file_dialog_gtk.cc @@ -211,7 +211,9 @@ base::FilePath FileChooserDialog::AddExtensionForFilename( const auto& extensions = filters_[i].second; for (const auto& extension : extensions) { - if (extension == "*" || path.MatchesExtension("." + extension)) + if (extension == "*" || + base::EndsWith(path.value(), "." + extension, + base::CompareCase::INSENSITIVE_ASCII)) return path; } From d4f64ce94307d3897231fd842e4a37fa42ef2d0a Mon Sep 17 00:00:00 2001 From: Jhen Date: Fri, 1 Jul 2016 23:05:00 +0800 Subject: [PATCH 172/192] Use background page path instead of generated path if extension use manifest.background.page --- lib/browser/chrome-extension.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/browser/chrome-extension.js b/lib/browser/chrome-extension.js index 68b86ee4983c..4bb4a51c191d 100644 --- a/lib/browser/chrome-extension.js +++ b/lib/browser/chrome-extension.js @@ -73,9 +73,12 @@ const startBackgroundPages = function (manifest) { if (backgroundPages[manifest.extensionId] || !manifest.background) return let html + let name if (manifest.background.page) { + name = manifest.background.page html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page)) } else { + name = '_generated_background_page.html' const scripts = manifest.background.scripts.map((name) => { return `` }).join('') @@ -86,12 +89,12 @@ const startBackgroundPages = function (manifest) { isBackgroundPage: true, commandLineSwitches: ['--background-page'] }) - backgroundPages[manifest.extensionId] = { html: html, webContents: contents } + backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name } contents.loadURL(url.format({ protocol: 'chrome-extension', slashes: true, hostname: manifest.extensionId, - pathname: '_generated_background_page.html' + pathname: name })) } @@ -314,11 +317,11 @@ app.once('ready', function () { const manifest = manifestMap[parsed.hostname] if (!manifest) return callback() - if (parsed.path === '/_generated_background_page.html' && - backgroundPages[parsed.hostname]) { + const page = backgroundPages[parsed.hostname] + if (page && parsed.path === `/${page.name}`) { return callback({ mimeType: 'text/html', - data: backgroundPages[parsed.hostname].html + data: page.html }) } From b47d0e3e583568b07f0bd9f29724f1fc9b3a66d0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 1 Jul 2016 08:51:25 -0700 Subject: [PATCH 173/192] Tweak launcher note text --- docs/api/app.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index 2a640de75e64..cf3d785c54b7 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -586,8 +586,8 @@ badge. Returns `true` when the call succeeded, otherwise returns `false`. On macOS it shows on the dock icon. On Linux it only works for Unity launcher, -**Note:** Unity launcher requires the exsistence of `.desktop` file to work, for -more please read [Desktop Environment Integration][unity-requiremnt] +**Note:** Unity launcher requires the exsistence of a `.desktop` file to work, +for more information please read [Desktop Environment Integration][unity-requiremnt]. ### `app.getBadgeCount()` _Linux_ _macOS_ From 4f660f3e6f2e64abb5815f878a4de7ea64dada6c Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 2 Jul 2016 13:28:28 +1200 Subject: [PATCH 174/192] Implement WebContenstAudioMuted in AtomBrowserClient --- atom/browser/atom_browser_client.cc | 7 +++++++ atom/browser/atom_browser_client.h | 2 ++ lib/renderer/override.js | 11 ----------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index c1b50459c680..13503ae57d7d 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -303,6 +303,13 @@ void AtomBrowserClient::WebNotificationAllowed( permission_helper->RequestWebNotificationPermission(callback); } +bool AtomBrowserClient::WebContentsAudioMuted( + int render_process_id) { + content::WebContents* web_contents = + WebContentsPreferences::GetWebContentsFromProcessID(render_process_id); + return web_contents->IsAudioMuted(); + } + void AtomBrowserClient::RenderProcessHostDestroyed( content::RenderProcessHost* host) { int process_id = host->GetID(); diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index cf1a4cc438b6..47d743449744 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -101,6 +101,8 @@ class AtomBrowserClient : public brightray::BrowserClient, void WebNotificationAllowed( int render_process_id, const base::Callback& callback) override; + bool WebContentsAudioMuted( + int render_process_id) override; // content::RenderProcessHostObserver: void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; diff --git a/lib/renderer/override.js b/lib/renderer/override.js index fb81c24e555f..c56838887676 100644 --- a/lib/renderer/override.js +++ b/lib/renderer/override.js @@ -248,14 +248,3 @@ Object.defineProperty(document, 'visibilityState', { return cachedVisibilityState } }) - -// Make notifications silent if the the current webContents is muted -const NativeNotification = window.Notification -class Notification extends NativeNotification { - constructor (title, opts) { - super(title, Object.assign({ - silent: remote.getCurrentWebContents().isAudioMuted() - }, opts)) - } -} -window.Notification = Notification From 767124c45cfe1347a48b3f1ae3d640d8458f5c91 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 2 Jul 2016 10:36:11 +0900 Subject: [PATCH 175/192] Update for reviews --- atom/browser/browser.cc | 2 +- atom/browser/browser.h | 2 +- atom/browser/browser_linux.cc | 2 +- atom/browser/browser_mac.mm | 4 ++-- spec/api-app-spec.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 3267653a7457..b7d5626a82e3 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -119,7 +119,7 @@ void Browser::SetName(const std::string& name) { } int Browser::GetBadgeCount() { - return current_badge_count_; + return badge_count_; } bool Browser::OpenFile(const std::string& file_path) { diff --git a/atom/browser/browser.h b/atom/browser/browser.h index bdaed5be0f1a..37d2e11e6799 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -225,7 +225,7 @@ class Browser : public WindowListObserver { std::string version_override_; std::string name_override_; - int current_badge_count_ = 0; + int badge_count_ = 0; #if defined(OS_WIN) base::string16 app_user_model_id_; diff --git a/atom/browser/browser_linux.cc b/atom/browser/browser_linux.cc index e2f968613b36..fe1a0af9ba3a 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -50,7 +50,7 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol) { bool Browser::SetBadgeCount(int count) { if (IsUnityRunning()) { unity::SetDownloadCount(count); - current_badge_count_ = count; + badge_count_ = count; return true; } else { return false; diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index a02034097ad5..bb789365ffb2 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -11,8 +11,8 @@ #include "atom/browser/window_list.h" #include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" -#include "base/strings/sys_string_conversions.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/sys_string_conversions.h" #include "brightray/common/application_info.h" #include "net/base/mac/url_conversions.h" #include "url/gurl.h" @@ -117,7 +117,7 @@ void Browser::SetAppUserModelID(const base::string16& name) { bool Browser::SetBadgeCount(int count) { DockSetBadgeText(count != 0 ? base::IntToString(count) : ""); - current_badge_count_ = count; + badge_count_ = count; return true; } diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 44d4ddd019e4..111d387d3404 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -287,7 +287,7 @@ describe('app module', function () { describe('app.setBadgeCount API', function () { const shouldFail = process.platform === 'win32' || - (process.platform === 'linux' && app.isUnityRunning()) + (process.platform === 'linux' && !app.isUnityRunning()) it('returns false when failed', function () { assert.equal(app.setBadgeCount(42), !shouldFail) From cd38e5c85e43e9aa4024113032ae10d04a112dfd Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 2 Jul 2016 13:54:34 +0900 Subject: [PATCH 176/192] Update to Chrome 51.0.2704.106 --- atom/common/chrome_version.h | 2 +- script/lib/config.py | 2 +- vendor/brightray | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/atom/common/chrome_version.h b/atom/common/chrome_version.h index 3c3b780b9ce0..66870f4d6c38 100644 --- a/atom/common/chrome_version.h +++ b/atom/common/chrome_version.h @@ -8,7 +8,7 @@ #ifndef ATOM_COMMON_CHROME_VERSION_H_ #define ATOM_COMMON_CHROME_VERSION_H_ -#define CHROME_VERSION_STRING "51.0.2704.103" +#define CHROME_VERSION_STRING "51.0.2704.106" #define CHROME_VERSION "v" CHROME_VERSION_STRING #endif // ATOM_COMMON_CHROME_VERSION_H_ diff --git a/script/lib/config.py b/script/lib/config.py index 125cecb5a35e..08297c58a272 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -8,7 +8,7 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ 'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = '2932c73bec8852c8cdf4b18f8b6372075c3e651e' +LIBCHROMIUMCONTENT_COMMIT = '31144d583c19b70d8d9de7d4ef15f0f720779860' PLATFORM = { 'cygwin': 'win32', diff --git a/vendor/brightray b/vendor/brightray index 25d39ad99a77..f9bc45b03c94 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 25d39ad99a7738385af0947804261608d99765ab +Subproject commit f9bc45b03c94a2db729b53885691db5e04b757df From e76ec92812da70641986fe24278723ea8014fd43 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 2 Jul 2016 16:16:47 +0900 Subject: [PATCH 177/192] Set window icon before showing window --- atom/browser/api/atom_api_window.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index ae6bb77cf9bc..ac5fb87c7535 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -106,9 +106,6 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) { options, parent.IsEmpty() ? nullptr : parent->window_.get())); web_contents->SetOwnerWindow(window_.get()); - window_->InitFromOptions(options); - window_->AddObserver(this); - AttachAsUserData(window_.get()); #if defined(TOOLKIT_VIEWS) // Sets the window icon. @@ -116,6 +113,10 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) { if (options.Get(options::kIcon, &icon)) SetIcon(icon); #endif + + window_->InitFromOptions(options); + window_->AddObserver(this); + AttachAsUserData(window_.get()); } Window::~Window() { From 14c4be50c11a9678b12693661eeec7ce960779a6 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 2 Jul 2016 16:54:12 +0900 Subject: [PATCH 178/192] Avoid calling GetWebContentsFromProcessID for twice --- atom/browser/atom_browser_client.cc | 16 +++++----------- atom/browser/atom_browser_client.h | 4 +--- vendor/brightray | 2 +- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 13503ae57d7d..add3154642dc 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -287,29 +287,23 @@ brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( void AtomBrowserClient::WebNotificationAllowed( int render_process_id, - const base::Callback& callback) { + const base::Callback& callback) { content::WebContents* web_contents = WebContentsPreferences::GetWebContentsFromProcessID(render_process_id); if (!web_contents) { - callback.Run(false); + callback.Run(false, false); return; } auto permission_helper = WebContentsPermissionHelper::FromWebContents(web_contents); if (!permission_helper) { - callback.Run(false); + callback.Run(false, false); return; } - permission_helper->RequestWebNotificationPermission(callback); + permission_helper->RequestWebNotificationPermission( + base::Bind(callback, web_contents->IsAudioMuted())); } -bool AtomBrowserClient::WebContentsAudioMuted( - int render_process_id) { - content::WebContents* web_contents = - WebContentsPreferences::GetWebContentsFromProcessID(render_process_id); - return web_contents->IsAudioMuted(); - } - void AtomBrowserClient::RenderProcessHostDestroyed( content::RenderProcessHost* host) { int process_id = host->GetID(); diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index 47d743449744..a61706534aa8 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -100,9 +100,7 @@ class AtomBrowserClient : public brightray::BrowserClient, const content::MainFunctionParams&) override; void WebNotificationAllowed( int render_process_id, - const base::Callback& callback) override; - bool WebContentsAudioMuted( - int render_process_id) override; + const base::Callback& callback) override; // content::RenderProcessHostObserver: void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; diff --git a/vendor/brightray b/vendor/brightray index f9bc45b03c94..0e8ffb4d653f 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit f9bc45b03c94a2db729b53885691db5e04b757df +Subproject commit 0e8ffb4d653f9e85722bc98df7ddde9a0e6d0650 From bbaab9b35536691ad4c8effab147c8797e629fbe Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 2 Jul 2016 21:17:37 +1200 Subject: [PATCH 179/192] Document possible getLocale returns values. Closes #5363 --- docs/api/app.md | 2 +- docs/faq/app-locale.md | 138 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 docs/faq/app-locale.md diff --git a/docs/api/app.md b/docs/api/app.md index cf3d785c54b7..1cdebef992c7 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -396,7 +396,7 @@ Overrides the current application's name. ### `app.getLocale()` -Returns the current application locale. +Returns the current application locale. Possible return values are documented [here](../faq/app-locale.md) **Note:** When distributing your packaged app, you have to also ship the `locales` folder. diff --git a/docs/faq/app-locale.md b/docs/faq/app-locale.md new file mode 100644 index 000000000000..2be03d4f4c11 --- /dev/null +++ b/docs/faq/app-locale.md @@ -0,0 +1,138 @@ +Possible Values of `app.getLocale()` +------------------------------------ + +Electron uses Chromium's l10n util library to fetch the locale. Possible values are listed below + + +| Language Code | Language Name | +|---------------|---------------| +| af | Afrikaans | +| ar-AE | Arabic (U.A.E.) | +| ar-IQ | Arabic (Iraq) | +| ar | Arabic (Standard) | +| ar-BH | Arabic (Bahrain) | +| ar-DZ | Arabic (Algeria) | +| ar-EG | Arabic (Egypt) | +| ar | Aragonese | +| ar-JO | Arabic (Jordan) | +| ar-KW | Arabic (Kuwait) | +| ar-LB | Arabic (Lebanon) | +| ar-LY | Arabic (Libya) | +| ar-MA | Arabic (Morocco) | +| ar-OM | Arabic (Oman) | +| ar-QA | Arabic (Qatar) | +| ar-SA | Arabic (Saudi Arabia) | +| ar-SY | Arabic (Syria) | +| ar-TN | Arabic (Tunisia) | +| ar-YE | Arabic (Yemen) | +| as | Assamese | +| ast | Asturian | +| az | Azerbaijani | +| be | Belarusian | +| bg | Bulgarian | +| bg | Bulgarian | +| bn | Bengali | +| br | Breton | +| bs | Bosnian | +| ca | Catalan | +| ce | Chechen | +| ch | Chamorro | +| co | Corsican | +| cr | Cree | +| cs | Czech | +| cv | Chuvash | +| da | Danish | +| de | German (Standard) | +| de-AT | German (Austria) | +| de-CH | German (Switzerland) | +| de-DE | German (Germany) | +| de-LI | German (Liechtenstein) | +| de-LU | German (Luxembourg) | +| el | Greek | +| en-AU | English (Australia) | +| en-BZ | English (Belize) | +| en | English | +| en-CA | English (Canada) | +| en-GB | English (United Kingdom) | +| en-IE | English (Ireland) | +| en-JM | English (Jamaica) | +| en-NZ | English (New Zealand) | +| en-PH | English (Philippines) | +| en-TT | English (Trinidad & Tobago) | +| en-US | English (United States) | +| en-ZA | English (South Africa) | +| en-ZW | English (Zimbabwe) | +| eo | Esperanto | +| et | Estonian | +| eu | Basque | +| fa | Persian | +| fa | Farsi | +| fa-IR | Persian/Iran | +| fi | Finnish | +| fj | Fijian | +| fo | Faeroese | +| fr-CH | French (Switzerland) | +| fr-FR | French (France) | +| fr-LU | French (Luxembourg) | +| fr-MC | French (Monaco) | +| fr | French (Standard) | +| fr-BE | French (Belgium) | +| fr-CA | French (Canada) | +| fur | Friulian | +| fy | Frisian | +| ga | Irish | +| gd-IE | Gaelic (Irish) | +| gd | Gaelic (Scots) | +| gl | Galacian | +| gu | Gujurati | +| he | Hebrew | +| hi | Hindi | +| hr | Croatian | +| ht | Haitian | +| hu | Hungarian | +| hy | Armenian | +| id | Indonesian | +| is | Icelandic | +| it-CH | Italian (Switzerland) | +| it | Italian (Standard) | +| iu | Inuktitut | +| ja | Japanese | +| ka | Georgian | +| kk | Kazakh | +| km | Khmer | +| kn | Kannada | +| ko | Korean | +| ko-KP | Korean (North Korea) | +| ko-KR | Korean (South Korea) | +| ks | Kashmiri | +| ky | Kirghiz | +| la | Latin | +| lb | Luxembourgish | +| lt | Lithuanian | +| lv | Latvian | +| mi | Maori | +| mk | FYRO Macedonian | +| ml | Malayalam | +| mo | Moldavian | +| mr | Marathi | +| ms | Malay | +| mt | Maltese | +| my | Burmese | +| nb | Norwegian (Bokmal) | +| ne | Nepali | +| ng | Ndonga | +| nl | Dutch (Standard) | +| nl-BE | Dutch (Belgian) | +| nn | Norwegian (Nynorsk) | +| no | Norwegian | +| nv | Navajo | +| oc | Occitan | +| om | Oromo | +| or | Oriya | +| sq | Albanian | +| tlh | Klingon | +| zh-TW | Chinese (Taiwan) | +| zh | Chinese | +| zh-CN | Chinese (PRC) | +| zh-HK | Chinese (Hong Kong) | +| zh-SG | Chinese (Singapore) | From 7f914c65de508af70b08117c18c1bde1a20c2eb2 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 2 Jul 2016 23:21:45 +1200 Subject: [PATCH 180/192] Update docs to match style guide --- docs/{faq/app-locale.md => api/app-locales.md} | 3 +-- docs/api/app.md | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) rename docs/{faq/app-locale.md => api/app-locales.md} (97%) diff --git a/docs/faq/app-locale.md b/docs/api/app-locales.md similarity index 97% rename from docs/faq/app-locale.md rename to docs/api/app-locales.md index 2be03d4f4c11..96cc10e00ae8 100644 --- a/docs/faq/app-locale.md +++ b/docs/api/app-locales.md @@ -1,5 +1,4 @@ -Possible Values of `app.getLocale()` ------------------------------------- +# Possible Values of `app.getLocale()` Electron uses Chromium's l10n util library to fetch the locale. Possible values are listed below diff --git a/docs/api/app.md b/docs/api/app.md index 1cdebef992c7..e56a37298337 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -396,7 +396,7 @@ Overrides the current application's name. ### `app.getLocale()` -Returns the current application locale. Possible return values are documented [here](../faq/app-locale.md) +Returns the current application locale. Possible return values are documented [here](app-locales.md) **Note:** When distributing your packaged app, you have to also ship the `locales` folder. From 74ebbf9c78956f5ecc105477d8ac678c044464f7 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 2 Jul 2016 20:27:14 +0900 Subject: [PATCH 181/192] docs: Add trailing period --- docs/api/app-locales.md | 4 ++-- docs/api/app.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/api/app-locales.md b/docs/api/app-locales.md index 96cc10e00ae8..52efba6282d5 100644 --- a/docs/api/app-locales.md +++ b/docs/api/app-locales.md @@ -1,7 +1,7 @@ # Possible Values of `app.getLocale()` -Electron uses Chromium's l10n util library to fetch the locale. Possible values are listed below - +Electron uses Chromium's `l10n_util` library to fetch the locale. Possible +values are listed below: | Language Code | Language Name | |---------------|---------------| diff --git a/docs/api/app.md b/docs/api/app.md index e56a37298337..cfda6b574be1 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -396,7 +396,8 @@ Overrides the current application's name. ### `app.getLocale()` -Returns the current application locale. Possible return values are documented [here](app-locales.md) +Returns the current application locale. Possible return values are documented +[here](app-locales.md). **Note:** When distributing your packaged app, you have to also ship the `locales` folder. From 13c668f22b82579221f4aa82b9b7f9a52646646b Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 3 Jul 2016 12:26:43 +0900 Subject: [PATCH 182/192] mac: Implement webContents.startDrag --- atom/browser/api/atom_api_web_contents.cc | 9 ++++ atom/browser/api/atom_api_web_contents.h | 5 ++ atom/browser/ui/drag_util.h | 22 +++++++++ atom/browser/ui/drag_util_mac.mm | 56 +++++++++++++++++++++++ atom/browser/ui/drag_util_views.cc | 11 +++++ filenames.gypi | 3 ++ 6 files changed, 106 insertions(+) create mode 100644 atom/browser/ui/drag_util.h create mode 100644 atom/browser/ui/drag_util_mac.mm create mode 100644 atom/browser/ui/drag_util_views.cc diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index e6959c887111..370cac33fcb3 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -17,6 +17,7 @@ #include "atom/browser/lib/bluetooth_chooser.h" #include "atom/browser/native_window.h" #include "atom/browser/net/atom_network_delegate.h" +#include "atom/browser/ui/drag_util.h" #include "atom/browser/web_contents_permission_helper.h" #include "atom/browser/web_contents_preferences.h" #include "atom/browser/web_view_guest_delegate.h" @@ -1205,6 +1206,13 @@ void WebContents::EndFrameSubscription() { view->EndFrameSubscription(); } +void WebContents::StartDrag(const base::FilePath& file, + mate::Handle image) { + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + DragItem(file, image->image(), web_contents()->GetNativeView()); +} + void WebContents::OnCursorChange(const content::WebCursor& cursor) { content::WebCursor::CursorInfo info; cursor.GetCursorInfo(&info); @@ -1324,6 +1332,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("beginFrameSubscription", &WebContents::BeginFrameSubscription) .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription) + .SetMethod("startDrag", &WebContents::StartDrag) .SetMethod("setSize", &WebContents::SetSize) .SetMethod("isGuest", &WebContents::IsGuest) .SetMethod("getType", &WebContents::GetType) diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 5279812be427..88eec80d3370 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -39,6 +39,8 @@ class WebViewGuestDelegate; namespace api { +class NativeImage; + class WebContents : public mate::TrackableObject, public CommonWebContentsDelegate, public content::WebContentsObserver { @@ -142,6 +144,9 @@ class WebContents : public mate::TrackableObject, void BeginFrameSubscription(mate::Arguments* args); void EndFrameSubscription(); + // Dragging native items. + void StartDrag(const base::FilePath& file, mate::Handle image); + // Methods for creating . void SetSize(const SetSizeParams& params); bool IsGuest() const; diff --git a/atom/browser/ui/drag_util.h b/atom/browser/ui/drag_util.h new file mode 100644 index 000000000000..50f87c4d6339 --- /dev/null +++ b/atom/browser/ui/drag_util.h @@ -0,0 +1,22 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_UI_DRAG_UTIL_H_ +#define ATOM_BROWSER_UI_DRAG_UTIL_H_ + +#include "ui/gfx/image/image.h" + +namespace base { +class FilePath; +} + +namespace atom { + +void DragItem(const base::FilePath& path, + const gfx::Image& icon, + gfx::NativeView view); + +} // namespace atom + +#endif // ATOM_BROWSER_UI_DRAG_UTIL_H_ diff --git a/atom/browser/ui/drag_util_mac.mm b/atom/browser/ui/drag_util_mac.mm new file mode 100644 index 000000000000..fe4254b43cbe --- /dev/null +++ b/atom/browser/ui/drag_util_mac.mm @@ -0,0 +1,56 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#import + +#include "atom/browser/ui/drag_util.h" +#include "base/files/file_path.h" +#include "base/strings/sys_string_conversions.h" + +namespace atom { + +namespace { + +// Write information about the file being dragged to the pasteboard. +void AddFileToPasteboard(NSPasteboard* pasteboard, const base::FilePath& path) { + NSString* file = base::SysUTF8ToNSString(path.value()); + NSArray* fileList = [NSArray arrayWithObject:file]; + [pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] + owner:nil]; + [pasteboard setPropertyList:fileList forType:NSFilenamesPboardType]; +} + +} // namespace + +void DragItem(const base::FilePath& path, + const gfx::Image& icon, + gfx::NativeView view) { + NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + AddFileToPasteboard(pasteboard, path); + + // Synthesize a drag event, since we don't have access to the actual event + // that initiated a drag (possibly consumed by the Web UI, for example). + NSPoint position = [[view window] mouseLocationOutsideOfEventStream]; + NSTimeInterval eventTime = [[NSApp currentEvent] timestamp]; + NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged + location:position + modifierFlags:NSLeftMouseDraggedMask + timestamp:eventTime + windowNumber:[[view window] windowNumber] + context:nil + eventNumber:0 + clickCount:1 + pressure:1.0]; + + // Run the drag operation. + [[view window] dragImage:icon.ToNSImage() + at:position + offset:NSZeroSize + event:dragEvent + pasteboard:pasteboard + source:view + slideBack:YES]; +} + +} // namespace atom diff --git a/atom/browser/ui/drag_util_views.cc b/atom/browser/ui/drag_util_views.cc new file mode 100644 index 000000000000..1db7e5439455 --- /dev/null +++ b/atom/browser/ui/drag_util_views.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/ui/drag_util.h" + +namespace atom { + + + +} // namespace atom diff --git a/filenames.gypi b/filenames.gypi index 6b086e1f8b98..a86c53ba4fd0 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -245,6 +245,9 @@ 'atom/browser/ui/atom_menu_model.h', 'atom/browser/ui/cocoa/atom_menu_controller.h', 'atom/browser/ui/cocoa/atom_menu_controller.mm', + 'atom/browser/ui/drag_util_mac.mm', + 'atom/browser/ui/drag_util_views.cc', + 'atom/browser/ui/drag_util.h', 'atom/browser/ui/file_dialog.h', 'atom/browser/ui/file_dialog_gtk.cc', 'atom/browser/ui/file_dialog_mac.mm', From fa468a529be9e210170f251aff21ba7e97aca73b Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 3 Jul 2016 13:58:31 +0900 Subject: [PATCH 183/192] Support dragging multiple files --- atom/browser/api/atom_api_web_contents.cc | 29 ++++++++++++++++++++--- atom/browser/api/atom_api_web_contents.h | 4 +--- atom/browser/ui/drag_util.h | 8 ++++--- atom/browser/ui/drag_util_mac.mm | 16 +++++++------ 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 370cac33fcb3..7b710e5f85a8 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1206,11 +1206,34 @@ void WebContents::EndFrameSubscription() { view->EndFrameSubscription(); } -void WebContents::StartDrag(const base::FilePath& file, - mate::Handle image) { +void WebContents::StartDrag(const mate::Dictionary& item, + mate::Arguments* args) { base::MessageLoop::ScopedNestableTaskAllower allow( base::MessageLoop::current()); - DragItem(file, image->image(), web_contents()->GetNativeView()); + + base::FilePath file; + std::vector files; + if (!item.Get("files", &files) && item.Get("file", &file)) { + files.push_back(file); + } + + mate::Handle icon; + if (!item.Get("icon", &icon) && !file.empty()) { + // TODO(zcbenz): Set default icon from file. + } + + // Error checking. + if (icon.IsEmpty()) { + args->ThrowError("icon must be set"); + return; + } + + // Start dragging. + if (!files.empty()) { + DragFileItems(files, icon->image(), web_contents()->GetNativeView()); + } else { + args->ThrowError("There is nothing to drag"); + } } void WebContents::OnCursorChange(const content::WebCursor& cursor) { diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 88eec80d3370..2917aa860a81 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -39,8 +39,6 @@ class WebViewGuestDelegate; namespace api { -class NativeImage; - class WebContents : public mate::TrackableObject, public CommonWebContentsDelegate, public content::WebContentsObserver { @@ -145,7 +143,7 @@ class WebContents : public mate::TrackableObject, void EndFrameSubscription(); // Dragging native items. - void StartDrag(const base::FilePath& file, mate::Handle image); + void StartDrag(const mate::Dictionary& item, mate::Arguments* args); // Methods for creating . void SetSize(const SetSizeParams& params); diff --git a/atom/browser/ui/drag_util.h b/atom/browser/ui/drag_util.h index 50f87c4d6339..df5303c65e45 100644 --- a/atom/browser/ui/drag_util.h +++ b/atom/browser/ui/drag_util.h @@ -5,6 +5,8 @@ #ifndef ATOM_BROWSER_UI_DRAG_UTIL_H_ #define ATOM_BROWSER_UI_DRAG_UTIL_H_ +#include + #include "ui/gfx/image/image.h" namespace base { @@ -13,9 +15,9 @@ class FilePath; namespace atom { -void DragItem(const base::FilePath& path, - const gfx::Image& icon, - gfx::NativeView view); +void DragFileItems(const std::vector& files, + const gfx::Image& icon, + gfx::NativeView view); } // namespace atom diff --git a/atom/browser/ui/drag_util_mac.mm b/atom/browser/ui/drag_util_mac.mm index fe4254b43cbe..7197e79af68c 100644 --- a/atom/browser/ui/drag_util_mac.mm +++ b/atom/browser/ui/drag_util_mac.mm @@ -13,9 +13,11 @@ namespace atom { namespace { // Write information about the file being dragged to the pasteboard. -void AddFileToPasteboard(NSPasteboard* pasteboard, const base::FilePath& path) { - NSString* file = base::SysUTF8ToNSString(path.value()); - NSArray* fileList = [NSArray arrayWithObject:file]; +void AddFilesToPasteboard(NSPasteboard* pasteboard, + const std::vector& files) { + NSMutableArray* fileList = [NSMutableArray array]; + for (const base::FilePath& file : files) + [fileList addObject:base::SysUTF8ToNSString(file.value())]; [pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil]; [pasteboard setPropertyList:fileList forType:NSFilenamesPboardType]; @@ -23,11 +25,11 @@ void AddFileToPasteboard(NSPasteboard* pasteboard, const base::FilePath& path) { } // namespace -void DragItem(const base::FilePath& path, - const gfx::Image& icon, - gfx::NativeView view) { +void DragFileItems(const std::vector& files, + const gfx::Image& icon, + gfx::NativeView view) { NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - AddFileToPasteboard(pasteboard, path); + AddFilesToPasteboard(pasteboard, files); // Synthesize a drag event, since we don't have access to the actual event // that initiated a drag (possibly consumed by the Web UI, for example). From 63a8961ed1eaf86377ece40f26f8bb7c843b742d Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 3 Jul 2016 14:34:35 +0900 Subject: [PATCH 184/192] views: Implement webContents.startDrag --- atom/browser/ui/drag_util_views.cc | 119 +++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/atom/browser/ui/drag_util_views.cc b/atom/browser/ui/drag_util_views.cc index 1db7e5439455..54bbfd1eb8ee 100644 --- a/atom/browser/ui/drag_util_views.cc +++ b/atom/browser/ui/drag_util_views.cc @@ -4,8 +4,127 @@ #include "atom/browser/ui/drag_util.h" +#if defined(OS_WIN) +#include +#include +#include +#include +#endif + +#include "ui/aura/window.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/drag_utils.h" +#include "ui/base/dragdrop/file_info.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/screen.h" +#include "ui/views/widget/widget.h" +#include "ui/wm/public/drag_drop_client.h" + +#if defined(OS_WIN) +#include "base/win/scoped_comptr.h" +#include "base/win/scoped_hdc.h" +#include "ui/base/dragdrop/os_exchange_data_provider_win.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/gdi_util.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/skbitmap_operations.h" +#endif + namespace atom { +namespace { +#if defined(OS_WIN) +void SetDragImageOnDataObject(HBITMAP hbitmap, + const gfx::Size& size_in_pixels, + const gfx::Vector2d& cursor_offset, + IDataObject* data_object) { + base::win::ScopedComPtr helper; + HRESULT rv = CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER, + IID_IDragSourceHelper, helper.ReceiveVoid()); + if (SUCCEEDED(rv)) { + SHDRAGIMAGE sdi; + sdi.sizeDragImage = size_in_pixels.ToSIZE(); + sdi.crColorKey = 0xFFFFFFFF; + sdi.hbmpDragImage = hbitmap; + sdi.ptOffset = gfx::PointAtOffsetFromOrigin(cursor_offset).ToPOINT(); + helper->InitializeFromBitmap(&sdi, data_object); + } +} + +// Blit the contents of the canvas to a new HBITMAP. It is the caller's +// responsibility to release the |bits| buffer. +HBITMAP CreateHBITMAPFromSkBitmap(const SkBitmap& sk_bitmap) { + base::win::ScopedGetDC screen_dc(NULL); + BITMAPINFOHEADER header; + gfx::CreateBitmapHeader(sk_bitmap.width(), sk_bitmap.height(), &header); + void* bits; + HBITMAP bitmap = + CreateDIBSection(screen_dc, reinterpret_cast(&header), + DIB_RGB_COLORS, &bits, NULL, 0); + if (!bitmap || !bits) + return NULL; + DCHECK_EQ(sk_bitmap.rowBytes(), static_cast(sk_bitmap.width() * 4)); + SkAutoLockPixels lock(sk_bitmap); + memcpy( + bits, sk_bitmap.getPixels(), sk_bitmap.height() * sk_bitmap.rowBytes()); + return bitmap; +} + +void SetDragImageOnDataObject(const gfx::ImageSkia& image_skia, + const gfx::Vector2d& cursor_offset, + ui::OSExchangeData* data_object) { + DCHECK(data_object && !image_skia.size().IsEmpty()); + // InitializeFromBitmap() doesn't expect an alpha channel and is confused + // by premultiplied colors, so unpremultiply the bitmap. + // SetDragImageOnDataObject(HBITMAP) takes ownership of the bitmap. + HBITMAP bitmap = CreateHBITMAPFromSkBitmap( + SkBitmapOperations::UnPreMultiply(*image_skia.bitmap())); + if (bitmap) { + // Attach 'bitmap' to the data_object. + SetDragImageOnDataObject( + bitmap, + gfx::Size(image_skia.bitmap()->width(), image_skia.bitmap()->height()), + cursor_offset, + ui::OSExchangeDataProviderWin::GetIDataObject(*data_object)); + } +} +#endif + +} // namespace + +void DragFileItems(const std::vector& files, + const gfx::Image& icon, + gfx::NativeView view) { + // Set up our OLE machinery + ui::OSExchangeData data; + +#if defined(OS_WIN) + SetDragImageOnDataObject(icon.AsImageSkia(), gfx::Vector2d(), &data); +#endif + data.provider().SetDragImage(icon.AsImageSkia(), gfx::Vector2d()); + + std::vector file_infos; + for (const base::FilePath& file : files) { + file_infos.push_back(ui::FileInfo(file, base::FilePath())); + } + data.SetFilenames(file_infos); + + aura::Window* root_window = view->GetRootWindow(); + if (!root_window || !aura::client::GetDragDropClient(root_window)) + return; + + gfx::Point location = gfx::Screen::GetScreen()->GetCursorScreenPoint(); + // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below. + aura::client::GetDragDropClient(root_window)->StartDragAndDrop( + data, + root_window, + view, + location, + ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK, + ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); +} } // namespace atom From 7916f0ea18c2e18241b0d9fa03ef0f827aa77c4f Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 3 Jul 2016 14:44:47 +0900 Subject: [PATCH 185/192] win: Use drag_utils::CreateDragImageForFile --- atom/browser/ui/drag_util_views.cc | 84 +----------------------------- 1 file changed, 1 insertion(+), 83 deletions(-) diff --git a/atom/browser/ui/drag_util_views.cc b/atom/browser/ui/drag_util_views.cc index 54bbfd1eb8ee..c69e34326c94 100644 --- a/atom/browser/ui/drag_util_views.cc +++ b/atom/browser/ui/drag_util_views.cc @@ -4,13 +4,6 @@ #include "atom/browser/ui/drag_util.h" -#if defined(OS_WIN) -#include -#include -#include -#include -#endif - #include "ui/aura/window.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_utils.h" @@ -21,90 +14,15 @@ #include "ui/views/widget/widget.h" #include "ui/wm/public/drag_drop_client.h" -#if defined(OS_WIN) -#include "base/win/scoped_comptr.h" -#include "base/win/scoped_hdc.h" -#include "ui/base/dragdrop/os_exchange_data_provider_win.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/gdi_util.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/image/image_skia.h" -#include "ui/gfx/skbitmap_operations.h" -#endif - namespace atom { -namespace { - -#if defined(OS_WIN) -void SetDragImageOnDataObject(HBITMAP hbitmap, - const gfx::Size& size_in_pixels, - const gfx::Vector2d& cursor_offset, - IDataObject* data_object) { - base::win::ScopedComPtr helper; - HRESULT rv = CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER, - IID_IDragSourceHelper, helper.ReceiveVoid()); - if (SUCCEEDED(rv)) { - SHDRAGIMAGE sdi; - sdi.sizeDragImage = size_in_pixels.ToSIZE(); - sdi.crColorKey = 0xFFFFFFFF; - sdi.hbmpDragImage = hbitmap; - sdi.ptOffset = gfx::PointAtOffsetFromOrigin(cursor_offset).ToPOINT(); - helper->InitializeFromBitmap(&sdi, data_object); - } -} - -// Blit the contents of the canvas to a new HBITMAP. It is the caller's -// responsibility to release the |bits| buffer. -HBITMAP CreateHBITMAPFromSkBitmap(const SkBitmap& sk_bitmap) { - base::win::ScopedGetDC screen_dc(NULL); - BITMAPINFOHEADER header; - gfx::CreateBitmapHeader(sk_bitmap.width(), sk_bitmap.height(), &header); - void* bits; - HBITMAP bitmap = - CreateDIBSection(screen_dc, reinterpret_cast(&header), - DIB_RGB_COLORS, &bits, NULL, 0); - if (!bitmap || !bits) - return NULL; - DCHECK_EQ(sk_bitmap.rowBytes(), static_cast(sk_bitmap.width() * 4)); - SkAutoLockPixels lock(sk_bitmap); - memcpy( - bits, sk_bitmap.getPixels(), sk_bitmap.height() * sk_bitmap.rowBytes()); - return bitmap; -} - -void SetDragImageOnDataObject(const gfx::ImageSkia& image_skia, - const gfx::Vector2d& cursor_offset, - ui::OSExchangeData* data_object) { - DCHECK(data_object && !image_skia.size().IsEmpty()); - // InitializeFromBitmap() doesn't expect an alpha channel and is confused - // by premultiplied colors, so unpremultiply the bitmap. - // SetDragImageOnDataObject(HBITMAP) takes ownership of the bitmap. - HBITMAP bitmap = CreateHBITMAPFromSkBitmap( - SkBitmapOperations::UnPreMultiply(*image_skia.bitmap())); - if (bitmap) { - // Attach 'bitmap' to the data_object. - SetDragImageOnDataObject( - bitmap, - gfx::Size(image_skia.bitmap()->width(), image_skia.bitmap()->height()), - cursor_offset, - ui::OSExchangeDataProviderWin::GetIDataObject(*data_object)); - } -} -#endif - -} // namespace - void DragFileItems(const std::vector& files, const gfx::Image& icon, gfx::NativeView view) { // Set up our OLE machinery ui::OSExchangeData data; -#if defined(OS_WIN) - SetDragImageOnDataObject(icon.AsImageSkia(), gfx::Vector2d(), &data); -#endif - data.provider().SetDragImage(icon.AsImageSkia(), gfx::Vector2d()); + drag_utils::CreateDragImageForFile(files[0], icon.AsImageSkia(), &data); std::vector file_infos; for (const base::FilePath& file : files) { From f072d9dd3a1cb7dd62c91d202b00f78ea82aedda Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 3 Jul 2016 15:10:59 +0900 Subject: [PATCH 186/192] docs: webContents.startDrag(item) --- docs/api/web-contents.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 913504cd3745..c3d175125b84 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -943,6 +943,16 @@ defaults to `false`. End subscribing for frame presentation events. +### `webContents.startDrag(item)` + +* `item` object + * `file` String + * `icon` [NativeImage](native-image.md) + +Sets the `item` as dragging item for current drag-drop operation, `file` is the +absolute path of the file to be dragged, and `icon` is the image showing under +the cursor when dragging. + ### `webContents.savePage(fullPath, saveType, callback)` * `fullPath` String - The full file path. From b7459d50f947a080418a0c83e110b1b8c85d2dec Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 3 Jul 2016 15:26:22 +0900 Subject: [PATCH 187/192] docs: Dragging files out of the window --- .../desktop-environment-integration.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/tutorial/desktop-environment-integration.md b/docs/tutorial/desktop-environment-integration.md index adb08ad5dc40..e430cb4235d9 100644 --- a/docs/tutorial/desktop-environment-integration.md +++ b/docs/tutorial/desktop-environment-integration.md @@ -321,6 +321,32 @@ win.setRepresentedFilename('/etc/passwd'); win.setDocumentEdited(true); ``` +## Dragging files out of the window + +For certain kinds of apps that manipulate on files, it is important to be able +to drag files from Electron to other apps. To implement this feature in your +app, you need to call `webContents.startDrag(item)` API on `ondragstart` event. + +In web page: + +```javascript +document.getElementById('targetItem').ondragstart = (event) => { + event.preventDefault() + ipcRenderer.send('ondragstart', '/path/to/item') +} +``` + +In the main process: + +```javascript +ipcMain.on('ondragstart', (event, filePath) => { + event.sender.startDrag({ + file: filePath, + icon: '/path/to/icon.png' + }) +}) +``` + [addrecentdocument]: ../api/app.md#appaddrecentdocumentpath-os-x-windows [clearrecentdocuments]: ../api/app.md#appclearrecentdocuments-os-x-windows [setusertaskstasks]: ../api/app.md#appsetusertaskstasks-windows From ba1fe322e9532d1b2a866401056d9355e6b88c7c Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 3 Jul 2016 15:27:26 +0900 Subject: [PATCH 188/192] docs: Use as example --- docs/tutorial/desktop-environment-integration.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/tutorial/desktop-environment-integration.md b/docs/tutorial/desktop-environment-integration.md index e430cb4235d9..ab84681609e1 100644 --- a/docs/tutorial/desktop-environment-integration.md +++ b/docs/tutorial/desktop-environment-integration.md @@ -329,11 +329,14 @@ app, you need to call `webContents.startDrag(item)` API on `ondragstart` event. In web page: -```javascript -document.getElementById('targetItem').ondragstart = (event) => { - event.preventDefault() - ipcRenderer.send('ondragstart', '/path/to/item') -} +```html +item + ``` In the main process: From 23582cc9d3e224638e7493cbf36bb8b00509becc Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 3 Jul 2016 15:30:27 +0900 Subject: [PATCH 189/192] Reduce the scope of ScopedNestableTaskAllower --- atom/browser/api/atom_api_web_contents.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 7b710e5f85a8..99cb2e007482 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1208,9 +1208,6 @@ void WebContents::EndFrameSubscription() { void WebContents::StartDrag(const mate::Dictionary& item, mate::Arguments* args) { - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); - base::FilePath file; std::vector files; if (!item.Get("files", &files) && item.Get("file", &file)) { @@ -1230,6 +1227,8 @@ void WebContents::StartDrag(const mate::Dictionary& item, // Start dragging. if (!files.empty()) { + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); DragFileItems(files, icon->image(), web_contents()->GetNativeView()); } else { args->ThrowError("There is nothing to drag"); From b8e51801322e6e45685df4e3e20be397c598b462 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 5 Jul 2016 15:50:12 -0700 Subject: [PATCH 190/192] Use require to load package.json instead of fs/JSON --- lib/browser/init.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/browser/init.js b/lib/browser/init.js index 6326b8e80e8b..68a555ad5858 100644 --- a/lib/browser/init.js +++ b/lib/browser/init.js @@ -119,7 +119,7 @@ for (i = 0, len = searchPaths.length; i < len; i++) { packagePath = searchPaths[i] try { packagePath = path.join(process.resourcesPath, packagePath) - packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json'))) + packageJson = require(path.join(packagePath, 'package.json')) break } catch (error) { continue From 7baf7372af2377495233b975028f654e95ebe510 Mon Sep 17 00:00:00 2001 From: Jonathan Sampson Date: Tue, 5 Jul 2016 19:34:33 -0700 Subject: [PATCH 191/192] modern.ie is no longer in use The URL exists for historical reasons, but is no longer the primary URL for developer resources. --- docs/development/build-instructions-windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/build-instructions-windows.md b/docs/development/build-instructions-windows.md index b3c2c4573462..7a7e1ee6c6e9 100644 --- a/docs/development/build-instructions-windows.md +++ b/docs/development/build-instructions-windows.md @@ -12,7 +12,7 @@ Follow the guidelines below for building Electron on Windows. * [Git](http://git-scm.com) If you don't currently have a Windows installation, -[modern.ie](https://www.modern.ie/en-us/virtualization-tools#downloads) +[dev.microsoftedge.com](https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/) has timebombed versions of Windows that you can use to build Electron. Building Electron is done entirely with command-line scripts and cannot be done From 92b97d3576368acc5e78220094fe28e20002866e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 6 Jul 2016 13:20:36 +0900 Subject: [PATCH 192/192] Bump v1.2.6 --- atom/browser/resources/mac/Info.plist | 4 ++-- atom/browser/resources/win/atom.rc | 8 ++++---- atom/common/atom_version.h | 2 +- electron.gyp | 2 +- package.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index 33cfc66b2718..2f346b685508 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile electron.icns CFBundleVersion - 1.2.5 + 1.2.6 CFBundleShortVersionString - 1.2.5 + 1.2.6 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 91101128feda..50ed8a37d6d6 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,5,0 - PRODUCTVERSION 1,2,5,0 + FILEVERSION 1,2,6,0 + PRODUCTVERSION 1,2,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "1.2.5" + VALUE "FileVersion", "1.2.6" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "1.2.5" + VALUE "ProductVersion", "1.2.6" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 7a61d6bc72bd..ff139fe9846f 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 1 #define ATOM_MINOR_VERSION 2 -#define ATOM_PATCH_VERSION 5 +#define ATOM_PATCH_VERSION 6 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/electron.gyp b/electron.gyp index a4c412fc9877..cfa96eae2286 100644 --- a/electron.gyp +++ b/electron.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '1.2.5', + 'version%': '1.2.6', }, 'includes': [ 'filenames.gypi', diff --git a/package.json b/package.json index 738ffbb732ab..d8e71d36b493 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron", - "version": "1.2.5", + "version": "1.2.6", "devDependencies": { "asar": "^0.11.0", "request": "*",