diff --git a/atom/browser/api/atom_api_browser_window.cc b/atom/browser/api/atom_api_browser_window.cc index b137ec092ed5..bb8247ec963c 100644 --- a/atom/browser/api/atom_api_browser_window.cc +++ b/atom/browser/api/atom_api_browser_window.cc @@ -29,7 +29,7 @@ namespace api { BrowserWindow::BrowserWindow(v8::Isolate* isolate, v8::Local wrapper, const mate::Dictionary& options) - : TopLevelWindow(isolate, wrapper, options), weak_factory_(this) { + : TopLevelWindow(isolate, options), weak_factory_(this) { mate::Handle web_contents; // Use options.webPreferences in WebContents. @@ -397,7 +397,6 @@ mate::WrappableBase* BrowserWindow::New(mate::Arguments* args) { // static void BrowserWindow::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { - TopLevelWindow::BuildPrototype(isolate, prototype); prototype->SetClassName(mate::StringToV8(isolate, "BrowserWindow")); mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) .SetMethod("focusOnWebView", &BrowserWindow::FocusOnWebView) @@ -439,14 +438,8 @@ void Initialize(v8::Local exports, templ->InstanceTemplate()->SetInternalFieldCount(1); BrowserWindow::BuildPrototype(isolate, templ); - mate::Dictionary browser_window(isolate, templ->GetFunction()); - browser_window.SetMethod( - "fromId", &mate::TrackableObject::FromWeakMapID); - browser_window.SetMethod("getAllWindows", - &mate::TrackableObject::GetAll); - mate::Dictionary dict(isolate, exports); - dict.Set("BrowserWindow", browser_window); + dict.Set("BrowserWindow", templ->GetFunction()); } } // namespace diff --git a/atom/browser/api/atom_api_browser_window.h b/atom/browser/api/atom_api_browser_window.h index 7b1307922cda..5f9610f3a3c2 100644 --- a/atom/browser/api/atom_api_browser_window.h +++ b/atom/browser/api/atom_api_browser_window.h @@ -118,27 +118,4 @@ class BrowserWindow : public TopLevelWindow, } // namespace atom -namespace mate { - -template <> -struct Converter { - static bool FromV8(v8::Isolate* isolate, - v8::Local val, - atom::NativeWindow** out) { - // null would be tranfered to NULL. - if (val->IsNull()) { - *out = NULL; - return true; - } - - atom::api::BrowserWindow* window; - if (!Converter::FromV8(isolate, val, &window)) - return false; - *out = window->window(); - return true; - } -}; - -} // namespace mate - #endif // ATOM_BROWSER_API_ATOM_API_BROWSER_WINDOW_H_ diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 2770742ce287..53c7927689a5 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -8,7 +8,7 @@ #include #include -#include "atom/browser/api/atom_api_browser_window.h" +#include "atom/browser/api/atom_api_top_level_window.h" #include "atom/browser/api/trackable_object.h" #include "atom/browser/ui/atom_menu_model.h" #include "base/callback.h" @@ -54,7 +54,7 @@ class Menu : public mate::TrackableObject, void ExecuteCommand(int command_id, int event_flags) override; void MenuWillShow(ui::SimpleMenuModel* source) override; - virtual void PopupAt(BrowserWindow* window, + virtual void PopupAt(TopLevelWindow* window, int x, int y, int positioning_item, diff --git a/atom/browser/api/atom_api_menu_mac.h b/atom/browser/api/atom_api_menu_mac.h index 5b4816ea8462..fb575cfcbaec 100644 --- a/atom/browser/api/atom_api_menu_mac.h +++ b/atom/browser/api/atom_api_menu_mac.h @@ -22,7 +22,7 @@ class MenuMac : public Menu { protected: MenuMac(v8::Isolate* isolate, v8::Local wrapper); - void PopupAt(BrowserWindow* window, + void PopupAt(TopLevelWindow* window, int x, int y, int positioning_item, diff --git a/atom/browser/api/atom_api_menu_mac.mm b/atom/browser/api/atom_api_menu_mac.mm index cc08fa531509..fe077e347608 100644 --- a/atom/browser/api/atom_api_menu_mac.mm +++ b/atom/browser/api/atom_api_menu_mac.mm @@ -9,8 +9,6 @@ #include "base/mac/scoped_sending_event.h" #include "base/message_loop/message_loop.h" #include "base/strings/sys_string_conversions.h" -#include "brightray/browser/inspectable_web_contents.h" -#include "brightray/browser/inspectable_web_contents_view.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" @@ -27,7 +25,7 @@ MenuMac::MenuMac(v8::Isolate* isolate, v8::Local wrapper) weak_factory_(this) { } -void MenuMac::PopupAt(BrowserWindow* window, +void MenuMac::PopupAt(TopLevelWindow* window, int x, int y, int positioning_item, const base::Closure& callback) { NativeWindow* native_window = window->window(); diff --git a/atom/browser/api/atom_api_menu_views.cc b/atom/browser/api/atom_api_menu_views.cc index 52d2fb726c9c..1060964a297b 100644 --- a/atom/browser/api/atom_api_menu_views.cc +++ b/atom/browser/api/atom_api_menu_views.cc @@ -6,8 +6,6 @@ #include "atom/browser/native_window_views.h" #include "atom/browser/unresponsive_suppressor.h" -#include "brightray/browser/inspectable_web_contents.h" -#include "brightray/browser/inspectable_web_contents_view.h" #include "ui/display/screen.h" using views::MenuRunner; @@ -19,7 +17,7 @@ namespace api { MenuViews::MenuViews(v8::Isolate* isolate, v8::Local wrapper) : Menu(isolate, wrapper), weak_factory_(this) {} -void MenuViews::PopupAt(BrowserWindow* window, +void MenuViews::PopupAt(TopLevelWindow* window, int x, int y, int positioning_item, diff --git a/atom/browser/api/atom_api_menu_views.h b/atom/browser/api/atom_api_menu_views.h index 31d7d48db8e4..e5b93a3cfa0b 100644 --- a/atom/browser/api/atom_api_menu_views.h +++ b/atom/browser/api/atom_api_menu_views.h @@ -21,7 +21,7 @@ class MenuViews : public Menu { MenuViews(v8::Isolate* isolate, v8::Local wrapper); protected: - void PopupAt(BrowserWindow* window, + void PopupAt(TopLevelWindow* window, int x, int y, int positioning_item, diff --git a/atom/browser/api/atom_api_top_level_window.cc b/atom/browser/api/atom_api_top_level_window.cc index 735835c87ccf..87cc03540edd 100644 --- a/atom/browser/api/atom_api_top_level_window.cc +++ b/atom/browser/api/atom_api_top_level_window.cc @@ -9,6 +9,7 @@ #include "atom/browser/api/atom_api_browser_view.h" #include "atom/browser/api/atom_api_menu.h" +#include "atom/browser/api/atom_api_web_contents.h" #include "atom/common/color_util.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/file_path_converter.h" @@ -70,7 +71,6 @@ v8::Local ToBuffer(v8::Isolate* isolate, void* val, int size) { } // namespace TopLevelWindow::TopLevelWindow(v8::Isolate* isolate, - v8::Local wrapper, const mate::Dictionary& options) : weak_factory_(this) { // The parent window. @@ -99,13 +99,15 @@ TopLevelWindow::TopLevelWindow(v8::Isolate* isolate, if (options.Get(options::kIcon, &icon) && !icon.IsEmpty()) SetIcon(icon); #endif +} - AttachAsUserData(window_.get()); - - // We can only append this window to parent window's child windows after this - // window's JS wrapper gets initialized. - if (!parent.IsEmpty()) - parent->child_windows_.Set(isolate, weak_map_id(), wrapper); +TopLevelWindow::TopLevelWindow(v8::Isolate* isolate, + v8::Local wrapper, + const mate::Dictionary& options) + : TopLevelWindow(isolate, options) { + InitWith(isolate, wrapper); + // Init window after everything has been setup. + window()->InitFromOptions(options); } TopLevelWindow::~TopLevelWindow() { @@ -117,6 +119,21 @@ TopLevelWindow::~TopLevelWindow() { base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, window_.release()); } +void TopLevelWindow::InitWith(v8::Isolate* isolate, + v8::Local wrapper) { + AttachAsUserData(window_.get()); + mate::TrackableObject::InitWith(isolate, wrapper); + + // We can only append this window to parent window's child windows after this + // window's JS wrapper gets initialized. + if (!parent_window_.IsEmpty()) { + mate::Handle parent; + mate::ConvertFromV8(isolate, GetParentWindow(), &parent); + DCHECK(!parent.IsEmpty()); + parent->child_windows_.Set(isolate, weak_map_id(), wrapper); + } +} + void TopLevelWindow::WillCloseWindow(bool* prevent_default) { *prevent_default = Emit("close"); } diff --git a/atom/browser/api/atom_api_top_level_window.h b/atom/browser/api/atom_api_top_level_window.h index ef140f6e8b27..caba53c0e0f0 100644 --- a/atom/browser/api/atom_api_top_level_window.h +++ b/atom/browser/api/atom_api_top_level_window.h @@ -35,11 +35,17 @@ class TopLevelWindow : public mate::TrackableObject, NativeWindow* window() const { return window_.get(); } protected: + // Common constructor. + TopLevelWindow(v8::Isolate* isolate, const mate::Dictionary& options); + // Creating independent TopLevelWindow instance. TopLevelWindow(v8::Isolate* isolate, v8::Local wrapper, const mate::Dictionary& options); ~TopLevelWindow() override; + // TrackableObject: + void InitWith(v8::Isolate* isolate, v8::Local wrapper) override; + // NativeWindowObserver: void WillCloseWindow(bool* prevent_default) override; void OnWindowClosed() override; @@ -223,4 +229,27 @@ class TopLevelWindow : public mate::TrackableObject, } // namespace atom +namespace mate { + +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, + v8::Local val, + atom::NativeWindow** out) { + // null would be tranfered to NULL. + if (val->IsNull()) { + *out = NULL; + return true; + } + + atom::api::TopLevelWindow* window; + if (!Converter::FromV8(isolate, val, &window)) + return false; + *out = window->window(); + return true; + } +}; + +} // namespace mate + #endif // ATOM_BROWSER_API_ATOM_API_TOP_LEVEL_WINDOW_H_ diff --git a/filenames.gypi b/filenames.gypi index 8b8dd7690bb9..3e0cb381fb6e 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -34,6 +34,7 @@ 'lib/browser/api/screen.js', 'lib/browser/api/session.js', 'lib/browser/api/system-preferences.js', + 'lib/browser/api/top-level-window.js', 'lib/browser/api/touch-bar.js', 'lib/browser/api/tray.js', 'lib/browser/api/web-contents.js', diff --git a/lib/browser/api/browser-window.js b/lib/browser/api/browser-window.js index 6b14eb84d631..924a99c4bebb 100644 --- a/lib/browser/api/browser-window.js +++ b/lib/browser/api/browser-window.js @@ -1,22 +1,18 @@ 'use strict' const electron = require('electron') -const {ipcMain} = electron -const {EventEmitter} = require('events') +const {ipcMain, TopLevelWindow} = electron const {BrowserWindow} = process.atomBinding('window') const v8Util = process.atomBinding('v8_util') -Object.setPrototypeOf(BrowserWindow.prototype, EventEmitter.prototype) +Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype) BrowserWindow.prototype._init = function () { - // Avoid recursive require. - const {app} = require('electron') + // Call parent class's _init. + TopLevelWindow.prototype._init.call(this) - // Simulate the application menu on platforms other than macOS. - if (process.platform !== 'darwin') { - const menu = app.getApplicationMenu() - if (menu) this.setMenu(menu) - } + // Avoid recursive require. + const {app} = electron // Make new windows requested by links behave like "window.open" this.webContents.on('-new-window', (event, url, frameName, disposition, @@ -132,6 +128,19 @@ BrowserWindow.prototype._init = function () { }) } +const isBrowserWindow = (win) => { + return win && win.constructor.name === 'BrowserWindow' +} + +BrowserWindow.fromId = (id) => { + const win = TopLevelWindow.fromId(id) + return isBrowserWindow(win) ? win : null +} + +BrowserWindow.getAllWindows = () => { + return TopLevelWindow.getAllWindows().filter(isBrowserWindow) +} + BrowserWindow.getFocusedWindow = () => { for (let window of BrowserWindow.getAllWindows()) { if (window.isFocused() || window.isDevToolsFocused()) return window diff --git a/lib/browser/api/menu.js b/lib/browser/api/menu.js index eab8184efe6c..ddce950290fd 100644 --- a/lib/browser/api/menu.js +++ b/lib/browser/api/menu.js @@ -1,6 +1,6 @@ 'use strict' -const {BrowserWindow, MenuItem, webContents} = require('electron') +const {TopLevelWindow, MenuItem, webContents} = require('electron') const EventEmitter = require('events').EventEmitter const v8Util = process.atomBinding('v8_util') const bindings = process.atomBinding('menu') @@ -26,7 +26,7 @@ const delegate = { executeCommand: (menu, event, id) => { const command = menu.commandsMap[id] if (!command) return - command.click(event, BrowserWindow.getFocusedWindow(), webContents.getFocusedWebContents()) + command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents()) }, menuWillShow: (menu) => { // Ensure radio groups have at least one menu item seleted @@ -61,14 +61,14 @@ Menu.prototype.popup = function (options) { if (typeof positioningItem !== 'number') positioningItem = -1 // find which window to use - const wins = BrowserWindow.getAllWindows() + const wins = TopLevelWindow.getAllWindows() if (!wins || wins.indexOf(window) === -1) { - window = BrowserWindow.getFocusedWindow() + window = TopLevelWindow.getFocusedWindow() if (!window && wins && wins.length > 0) { window = wins[0] } if (!window) { - throw new Error(`Cannot open Menu without a BrowserWindow present`) + throw new Error(`Cannot open Menu without a TopLevelWindow present`) } } @@ -77,7 +77,7 @@ Menu.prototype.popup = function (options) { } Menu.prototype.closePopup = function (window) { - if (window && window.constructor !== BrowserWindow) { + if (window instanceof TopLevelWindow) { this.closePopupAt(window.id) } else { // Passing -1 (invalid) would make closePopupAt close the all menu runners @@ -148,7 +148,7 @@ Menu.setApplicationMenu = function (menu) { menu._callMenuWillShow() bindings.setApplicationMenu(menu) } else { - const windows = BrowserWindow.getAllWindows() + const windows = TopLevelWindow.getAllWindows() return windows.map(w => w.setMenu(menu)) } } diff --git a/lib/browser/api/module-list.js b/lib/browser/api/module-list.js index e6f398b47d00..68f41b491b4c 100644 --- a/lib/browser/api/module-list.js +++ b/lib/browser/api/module-list.js @@ -19,6 +19,7 @@ module.exports = [ {name: 'screen', file: 'screen'}, {name: 'session', file: 'session'}, {name: 'systemPreferences', file: 'system-preferences'}, + {name: 'TopLevelWindow', file: 'top-level-window'}, {name: 'TouchBar', file: 'touch-bar'}, {name: 'Tray', file: 'tray'}, {name: 'webContents', file: 'web-contents'}, diff --git a/lib/browser/api/top-level-window.js b/lib/browser/api/top-level-window.js new file mode 100644 index 000000000000..d798314e96fd --- /dev/null +++ b/lib/browser/api/top-level-window.js @@ -0,0 +1,24 @@ +'use strict' + +const electron = require('electron') +const {EventEmitter} = require('events') +const {TopLevelWindow} = process.atomBinding('top_level_window') + +Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype) + +TopLevelWindow.prototype._init = function () { + // Avoid recursive require. + const {app} = electron + + // Simulate the application menu on platforms other than macOS. + if (process.platform !== 'darwin') { + const menu = app.getApplicationMenu() + if (menu) this.setMenu(menu) + } +} + +TopLevelWindow.getFocusedWindow = () => { + return TopLevelWindow.getAllWindows().find((win) => win.isFocused()) +} + +module.exports = TopLevelWindow