diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index bb7060adb7..372bf3d1e0 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -3,7 +3,8 @@ // found in the LICENSE file. #include "atom/browser/api/atom_api_window.h" -#include "atom/common/native_mate_converters/value_converter.h" + +#include #include "atom/browser/api/atom_api_browser_view.h" #include "atom/browser/api/atom_api_menu.h" @@ -17,6 +18,7 @@ #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/image_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/threading/thread_task_runner_handle.h" @@ -174,6 +176,19 @@ Window::~Window() { base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, window_.release()); } +std::vector g_preloads; +void Window::AddGlobalPreload(const base::FilePath::StringType preloadPath) { + g_preloads.push_back(preloadPath); +} +void Window::RemoveGlobalPreload(const base::FilePath::StringType preloadPath) { + g_preloads.erase( + std::remove(g_preloads.begin(), g_preloads.end(), preloadPath), + g_preloads.end()); +} +std::vector Window::GetGlobalPreloads() { + return g_preloads; +} + void Window::WillCloseWindow(bool* prevent_default) { *prevent_default = Emit("close"); } @@ -1152,6 +1167,12 @@ void Initialize(v8::Local exports, v8::Local unused, &mate::TrackableObject::FromWeakMapID); browser_window.SetMethod("getAllWindows", &mate::TrackableObject::GetAll); + browser_window.SetMethod("addGlobalPreload", + &Window::AddGlobalPreload); + browser_window.SetMethod("removeGlobalPreload", + &Window::RemoveGlobalPreload); + browser_window.SetMethod("getGlobalPreloads", + &Window::GetGlobalPreloads); mate::Dictionary dict(isolate, exports); dict.Set("BrowserWindow", browser_window); diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index 657688f128..3602fbd2f7 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -54,6 +54,10 @@ class Window : public mate::TrackableObject, int32_t ID() const; + static void AddGlobalPreload(const base::FilePath::StringType preloadPath); + static void RemoveGlobalPreload(const base::FilePath::StringType preloadPath); + static std::vector GetGlobalPreloads(); + protected: Window(v8::Isolate* isolate, v8::Local wrapper, const mate::Dictionary& options); diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 81ce0c1e06..262f164c29 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -8,6 +8,7 @@ #include #include +#include "atom/browser/api/atom_api_window.h" #include "atom/browser/native_window.h" #include "atom/browser/web_view_manager.h" #include "atom/common/native_mate_converters/value_converter.h" @@ -136,6 +137,14 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches( LOG(ERROR) << "preload url must be file:// protocol."; } + for (auto preloadPath : atom::api::Window::GetGlobalPreloads()) { + if (base::FilePath(preloadPath).IsAbsolute()) + command_line->AppendSwitchNative(switches::kGlobalPreloadScript, + preloadPath); + else + LOG(ERROR) << "preload script must have absolute path."; + } + // Run Electron APIs and preload script in isolated world bool isolated; if (web_preferences.GetBoolean(options::kContextIsolation, &isolated) && diff --git a/atom/common/options_switches.cc b/atom/common/options_switches.cc index 78235426ef..3332278884 100644 --- a/atom/common/options_switches.cc +++ b/atom/common/options_switches.cc @@ -177,17 +177,18 @@ const char kAppUserModelId[] = "app-user-model-id"; const char kAppPath[] = "app-path"; // The command line switch versions of the options. -const char kBackgroundColor[] = "background-color"; -const char kPreloadScript[] = "preload"; -const char kPreloadURL[] = "preload-url"; -const char kNodeIntegration[] = "node-integration"; -const char kContextIsolation[] = "context-isolation"; -const char kGuestInstanceID[] = "guest-instance-id"; -const char kOpenerID[] = "opener-id"; -const char kScrollBounce[] = "scroll-bounce"; -const char kHiddenPage[] = "hidden-page"; -const char kNativeWindowOpen[] = "native-window-open"; -const char kWebviewTag[] = "webview-tag"; +const char kBackgroundColor[] = "background-color"; +const char kPreloadScript[] = "preload"; +const char kPreloadURL[] = "preload-url"; +const char kGlobalPreloadScript[] = "global-preload"; +const char kNodeIntegration[] = "node-integration"; +const char kContextIsolation[] = "context-isolation"; +const char kGuestInstanceID[] = "guest-instance-id"; +const char kOpenerID[] = "opener-id"; +const char kScrollBounce[] = "scroll-bounce"; +const char kHiddenPage[] = "hidden-page"; +const char kNativeWindowOpen[] = "native-window-open"; +const char kWebviewTag[] = "webview-tag"; // Command switch passed to renderer process to control nodeIntegration. const char kNodeIntegrationInWorker[] = "node-integration-in-worker"; diff --git a/atom/common/options_switches.h b/atom/common/options_switches.h index 401feae48a..550f19f191 100644 --- a/atom/common/options_switches.h +++ b/atom/common/options_switches.h @@ -91,6 +91,7 @@ extern const char kAppPath[]; extern const char kBackgroundColor[]; extern const char kPreloadScript[]; extern const char kPreloadURL[]; +extern const char kGlobalPreloadScript[]; extern const char kNodeIntegration[]; extern const char kContextIsolation[]; extern const char kGuestInstanceID[]; diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index ef90dc0133..bb97c50b61 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -662,6 +662,25 @@ console.log(installed) **Note:** This API cannot be called before the `ready` event of the `app` module is emitted. +#### `BrowserWindow.addGlobalPreload(preloadPath)` + +* `preloadPath` String - An absolute path to the preload script + +Adds a script that will be executed on ALL new BrowserWindows just before normal `preload` scripts run. + +#### `BrowserWindow.removeGlobalPreload(preloadPath)` + +* `preloadPath` String - An absolute path to the preload script + +Removes the given script from the list of global preload scripts + +#### `BrowserWindow.getGlobalPreloads()` + +Returns `String[]` an array of paths to preload scripts that have been registered + +Adds a script that will be executed on ALL new BrowserWindows just before normal `preload` scripts run. + + ### Instance Properties Objects created with `new BrowserWindow` have the following properties: diff --git a/lib/renderer/init.js b/lib/renderer/init.js index 0d1a251c4d..fa6eeb3af8 100644 --- a/lib/renderer/init.js +++ b/lib/renderer/init.js @@ -67,6 +67,7 @@ electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (ev let nodeIntegration = 'false' let webviewTag = 'false' let preloadScript = null +const globalPreloadScripts = [] let isBackgroundPage = false let appPath = null for (let arg of process.argv) { @@ -86,6 +87,8 @@ for (let arg of process.argv) { appPath = arg.substr(arg.indexOf('=') + 1) } else if (arg.indexOf('--webview-tag=') === 0) { webviewTag = arg.substr(arg.indexOf('=') + 1) + } else if (arg.indexOf('--global-preload') === 0) { + globalPreloadScripts.push(arg.substr(arg.indexOf('=') + 1)) } } @@ -171,6 +174,15 @@ if (nodeIntegration === 'true') { }) } +for (const globalPreloadScript of globalPreloadScripts) { + try { + require(globalPreloadScript) + } catch (error) { + console.error('Unable to load global preload script: ' + globalPreloadScript) + console.error(error.stack || error.message) + } +} + // Load the script specfied by the "preload" attribute. if (preloadScript) { try { diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index d04c340dbb..1ffbbf01a6 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1021,6 +1021,41 @@ describe('BrowserWindow module', () => { }) }) + describe('global preload scripts', function () { + it('can add and remove multiple global preload script', function () { + var preload = path.join(fixtures, 'module', 'set-global.js') + var preload2 = path.join(fixtures, 'module', 'set-global-2.js') + assert.deepEqual(BrowserWindow.getGlobalPreloads(), []) + BrowserWindow.addGlobalPreload(preload) + assert.deepEqual(BrowserWindow.getGlobalPreloads(), [preload]) + BrowserWindow.addGlobalPreload(preload2) + assert.deepEqual(BrowserWindow.getGlobalPreloads(), [preload, preload2]) + BrowserWindow.removeGlobalPreload(preload) + assert.deepEqual(BrowserWindow.getGlobalPreloads(), [preload2]) + BrowserWindow.removeGlobalPreload(preload2) + assert.deepEqual(BrowserWindow.getGlobalPreloads(), []) + }) + + it('loads the script before other scripts in window including normal preloads', function (done) { + var preload = path.join(fixtures, 'module', 'set-global.js') + var preload2 = path.join(fixtures, 'module', 'set-global-2.js') + ipcMain.once('answer', function (event, test) { + BrowserWindow.removeGlobalPreload(preload2) + assert.equal(test, 'preload2') + done() + }) + w.destroy() + BrowserWindow.addGlobalPreload(preload2) + w = new BrowserWindow({ + show: false, + webPreferences: { + preload: preload + } + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'preload.html')) + }) + }) + describe('"node-integration" option', () => { it('disables node integration when specified to false', (done) => { const preload = path.join(fixtures, 'module', 'send-later.js') diff --git a/spec/fixtures/module/set-global-2.js b/spec/fixtures/module/set-global-2.js new file mode 100644 index 0000000000..9e2b61da2b --- /dev/null +++ b/spec/fixtures/module/set-global-2.js @@ -0,0 +1 @@ +if (!window.test) window.test = 'preload2' diff --git a/spec/fixtures/module/set-global.js b/spec/fixtures/module/set-global.js index ba4f30d155..5ad98817e0 100644 --- a/spec/fixtures/module/set-global.js +++ b/spec/fixtures/module/set-global.js @@ -1 +1 @@ -window.test = 'preload' +if (!window.test) window.test = 'preload'