diff --git a/electron.gyp b/electron.gyp index 9919d4b0e631..f15ed8e2c2de 100644 --- a/electron.gyp +++ b/electron.gyp @@ -423,11 +423,44 @@ }, ], }, # target atom_js2c_copy + { + 'target_name': 'atom_browserify', + 'type': 'none', + 'dependencies': [ + # depend on this target to ensure the '<(js2c_input_dir)' is created + 'atom_js2c_copy', + ], + 'actions': [ + { + 'action_name': 'atom_browserify', + 'inputs': [ + '<@(browserify_entries)', + # Any js file under `lib/` can be included in the preload bundle. + # Add all js sources as dependencies so any change to a js file will + # trigger a rebuild of the bundle(and consequently of js2c). + '<@(js_sources)', + ], + 'outputs': [ + '<(js2c_input_dir)/preload_bundle.js', + ], + 'action': [ + 'npm', + 'run', + 'browserify', + '--', + '<@(browserify_entries)', + '-o', + '<@(_outputs)', + ], + } + ], + }, # target atom_browserify { 'target_name': 'atom_js2c', 'type': 'none', 'dependencies': [ 'atom_js2c_copy', + 'atom_browserify', ], 'actions': [ { @@ -435,6 +468,7 @@ 'inputs': [ # List all input files that should trigger a rebuild with js2c '<@(js2c_sources)', + '<(js2c_input_dir)/preload_bundle.js', ], 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/atom_natives.h', diff --git a/filenames.gypi b/filenames.gypi index 351c59caa748..aefc78d3944a 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -70,6 +70,9 @@ 'lib/renderer/extensions/storage.js', 'lib/renderer/extensions/web-navigation.js', ], + 'browserify_entries': [ + 'lib/sandboxed_renderer/init.js', + ], 'js2c_sources': [ 'lib/common/asar.js', 'lib/common/asar_init.js', diff --git a/lib/sandboxed_renderer/init.js b/lib/sandboxed_renderer/init.js new file mode 100644 index 000000000000..e34b7bc70759 --- /dev/null +++ b/lib/sandboxed_renderer/init.js @@ -0,0 +1,56 @@ +/* eslint no-eval: "off" */ +/* global binding, preloadPath, process, Buffer */ +const events = require('events') + +const ipcRenderer = new events.EventEmitter() +const proc = new events.EventEmitter() +// eval in window scope: +// http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2 +const geval = eval + +require('../renderer/api/ipc-renderer-setup')(ipcRenderer, binding) + +binding.onMessage = function (channel, args) { + ipcRenderer.emit.apply(ipcRenderer, [channel].concat(args)) +} + +binding.onExit = function () { + proc.emit('exit') +} + +const preloadModules = new Map([ + ['electron', { + ipcRenderer: ipcRenderer + }] +]) + +function preloadRequire (module) { + if (preloadModules.has(module)) { + return preloadModules.get(module) + } + throw new Error('module not found') +} + +// Fetch the source for the preload +let preloadSrc = ipcRenderer.sendSync('ELECTRON_BROWSER_READ_FILE', preloadPath) +if (preloadSrc.err) { + throw new Error(preloadSrc.err) +} + +// Wrap the source into a function receives a `require` function as argument. +// Browserify bundles can make use of this, as explained in: +// https://github.com/substack/node-browserify#multiple-bundles +// +// For example, the user can create a browserify bundle with: +// +// $ browserify -x electron preload.js > renderer.js +// +// and any `require('electron')` calls in `preload.js` will work as expected +// since browserify won't try to include `electron` in the bundle and will fall +// back to the `preloadRequire` function above. +let preloadWrapperSrc = `(function(require, process, Buffer, global) { +${preloadSrc.data} +})` + +let preloadFn = geval(preloadWrapperSrc) +preloadFn(preloadRequire, proc, Buffer, global) diff --git a/package.json b/package.json index 25bde883feca..2fd5bec30542 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.4.1", "devDependencies": { "asar": "^0.11.0", + "browserify": "^13.1.0", "electabul": "~0.0.4", "electron-docs-linter": "^1.6.2", "request": "*", @@ -25,6 +26,7 @@ "private": true, "scripts": { "bootstrap": "python ./script/bootstrap.py", + "browserify": "browserify", "build": "python ./script/build.py -c D", "clean": "python ./script/clean.py", "coverage": "npm run instrument-code-coverage && npm test -- --use-instrumented-asar", diff --git a/script/bootstrap.py b/script/bootstrap.py index 37f4ec21de94..f23884f8afde 100755 --- a/script/bootstrap.py +++ b/script/bootstrap.py @@ -14,10 +14,7 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) VENDOR_DIR = os.path.join(SOURCE_ROOT, 'vendor') PYTHON_26_URL = 'https://chromium.googlesource.com/chromium/deps/python_26' -if os.environ.has_key('CI'): - NPM = os.path.join(SOURCE_ROOT, 'node_modules', '.bin', 'npm') -else: - NPM = 'npm' +NPM = 'npm' if sys.platform in ['win32', 'cygwin']: NPM += '.cmd' diff --git a/script/cibuild b/script/cibuild index ca3cb8db34e2..92dec9db2dee 100755 --- a/script/cibuild +++ b/script/cibuild @@ -65,6 +65,11 @@ def main(): execute([npm, 'install', 'npm@2.12.1']) log_versions() + # Add "./node_modules/.bin" to the beginning of $PATH, which will ensure + # future "npm" invocations use the right version. + node_bin_dir = os.path.join(SOURCE_ROOT, 'node_modules', '.bin') + os.environ['PATH'] = os.path.pathsep.join([node_bin_dir, + os.environ.get('PATH', '')]) is_release = os.environ.has_key('ELECTRON_RELEASE') args = ['--target_arch=' + target_arch]