Merge pull request #4065 from atom/coffee-to-js
[WIP] Convert from CoffeeScript to JavaScript
This commit is contained in:
commit
d9d821cea5
154 changed files with 11304 additions and 7389 deletions
24
atom.gyp
24
atom.gyp
|
@ -28,7 +28,7 @@
|
|||
'target_name': '<(project_name)',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'compile_coffee',
|
||||
'js2asar',
|
||||
'<(project_name)_lib',
|
||||
],
|
||||
'sources': [
|
||||
|
@ -221,7 +221,7 @@
|
|||
'target_name': '<(project_name)_lib',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'atom_coffee2c',
|
||||
'atom_js2c',
|
||||
'vendor/brightray/brightray.gyp:brightray',
|
||||
'vendor/node/node.gyp:node',
|
||||
],
|
||||
|
@ -351,11 +351,11 @@
|
|||
],
|
||||
}, # target <(product_name)_lib
|
||||
{
|
||||
'target_name': 'compile_coffee',
|
||||
'target_name': 'js2asar',
|
||||
'type': 'none',
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'compile_coffee',
|
||||
'action_name': 'js2asar',
|
||||
'variables': {
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
|
@ -366,41 +366,41 @@
|
|||
],
|
||||
},
|
||||
'inputs': [
|
||||
'<@(coffee_sources)',
|
||||
'<@(js_sources)',
|
||||
],
|
||||
'outputs': [
|
||||
'<(resources_path)/atom.asar',
|
||||
],
|
||||
'action': [
|
||||
'python',
|
||||
'tools/coffee2asar.py',
|
||||
'tools/js2asar.py',
|
||||
'<@(_outputs)',
|
||||
'<@(_inputs)',
|
||||
],
|
||||
}
|
||||
],
|
||||
}, # target compile_coffee
|
||||
}, # target js2asar
|
||||
{
|
||||
'target_name': 'atom_coffee2c',
|
||||
'target_name': 'atom_js2c',
|
||||
'type': 'none',
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'atom_coffee2c',
|
||||
'action_name': 'atom_js2c',
|
||||
'inputs': [
|
||||
'<@(coffee2c_sources)',
|
||||
'<@(js2c_sources)',
|
||||
],
|
||||
'outputs': [
|
||||
'<(SHARED_INTERMEDIATE_DIR)/atom_natives.h',
|
||||
],
|
||||
'action': [
|
||||
'python',
|
||||
'tools/coffee2c.py',
|
||||
'tools/js2c.py',
|
||||
'<@(_outputs)',
|
||||
'<@(_inputs)',
|
||||
],
|
||||
}
|
||||
],
|
||||
}, # target atom_coffee2c
|
||||
}, # target atom_js2c
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
{deprecate, session, Menu} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
bindings = process.atomBinding 'app'
|
||||
downloadItemBindings = process.atomBinding 'download_item'
|
||||
|
||||
app = bindings.app
|
||||
app.__proto__ = EventEmitter.prototype
|
||||
|
||||
app.setApplicationMenu = (menu) ->
|
||||
Menu.setApplicationMenu menu
|
||||
|
||||
app.getApplicationMenu = ->
|
||||
Menu.getApplicationMenu()
|
||||
|
||||
app.commandLine =
|
||||
appendSwitch: bindings.appendSwitch,
|
||||
appendArgument: bindings.appendArgument
|
||||
|
||||
if process.platform is 'darwin'
|
||||
app.dock =
|
||||
bounce: (type='informational') -> bindings.dockBounce type
|
||||
cancelBounce: bindings.dockCancelBounce
|
||||
setBadge: bindings.dockSetBadgeText
|
||||
getBadge: bindings.dockGetBadgeText
|
||||
hide: bindings.dockHide
|
||||
show: bindings.dockShow
|
||||
setMenu: bindings.dockSetMenu
|
||||
|
||||
appPath = null
|
||||
app.setAppPath = (path) ->
|
||||
appPath = path
|
||||
|
||||
app.getAppPath = ->
|
||||
appPath
|
||||
|
||||
# Routes the events to webContents.
|
||||
for name in ['login', 'certificate-error', 'select-client-certificate']
|
||||
do (name) ->
|
||||
app.on name, (event, webContents, args...) ->
|
||||
webContents.emit name, event, args...
|
||||
|
||||
# Deprecated.
|
||||
app.getHomeDir = deprecate 'app.getHomeDir', 'app.getPath', ->
|
||||
@getPath 'home'
|
||||
app.getDataPath = deprecate 'app.getDataPath', 'app.getPath', ->
|
||||
@getPath 'userData'
|
||||
app.setDataPath = deprecate 'app.setDataPath', 'app.setPath', (path) ->
|
||||
@setPath 'userData', path
|
||||
app.resolveProxy = deprecate 'app.resolveProxy', 'session.defaultSession.resolveProxy', (url, callback) ->
|
||||
session.defaultSession.resolveProxy url, callback
|
||||
deprecate.rename app, 'terminate', 'quit'
|
||||
deprecate.event app, 'finish-launching', 'ready', ->
|
||||
setImmediate => # give default app a chance to setup default menu.
|
||||
@emit 'finish-launching'
|
||||
deprecate.event app, 'activate-with-no-open-windows', 'activate', (event, hasVisibleWindows) ->
|
||||
@emit 'activate-with-no-open-windows', event if not hasVisibleWindows
|
||||
deprecate.event app, 'select-certificate', 'select-client-certificate'
|
||||
|
||||
# Wrappers for native classes.
|
||||
wrapDownloadItem = (downloadItem) ->
|
||||
# downloadItem is an EventEmitter.
|
||||
downloadItem.__proto__ = EventEmitter.prototype
|
||||
# Deprecated.
|
||||
deprecate.property downloadItem, 'url', 'getURL'
|
||||
deprecate.property downloadItem, 'filename', 'getFilename'
|
||||
deprecate.property downloadItem, 'mimeType', 'getMimeType'
|
||||
deprecate.rename downloadItem, 'getUrl', 'getURL'
|
||||
downloadItemBindings._setWrapDownloadItem wrapDownloadItem
|
||||
|
||||
# Only one App object pemitted.
|
||||
module.exports = app
|
131
atom/browser/api/lib/app.js
Normal file
131
atom/browser/api/lib/app.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
var EventEmitter, Menu, app, appPath, bindings, deprecate, downloadItemBindings, fn, i, len, name, ref, ref1, session, wrapDownloadItem,
|
||||
slice = [].slice;
|
||||
|
||||
ref = require('electron'), deprecate = ref.deprecate, session = ref.session, Menu = ref.Menu;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
bindings = process.atomBinding('app');
|
||||
|
||||
downloadItemBindings = process.atomBinding('download_item');
|
||||
|
||||
app = bindings.app;
|
||||
|
||||
app.__proto__ = EventEmitter.prototype;
|
||||
|
||||
app.setApplicationMenu = function(menu) {
|
||||
return Menu.setApplicationMenu(menu);
|
||||
};
|
||||
|
||||
app.getApplicationMenu = function() {
|
||||
return Menu.getApplicationMenu();
|
||||
};
|
||||
|
||||
app.commandLine = {
|
||||
appendSwitch: bindings.appendSwitch,
|
||||
appendArgument: bindings.appendArgument
|
||||
};
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
app.dock = {
|
||||
bounce: function(type) {
|
||||
if (type == null) {
|
||||
type = 'informational';
|
||||
}
|
||||
return bindings.dockBounce(type);
|
||||
},
|
||||
cancelBounce: bindings.dockCancelBounce,
|
||||
setBadge: bindings.dockSetBadgeText,
|
||||
getBadge: bindings.dockGetBadgeText,
|
||||
hide: bindings.dockHide,
|
||||
show: bindings.dockShow,
|
||||
setMenu: bindings.dockSetMenu
|
||||
};
|
||||
}
|
||||
|
||||
appPath = null;
|
||||
|
||||
app.setAppPath = function(path) {
|
||||
return appPath = path;
|
||||
};
|
||||
|
||||
app.getAppPath = function() {
|
||||
return appPath;
|
||||
};
|
||||
|
||||
|
||||
/* Routes the events to webContents. */
|
||||
|
||||
ref1 = ['login', 'certificate-error', 'select-client-certificate'];
|
||||
fn = function(name) {
|
||||
return app.on(name, function() {
|
||||
var args, event, webContents;
|
||||
event = arguments[0], webContents = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
return webContents.emit.apply(webContents, [name, event].concat(slice.call(args)));
|
||||
});
|
||||
};
|
||||
for (i = 0, len = ref1.length; i < len; i++) {
|
||||
name = ref1[i];
|
||||
fn(name);
|
||||
}
|
||||
|
||||
|
||||
/* Deprecated. */
|
||||
|
||||
app.getHomeDir = deprecate('app.getHomeDir', 'app.getPath', function() {
|
||||
return this.getPath('home');
|
||||
});
|
||||
|
||||
app.getDataPath = deprecate('app.getDataPath', 'app.getPath', function() {
|
||||
return this.getPath('userData');
|
||||
});
|
||||
|
||||
app.setDataPath = deprecate('app.setDataPath', 'app.setPath', function(path) {
|
||||
return this.setPath('userData', path);
|
||||
});
|
||||
|
||||
app.resolveProxy = deprecate('app.resolveProxy', 'session.defaultSession.resolveProxy', function(url, callback) {
|
||||
return session.defaultSession.resolveProxy(url, callback);
|
||||
});
|
||||
|
||||
deprecate.rename(app, 'terminate', 'quit');
|
||||
|
||||
deprecate.event(app, 'finish-launching', 'ready', function() {
|
||||
|
||||
/* give default app a chance to setup default menu. */
|
||||
return setImmediate((function(_this) {
|
||||
return function() {
|
||||
return _this.emit('finish-launching');
|
||||
};
|
||||
})(this));
|
||||
});
|
||||
|
||||
deprecate.event(app, 'activate-with-no-open-windows', 'activate', function(event, hasVisibleWindows) {
|
||||
if (!hasVisibleWindows) {
|
||||
return this.emit('activate-with-no-open-windows', event);
|
||||
}
|
||||
});
|
||||
|
||||
deprecate.event(app, 'select-certificate', 'select-client-certificate');
|
||||
|
||||
|
||||
/* Wrappers for native classes. */
|
||||
|
||||
wrapDownloadItem = function(downloadItem) {
|
||||
|
||||
/* downloadItem is an EventEmitter. */
|
||||
downloadItem.__proto__ = EventEmitter.prototype;
|
||||
|
||||
/* Deprecated. */
|
||||
deprecate.property(downloadItem, 'url', 'getURL');
|
||||
deprecate.property(downloadItem, 'filename', 'getFilename');
|
||||
deprecate.property(downloadItem, 'mimeType', 'getMimeType');
|
||||
return deprecate.rename(downloadItem, 'getUrl', 'getURL');
|
||||
};
|
||||
|
||||
downloadItemBindings._setWrapDownloadItem(wrapDownloadItem);
|
||||
|
||||
|
||||
/* Only one App object pemitted. */
|
||||
|
||||
module.exports = app;
|
|
@ -1,12 +0,0 @@
|
|||
{deprecate} = require 'electron'
|
||||
|
||||
autoUpdater =
|
||||
if process.platform is 'win32'
|
||||
require './auto-updater/auto-updater-win'
|
||||
else
|
||||
require './auto-updater/auto-updater-native'
|
||||
|
||||
# Deprecated.
|
||||
deprecate.rename autoUpdater, 'setFeedUrl', 'setFeedURL'
|
||||
|
||||
module.exports = autoUpdater
|
14
atom/browser/api/lib/auto-updater.js
Normal file
14
atom/browser/api/lib/auto-updater.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
var autoUpdater, deprecate;
|
||||
|
||||
deprecate = require('electron').deprecate;
|
||||
|
||||
autoUpdater = process.platform === 'win32' ? require('./auto-updater/auto-updater-win') : require('./auto-updater/auto-updater-native');
|
||||
|
||||
|
||||
/* Deprecated. */
|
||||
|
||||
deprecate.rename(autoUpdater, 'setFeedUrl', 'setFeedURL');
|
||||
|
||||
|
||||
|
||||
module.exports = autoUpdater;
|
|
@ -1,6 +0,0 @@
|
|||
{EventEmitter} = require 'events'
|
||||
{autoUpdater} = process.atomBinding 'auto_updater'
|
||||
|
||||
autoUpdater.__proto__ = EventEmitter.prototype
|
||||
|
||||
module.exports = autoUpdater
|
9
atom/browser/api/lib/auto-updater/auto-updater-native.js
Normal file
9
atom/browser/api/lib/auto-updater/auto-updater-native.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
var EventEmitter, autoUpdater;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
autoUpdater = process.atomBinding('auto_updater').autoUpdater;
|
||||
|
||||
autoUpdater.__proto__ = EventEmitter.prototype;
|
||||
|
||||
module.exports = autoUpdater;
|
|
@ -1,42 +0,0 @@
|
|||
{app} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
url = require 'url'
|
||||
|
||||
squirrelUpdate = require './squirrel-update-win'
|
||||
|
||||
class AutoUpdater extends EventEmitter
|
||||
quitAndInstall: ->
|
||||
squirrelUpdate.processStart()
|
||||
app.quit()
|
||||
|
||||
setFeedURL: (updateURL) ->
|
||||
@updateURL = updateURL
|
||||
|
||||
checkForUpdates: ->
|
||||
return @emitError 'Update URL is not set' unless @updateURL
|
||||
return @emitError 'Can not find Squirrel' unless squirrelUpdate.supported()
|
||||
|
||||
@emit 'checking-for-update'
|
||||
|
||||
squirrelUpdate.download @updateURL, (error, update) =>
|
||||
return @emitError error if error?
|
||||
return @emit 'update-not-available' unless update?
|
||||
|
||||
@emit 'update-available'
|
||||
|
||||
squirrelUpdate.update @updateURL, (error) =>
|
||||
return @emitError error if error?
|
||||
|
||||
{releaseNotes, version} = update
|
||||
# Following information is not available on Windows, so fake them.
|
||||
date = new Date
|
||||
url = @updateURL
|
||||
|
||||
@emit 'update-downloaded', {}, releaseNotes, version, date, url, => @quitAndInstall()
|
||||
|
||||
# Private: Emit both error object and message, this is to keep compatibility
|
||||
# with Old APIs.
|
||||
emitError: (message) ->
|
||||
@emit 'error', new Error(message), message
|
||||
|
||||
module.exports = new AutoUpdater
|
78
atom/browser/api/lib/auto-updater/auto-updater-win.js
Normal file
78
atom/browser/api/lib/auto-updater/auto-updater-win.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
var AutoUpdater, EventEmitter, app, squirrelUpdate, url,
|
||||
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
hasProp = {}.hasOwnProperty;
|
||||
|
||||
app = require('electron').app;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
url = require('url');
|
||||
|
||||
squirrelUpdate = require('./squirrel-update-win');
|
||||
|
||||
AutoUpdater = (function(superClass) {
|
||||
extend(AutoUpdater, superClass);
|
||||
|
||||
function AutoUpdater() {
|
||||
return AutoUpdater.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
AutoUpdater.prototype.quitAndInstall = function() {
|
||||
squirrelUpdate.processStart();
|
||||
return app.quit();
|
||||
};
|
||||
|
||||
AutoUpdater.prototype.setFeedURL = function(updateURL) {
|
||||
return this.updateURL = updateURL;
|
||||
};
|
||||
|
||||
AutoUpdater.prototype.checkForUpdates = function() {
|
||||
if (!this.updateURL) {
|
||||
return this.emitError('Update URL is not set');
|
||||
}
|
||||
if (!squirrelUpdate.supported()) {
|
||||
return this.emitError('Can not find Squirrel');
|
||||
}
|
||||
this.emit('checking-for-update');
|
||||
return squirrelUpdate.download(this.updateURL, (function(_this) {
|
||||
return function(error, update) {
|
||||
if (error != null) {
|
||||
return _this.emitError(error);
|
||||
}
|
||||
if (update == null) {
|
||||
return _this.emit('update-not-available');
|
||||
}
|
||||
_this.emit('update-available');
|
||||
return squirrelUpdate.update(_this.updateURL, function(error) {
|
||||
var date, releaseNotes, version;
|
||||
if (error != null) {
|
||||
return _this.emitError(error);
|
||||
}
|
||||
releaseNotes = update.releaseNotes, version = update.version;
|
||||
|
||||
/* Following information is not available on Windows, so fake them. */
|
||||
date = new Date;
|
||||
url = _this.updateURL;
|
||||
return _this.emit('update-downloaded', {}, releaseNotes, version, date, url, function() {
|
||||
return _this.quitAndInstall();
|
||||
});
|
||||
});
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Private: Emit both error object and message, this is to keep compatibility
|
||||
with Old APIs.
|
||||
*/
|
||||
|
||||
AutoUpdater.prototype.emitError = function(message) {
|
||||
return this.emit('error', new Error(message), message);
|
||||
};
|
||||
|
||||
return AutoUpdater;
|
||||
|
||||
})(EventEmitter);
|
||||
|
||||
module.exports = new AutoUpdater;
|
|
@ -1,67 +0,0 @@
|
|||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
{spawn} = require 'child_process'
|
||||
|
||||
appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/
|
||||
updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe
|
||||
exeName = path.basename process.execPath
|
||||
|
||||
# Spawn a command and invoke the callback when it completes with an error
|
||||
# and the output from standard out.
|
||||
spawnUpdate = (args, detached, callback) ->
|
||||
try
|
||||
spawnedProcess = spawn updateExe, args, {detached}
|
||||
catch error
|
||||
# Shouldn't happen, but still guard it.
|
||||
process.nextTick -> callback error
|
||||
return
|
||||
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
spawnedProcess.stdout.on 'data', (data) -> stdout += data
|
||||
spawnedProcess.stderr.on 'data', (data) -> stderr += data
|
||||
|
||||
errorEmitted = false
|
||||
spawnedProcess.on 'error', (error) ->
|
||||
errorEmitted = true
|
||||
callback error
|
||||
spawnedProcess.on 'exit', (code, signal) ->
|
||||
# We may have already emitted an error.
|
||||
return if errorEmitted
|
||||
|
||||
# Process terminated with error.
|
||||
if code isnt 0
|
||||
return callback "Command failed: #{signal ? code}\n#{stderr}"
|
||||
|
||||
# Success.
|
||||
callback null, stdout
|
||||
|
||||
# Start an instance of the installed app.
|
||||
exports.processStart = (callback) ->
|
||||
spawnUpdate ['--processStart', exeName], true, ->
|
||||
|
||||
# Download the releases specified by the URL and write new results to stdout.
|
||||
exports.download = (updateURL, callback) ->
|
||||
spawnUpdate ['--download', updateURL], false, (error, stdout) ->
|
||||
return callback(error) if error?
|
||||
|
||||
try
|
||||
# Last line of output is the JSON details about the releases
|
||||
json = stdout.trim().split('\n').pop()
|
||||
update = JSON.parse(json)?.releasesToApply?.pop?()
|
||||
catch
|
||||
return callback "Invalid result:\n#{stdout}"
|
||||
|
||||
callback null, update
|
||||
|
||||
# Update the application to the latest remote version specified by URL.
|
||||
exports.update = (updateURL, callback) ->
|
||||
spawnUpdate ['--update', updateURL], false, callback
|
||||
|
||||
# Is the Update.exe installed with the current application?
|
||||
exports.supported = ->
|
||||
try
|
||||
fs.accessSync updateExe, fs.R_OK
|
||||
return true
|
||||
catch
|
||||
return false
|
118
atom/browser/api/lib/auto-updater/squirrel-update-win.js
Normal file
118
atom/browser/api/lib/auto-updater/squirrel-update-win.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
var appFolder, exeName, fs, path, spawn, spawnUpdate, updateExe;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
spawn = require('child_process').spawn;
|
||||
|
||||
|
||||
/* i.e. my-app/app-0.1.13/ */
|
||||
|
||||
appFolder = path.dirname(process.execPath);
|
||||
|
||||
|
||||
/* i.e. my-app/Update.exe */
|
||||
|
||||
updateExe = path.resolve(appFolder, '..', 'Update.exe');
|
||||
|
||||
exeName = path.basename(process.execPath);
|
||||
|
||||
|
||||
/*
|
||||
Spawn a command and invoke the callback when it completes with an error
|
||||
and the output from standard out.
|
||||
*/
|
||||
|
||||
spawnUpdate = function(args, detached, callback) {
|
||||
var error, error1, errorEmitted, spawnedProcess, stderr, stdout;
|
||||
try {
|
||||
spawnedProcess = spawn(updateExe, args, {
|
||||
detached: detached
|
||||
});
|
||||
} catch (error1) {
|
||||
error = error1;
|
||||
|
||||
/* Shouldn't happen, but still guard it. */
|
||||
process.nextTick(function() {
|
||||
return callback(error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
stdout = '';
|
||||
stderr = '';
|
||||
spawnedProcess.stdout.on('data', function(data) {
|
||||
return stdout += data;
|
||||
});
|
||||
spawnedProcess.stderr.on('data', function(data) {
|
||||
return stderr += data;
|
||||
});
|
||||
errorEmitted = false;
|
||||
spawnedProcess.on('error', function(error) {
|
||||
errorEmitted = true;
|
||||
return callback(error);
|
||||
});
|
||||
return spawnedProcess.on('exit', function(code, signal) {
|
||||
|
||||
/* We may have already emitted an error. */
|
||||
if (errorEmitted) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Process terminated with error. */
|
||||
if (code !== 0) {
|
||||
return callback("Command failed: " + (signal != null ? signal : code) + "\n" + stderr);
|
||||
}
|
||||
|
||||
/* Success. */
|
||||
return callback(null, stdout);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Start an instance of the installed app. */
|
||||
|
||||
exports.processStart = function(callback) {
|
||||
return spawnUpdate(['--processStart', exeName], true, function() {});
|
||||
};
|
||||
|
||||
|
||||
/* Download the releases specified by the URL and write new results to stdout. */
|
||||
|
||||
exports.download = function(updateURL, callback) {
|
||||
return spawnUpdate(['--download', updateURL], false, function(error, stdout) {
|
||||
var error1, json, ref, ref1, update;
|
||||
if (error != null) {
|
||||
return callback(error);
|
||||
}
|
||||
try {
|
||||
|
||||
/* Last line of output is the JSON details about the releases */
|
||||
json = stdout.trim().split('\n').pop();
|
||||
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === "function" ? ref1.pop() : void 0 : void 0 : void 0;
|
||||
} catch (error1) {
|
||||
return callback("Invalid result:\n" + stdout);
|
||||
}
|
||||
return callback(null, update);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Update the application to the latest remote version specified by URL. */
|
||||
|
||||
exports.update = function(updateURL, callback) {
|
||||
return spawnUpdate(['--update', updateURL], false, callback);
|
||||
};
|
||||
|
||||
|
||||
/* Is the Update.exe installed with the current application? */
|
||||
|
||||
exports.supported = function() {
|
||||
var error1;
|
||||
try {
|
||||
fs.accessSync(updateExe, fs.R_OK);
|
||||
return true;
|
||||
} catch (error1) {
|
||||
return false;
|
||||
}
|
||||
};
|
|
@ -1,114 +0,0 @@
|
|||
{ipcMain, deprecate} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
{BrowserWindow} = process.atomBinding 'window'
|
||||
BrowserWindow::__proto__ = EventEmitter.prototype
|
||||
|
||||
BrowserWindow::_init = ->
|
||||
{app} = require 'electron' # avoid recursive require.
|
||||
|
||||
# Simulate the application menu on platforms other than OS X.
|
||||
if process.platform isnt 'darwin'
|
||||
menu = app.getApplicationMenu()
|
||||
@setMenu menu if menu?
|
||||
|
||||
# Make new windows requested by links behave like "window.open"
|
||||
@webContents.on '-new-window', (event, url, frameName) ->
|
||||
options = show: true, width: 800, height: 600
|
||||
ipcMain.emit 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options
|
||||
|
||||
# window.resizeTo(...)
|
||||
# window.moveTo(...)
|
||||
@webContents.on 'move', (event, size) =>
|
||||
@setBounds size
|
||||
|
||||
# Hide the auto-hide menu when webContents is focused.
|
||||
@webContents.on 'activate', =>
|
||||
if process.platform isnt 'darwin' and @isMenuBarAutoHide() and @isMenuBarVisible()
|
||||
@setMenuBarVisibility false
|
||||
|
||||
# Forward the crashed event.
|
||||
@webContents.on 'crashed', =>
|
||||
@emit 'crashed'
|
||||
|
||||
# Change window title to page title.
|
||||
@webContents.on 'page-title-updated', (event, title, explicitSet) =>
|
||||
@emit 'page-title-updated', event, title
|
||||
@setTitle title unless event.defaultPrevented
|
||||
|
||||
# Sometimes the webContents doesn't get focus when window is shown, so we have
|
||||
# to force focusing on webContents in this case. The safest way is to focus it
|
||||
# when we first start to load URL, if we do it earlier it won't have effect,
|
||||
# if we do it later we might move focus in the page.
|
||||
# Though this hack is only needed on OS X when the app is launched from
|
||||
# Finder, we still do it on all platforms in case of other bugs we don't know.
|
||||
@webContents.once 'load-url', ->
|
||||
@focus()
|
||||
|
||||
# Redirect focus/blur event to app instance too.
|
||||
@on 'blur', (event) =>
|
||||
app.emit 'browser-window-blur', event, this
|
||||
@on 'focus', (event) =>
|
||||
app.emit 'browser-window-focus', event, this
|
||||
|
||||
# Notify the creation of the window.
|
||||
app.emit 'browser-window-created', {}, this
|
||||
|
||||
# Be compatible with old APIs.
|
||||
@webContents.on 'devtools-focused', => @emit 'devtools-focused'
|
||||
@webContents.on 'devtools-opened', => @emit 'devtools-opened'
|
||||
@webContents.on 'devtools-closed', => @emit 'devtools-closed'
|
||||
Object.defineProperty this, 'devToolsWebContents',
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: -> @webContents.devToolsWebContents
|
||||
|
||||
BrowserWindow.getFocusedWindow = ->
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
return window for window in windows when window.isFocused()
|
||||
null
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents) ->
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
return window for window in windows when window.webContents?.equal webContents
|
||||
|
||||
BrowserWindow.fromDevToolsWebContents = (webContents) ->
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
return window for window in windows when window.devToolsWebContents?.equal webContents
|
||||
|
||||
# Helpers.
|
||||
BrowserWindow::loadURL = -> @webContents.loadURL.apply @webContents, arguments
|
||||
BrowserWindow::getURL = -> @webContents.getURL()
|
||||
BrowserWindow::reload = -> @webContents.reload.apply @webContents, arguments
|
||||
BrowserWindow::send = -> @webContents.send.apply @webContents, arguments
|
||||
BrowserWindow::openDevTools = -> @webContents.openDevTools.apply @webContents, arguments
|
||||
BrowserWindow::closeDevTools = -> @webContents.closeDevTools()
|
||||
BrowserWindow::isDevToolsOpened = -> @webContents.isDevToolsOpened()
|
||||
BrowserWindow::isDevToolsFocused = -> @webContents.isDevToolsFocused()
|
||||
BrowserWindow::toggleDevTools = -> @webContents.toggleDevTools()
|
||||
BrowserWindow::inspectElement = -> @webContents.inspectElement.apply @webContents, arguments
|
||||
BrowserWindow::inspectServiceWorker = -> @webContents.inspectServiceWorker()
|
||||
|
||||
# Deprecated.
|
||||
deprecate.member BrowserWindow, 'undo', 'webContents'
|
||||
deprecate.member BrowserWindow, 'redo', 'webContents'
|
||||
deprecate.member BrowserWindow, 'cut', 'webContents'
|
||||
deprecate.member BrowserWindow, 'copy', 'webContents'
|
||||
deprecate.member BrowserWindow, 'paste', 'webContents'
|
||||
deprecate.member BrowserWindow, 'selectAll', 'webContents'
|
||||
deprecate.member BrowserWindow, 'reloadIgnoringCache', 'webContents'
|
||||
deprecate.member BrowserWindow, 'isLoading', 'webContents'
|
||||
deprecate.member BrowserWindow, 'isWaitingForResponse', 'webContents'
|
||||
deprecate.member BrowserWindow, 'stop', 'webContents'
|
||||
deprecate.member BrowserWindow, 'isCrashed', 'webContents'
|
||||
deprecate.member BrowserWindow, 'print', 'webContents'
|
||||
deprecate.member BrowserWindow, 'printToPDF', 'webContents'
|
||||
deprecate.rename BrowserWindow, 'restart', 'reload'
|
||||
deprecate.rename BrowserWindow, 'loadUrl', 'loadURL'
|
||||
deprecate.rename BrowserWindow, 'getUrl', 'getURL'
|
||||
BrowserWindow::executeJavaScriptInDevTools = deprecate 'executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', (code) ->
|
||||
@devToolsWebContents?.executeJavaScript code
|
||||
BrowserWindow::getPageTitle = deprecate 'getPageTitle', 'webContents.getTitle', ->
|
||||
@webContents?.getTitle()
|
||||
|
||||
module.exports = BrowserWindow
|
250
atom/browser/api/lib/browser-window.js
Normal file
250
atom/browser/api/lib/browser-window.js
Normal file
|
@ -0,0 +1,250 @@
|
|||
var BrowserWindow, EventEmitter, deprecate, ipcMain, ref;
|
||||
|
||||
ref = require('electron'), ipcMain = ref.ipcMain, deprecate = ref.deprecate;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
BrowserWindow = process.atomBinding('window').BrowserWindow;
|
||||
|
||||
BrowserWindow.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
BrowserWindow.prototype._init = function() {
|
||||
|
||||
/* avoid recursive require. */
|
||||
var app, menu;
|
||||
app = require('electron').app;
|
||||
|
||||
/* Simulate the application menu on platforms other than OS X. */
|
||||
if (process.platform !== 'darwin') {
|
||||
menu = app.getApplicationMenu();
|
||||
if (menu != null) {
|
||||
this.setMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make new windows requested by links behave like "window.open" */
|
||||
this.webContents.on('-new-window', function(event, url, frameName) {
|
||||
var options;
|
||||
options = {
|
||||
show: true,
|
||||
width: 800,
|
||||
height: 600
|
||||
};
|
||||
return ipcMain.emit('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options);
|
||||
});
|
||||
|
||||
/*
|
||||
window.resizeTo(...)
|
||||
window.moveTo(...)
|
||||
*/
|
||||
this.webContents.on('move', (function(_this) {
|
||||
return function(event, size) {
|
||||
return _this.setBounds(size);
|
||||
};
|
||||
})(this));
|
||||
|
||||
/* Hide the auto-hide menu when webContents is focused. */
|
||||
this.webContents.on('activate', (function(_this) {
|
||||
return function() {
|
||||
if (process.platform !== 'darwin' && _this.isMenuBarAutoHide() && _this.isMenuBarVisible()) {
|
||||
return _this.setMenuBarVisibility(false);
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
|
||||
/* Forward the crashed event. */
|
||||
this.webContents.on('crashed', (function(_this) {
|
||||
return function() {
|
||||
return _this.emit('crashed');
|
||||
};
|
||||
})(this));
|
||||
|
||||
/* Change window title to page title. */
|
||||
this.webContents.on('page-title-updated', (function(_this) {
|
||||
return function(event, title, explicitSet) {
|
||||
_this.emit('page-title-updated', event, title);
|
||||
if (!event.defaultPrevented) {
|
||||
return _this.setTitle(title);
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
|
||||
/*
|
||||
Sometimes the webContents doesn't get focus when window is shown, so we have
|
||||
to force focusing on webContents in this case. The safest way is to focus it
|
||||
when we first start to load URL, if we do it earlier it won't have effect,
|
||||
if we do it later we might move focus in the page.
|
||||
Though this hack is only needed on OS X when the app is launched from
|
||||
Finder, we still do it on all platforms in case of other bugs we don't know.
|
||||
*/
|
||||
this.webContents.once('load-url', function() {
|
||||
return this.focus();
|
||||
});
|
||||
|
||||
/* Redirect focus/blur event to app instance too. */
|
||||
this.on('blur', (function(_this) {
|
||||
return function(event) {
|
||||
return app.emit('browser-window-blur', event, _this);
|
||||
};
|
||||
})(this));
|
||||
this.on('focus', (function(_this) {
|
||||
return function(event) {
|
||||
return app.emit('browser-window-focus', event, _this);
|
||||
};
|
||||
})(this));
|
||||
|
||||
/* Notify the creation of the window. */
|
||||
app.emit('browser-window-created', {}, this);
|
||||
|
||||
/* Be compatible with old APIs. */
|
||||
this.webContents.on('devtools-focused', (function(_this) {
|
||||
return function() {
|
||||
return _this.emit('devtools-focused');
|
||||
};
|
||||
})(this));
|
||||
this.webContents.on('devtools-opened', (function(_this) {
|
||||
return function() {
|
||||
return _this.emit('devtools-opened');
|
||||
};
|
||||
})(this));
|
||||
this.webContents.on('devtools-closed', (function(_this) {
|
||||
return function() {
|
||||
return _this.emit('devtools-closed');
|
||||
};
|
||||
})(this));
|
||||
return Object.defineProperty(this, 'devToolsWebContents', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: function() {
|
||||
return this.webContents.devToolsWebContents;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
BrowserWindow.getFocusedWindow = function() {
|
||||
var i, len, window, windows;
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
for (i = 0, len = windows.length; i < len; i++) {
|
||||
window = windows[i];
|
||||
if (window.isFocused()) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromWebContents = function(webContents) {
|
||||
var i, len, ref1, window, windows;
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
for (i = 0, len = windows.length; i < len; i++) {
|
||||
window = windows[i];
|
||||
if ((ref1 = window.webContents) != null ? ref1.equal(webContents) : void 0) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BrowserWindow.fromDevToolsWebContents = function(webContents) {
|
||||
var i, len, ref1, window, windows;
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
for (i = 0, len = windows.length; i < len; i++) {
|
||||
window = windows[i];
|
||||
if ((ref1 = window.devToolsWebContents) != null ? ref1.equal(webContents) : void 0) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Helpers. */
|
||||
|
||||
BrowserWindow.prototype.loadURL = function() {
|
||||
return this.webContents.loadURL.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.getURL = function() {
|
||||
return this.webContents.getURL();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.reload = function() {
|
||||
return this.webContents.reload.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.send = function() {
|
||||
return this.webContents.send.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.openDevTools = function() {
|
||||
return this.webContents.openDevTools.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.closeDevTools = function() {
|
||||
return this.webContents.closeDevTools();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.isDevToolsOpened = function() {
|
||||
return this.webContents.isDevToolsOpened();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.isDevToolsFocused = function() {
|
||||
return this.webContents.isDevToolsFocused();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.toggleDevTools = function() {
|
||||
return this.webContents.toggleDevTools();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectElement = function() {
|
||||
return this.webContents.inspectElement.apply(this.webContents, arguments);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectServiceWorker = function() {
|
||||
return this.webContents.inspectServiceWorker();
|
||||
};
|
||||
|
||||
|
||||
/* Deprecated. */
|
||||
|
||||
deprecate.member(BrowserWindow, 'undo', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'redo', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'cut', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'copy', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'paste', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'selectAll', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'reloadIgnoringCache', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'isLoading', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'isWaitingForResponse', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'stop', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'isCrashed', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'print', 'webContents');
|
||||
|
||||
deprecate.member(BrowserWindow, 'printToPDF', 'webContents');
|
||||
|
||||
deprecate.rename(BrowserWindow, 'restart', 'reload');
|
||||
|
||||
deprecate.rename(BrowserWindow, 'loadUrl', 'loadURL');
|
||||
|
||||
deprecate.rename(BrowserWindow, 'getUrl', 'getURL');
|
||||
|
||||
BrowserWindow.prototype.executeJavaScriptInDevTools = deprecate('executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', function(code) {
|
||||
var ref1;
|
||||
return (ref1 = this.devToolsWebContents) != null ? ref1.executeJavaScript(code) : void 0;
|
||||
});
|
||||
|
||||
BrowserWindow.prototype.getPageTitle = deprecate('getPageTitle', 'webContents.getTitle', function() {
|
||||
var ref1;
|
||||
return (ref1 = this.webContents) != null ? ref1.getTitle() : void 0;
|
||||
});
|
||||
|
||||
module.exports = BrowserWindow;
|
|
@ -1 +0,0 @@
|
|||
module.exports = process.atomBinding 'content_tracing'
|
1
atom/browser/api/lib/content-tracing.js
Normal file
1
atom/browser/api/lib/content-tracing.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = process.atomBinding('content_tracing');
|
|
@ -1,127 +0,0 @@
|
|||
{app, BrowserWindow} = require 'electron'
|
||||
|
||||
binding = process.atomBinding 'dialog'
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
fileDialogProperties =
|
||||
openFile: 1 << 0
|
||||
openDirectory: 1 << 1
|
||||
multiSelections: 1 << 2
|
||||
createDirectory: 1 << 3
|
||||
|
||||
messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
|
||||
|
||||
messageBoxOptions =
|
||||
noLink: 1 << 0
|
||||
|
||||
parseArgs = (window, options, callback) ->
|
||||
unless window is null or window?.constructor is BrowserWindow
|
||||
# Shift.
|
||||
callback = options
|
||||
options = window
|
||||
window = null
|
||||
if not callback? and typeof options is 'function'
|
||||
# Shift.
|
||||
callback = options
|
||||
options = null
|
||||
[window, options, callback]
|
||||
|
||||
checkAppInitialized = ->
|
||||
throw new Error('dialog module can only be used after app is ready') unless app.isReady()
|
||||
|
||||
module.exports =
|
||||
showOpenDialog: (args...) ->
|
||||
checkAppInitialized()
|
||||
[window, options, callback] = parseArgs args...
|
||||
|
||||
options ?= title: 'Open', properties: ['openFile']
|
||||
options.properties ?= ['openFile']
|
||||
throw new TypeError('Properties need to be array') unless Array.isArray options.properties
|
||||
|
||||
properties = 0
|
||||
for prop, value of fileDialogProperties
|
||||
properties |= value if prop in options.properties
|
||||
|
||||
options.title ?= ''
|
||||
options.defaultPath ?= ''
|
||||
options.filters ?= []
|
||||
|
||||
wrappedCallback =
|
||||
if typeof callback is 'function'
|
||||
(success, result) -> callback(if success then result)
|
||||
else
|
||||
null
|
||||
|
||||
binding.showOpenDialog String(options.title),
|
||||
String(options.defaultPath),
|
||||
options.filters
|
||||
properties,
|
||||
window,
|
||||
wrappedCallback
|
||||
|
||||
showSaveDialog: (args...) ->
|
||||
checkAppInitialized()
|
||||
[window, options, callback] = parseArgs args...
|
||||
|
||||
options ?= title: 'Save'
|
||||
options.title ?= ''
|
||||
options.defaultPath ?= ''
|
||||
options.filters ?= []
|
||||
|
||||
wrappedCallback =
|
||||
if typeof callback is 'function'
|
||||
(success, result) -> callback(if success then result)
|
||||
else
|
||||
null
|
||||
|
||||
binding.showSaveDialog String(options.title),
|
||||
String(options.defaultPath),
|
||||
options.filters
|
||||
window,
|
||||
wrappedCallback
|
||||
|
||||
showMessageBox: (args...) ->
|
||||
checkAppInitialized()
|
||||
[window, options, callback] = parseArgs args...
|
||||
|
||||
options ?= type: 'none'
|
||||
options.type ?= 'none'
|
||||
messageBoxType = messageBoxTypes.indexOf options.type
|
||||
throw new TypeError('Invalid message box type') unless messageBoxType > -1
|
||||
|
||||
throw new TypeError('Buttons need to be array') unless Array.isArray options.buttons
|
||||
|
||||
options.title ?= ''
|
||||
options.message ?= ''
|
||||
options.detail ?= ''
|
||||
options.icon ?= null
|
||||
options.defaultId ?= -1
|
||||
|
||||
# Choose a default button to get selected when dialog is cancelled.
|
||||
unless options.cancelId?
|
||||
options.cancelId = 0
|
||||
for text, i in options.buttons
|
||||
if text.toLowerCase() in ['cancel', 'no']
|
||||
options.cancelId = i
|
||||
break
|
||||
|
||||
flags = if options.noLink then messageBoxOptions.noLink else 0
|
||||
|
||||
binding.showMessageBox messageBoxType,
|
||||
options.buttons,
|
||||
options.defaultId,
|
||||
options.cancelId,
|
||||
flags,
|
||||
options.title,
|
||||
options.message,
|
||||
options.detail,
|
||||
options.icon,
|
||||
window,
|
||||
callback
|
||||
|
||||
showErrorBox: (args...) ->
|
||||
binding.showErrorBox args...
|
||||
|
||||
# Mark standard asynchronous functions.
|
||||
for api in ['showMessageBox', 'showOpenDialog', 'showSaveDialog']
|
||||
v8Util.setHiddenValue module.exports[api], 'asynchronous', true
|
175
atom/browser/api/lib/dialog.js
Normal file
175
atom/browser/api/lib/dialog.js
Normal file
|
@ -0,0 +1,175 @@
|
|||
var BrowserWindow, api, app, binding, checkAppInitialized, fileDialogProperties, j, len, messageBoxOptions, messageBoxTypes, parseArgs, ref, ref1, v8Util,
|
||||
slice = [].slice,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
ref = require('electron'), app = ref.app, BrowserWindow = ref.BrowserWindow;
|
||||
|
||||
binding = process.atomBinding('dialog');
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
fileDialogProperties = {
|
||||
openFile: 1 << 0,
|
||||
openDirectory: 1 << 1,
|
||||
multiSelections: 1 << 2,
|
||||
createDirectory: 1 << 3
|
||||
};
|
||||
|
||||
messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
|
||||
|
||||
messageBoxOptions = {
|
||||
noLink: 1 << 0
|
||||
};
|
||||
|
||||
parseArgs = function(window, options, callback) {
|
||||
if (!(window === null || (window != null ? window.constructor : void 0) === BrowserWindow)) {
|
||||
|
||||
/* Shift. */
|
||||
callback = options;
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
if ((callback == null) && typeof options === 'function') {
|
||||
|
||||
/* Shift. */
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
return [window, options, callback];
|
||||
};
|
||||
|
||||
checkAppInitialized = function() {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('dialog module can only be used after app is ready');
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
showOpenDialog: function() {
|
||||
var args, callback, options, prop, properties, ref1, value, window, wrappedCallback;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
checkAppInitialized();
|
||||
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
};
|
||||
}
|
||||
if (options.properties == null) {
|
||||
options.properties = ['openFile'];
|
||||
}
|
||||
if (!Array.isArray(options.properties)) {
|
||||
throw new TypeError('Properties need to be array');
|
||||
}
|
||||
properties = 0;
|
||||
for (prop in fileDialogProperties) {
|
||||
value = fileDialogProperties[prop];
|
||||
if (indexOf.call(options.properties, prop) >= 0) {
|
||||
properties |= value;
|
||||
}
|
||||
}
|
||||
if (options.title == null) {
|
||||
options.title = '';
|
||||
}
|
||||
if (options.defaultPath == null) {
|
||||
options.defaultPath = '';
|
||||
}
|
||||
if (options.filters == null) {
|
||||
options.filters = [];
|
||||
}
|
||||
wrappedCallback = typeof callback === 'function' ? function(success, result) {
|
||||
return callback(success ? result : void 0);
|
||||
} : null;
|
||||
return binding.showOpenDialog(String(options.title), String(options.defaultPath), options.filters, properties, window, wrappedCallback);
|
||||
},
|
||||
showSaveDialog: function() {
|
||||
var args, callback, options, ref1, window, wrappedCallback;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
checkAppInitialized();
|
||||
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Save'
|
||||
};
|
||||
}
|
||||
if (options.title == null) {
|
||||
options.title = '';
|
||||
}
|
||||
if (options.defaultPath == null) {
|
||||
options.defaultPath = '';
|
||||
}
|
||||
if (options.filters == null) {
|
||||
options.filters = [];
|
||||
}
|
||||
wrappedCallback = typeof callback === 'function' ? function(success, result) {
|
||||
return callback(success ? result : void 0);
|
||||
} : null;
|
||||
return binding.showSaveDialog(String(options.title), String(options.defaultPath), options.filters, window, wrappedCallback);
|
||||
},
|
||||
showMessageBox: function() {
|
||||
var args, callback, flags, i, j, len, messageBoxType, options, ref1, ref2, ref3, text, window;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
checkAppInitialized();
|
||||
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
|
||||
if (options == null) {
|
||||
options = {
|
||||
type: 'none'
|
||||
};
|
||||
}
|
||||
if (options.type == null) {
|
||||
options.type = 'none';
|
||||
}
|
||||
messageBoxType = messageBoxTypes.indexOf(options.type);
|
||||
if (!(messageBoxType > -1)) {
|
||||
throw new TypeError('Invalid message box type');
|
||||
}
|
||||
if (!Array.isArray(options.buttons)) {
|
||||
throw new TypeError('Buttons need to be array');
|
||||
}
|
||||
if (options.title == null) {
|
||||
options.title = '';
|
||||
}
|
||||
if (options.message == null) {
|
||||
options.message = '';
|
||||
}
|
||||
if (options.detail == null) {
|
||||
options.detail = '';
|
||||
}
|
||||
if (options.icon == null) {
|
||||
options.icon = null;
|
||||
}
|
||||
if (options.defaultId == null) {
|
||||
options.defaultId = -1;
|
||||
}
|
||||
|
||||
/* Choose a default button to get selected when dialog is cancelled. */
|
||||
if (options.cancelId == null) {
|
||||
options.cancelId = 0;
|
||||
ref2 = options.buttons;
|
||||
for (i = j = 0, len = ref2.length; j < len; i = ++j) {
|
||||
text = ref2[i];
|
||||
if ((ref3 = text.toLowerCase()) === 'cancel' || ref3 === 'no') {
|
||||
options.cancelId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
flags = options.noLink ? messageBoxOptions.noLink : 0;
|
||||
return binding.showMessageBox(messageBoxType, options.buttons, options.defaultId, options.cancelId, flags, options.title, options.message, options.detail, options.icon, window, callback);
|
||||
},
|
||||
showErrorBox: function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return binding.showErrorBox.apply(binding, args);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Mark standard asynchronous functions. */
|
||||
|
||||
ref1 = ['showMessageBox', 'showOpenDialog', 'showSaveDialog'];
|
||||
for (j = 0, len = ref1.length; j < len; j++) {
|
||||
api = ref1[j];
|
||||
v8Util.setHiddenValue(module.exports[api], 'asynchronous', true);
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
common = require '../../../../common/api/lib/exports/electron'
|
||||
|
||||
# Import common modules.
|
||||
common.defineProperties exports
|
||||
|
||||
Object.defineProperties exports,
|
||||
# Browser side modules, please sort with alphabet order.
|
||||
app:
|
||||
enumerable: true
|
||||
get: -> require '../app'
|
||||
autoUpdater:
|
||||
enumerable: true
|
||||
get: -> require '../auto-updater'
|
||||
BrowserWindow:
|
||||
enumerable: true
|
||||
get: -> require '../browser-window'
|
||||
contentTracing:
|
||||
enumerable: true
|
||||
get: -> require '../content-tracing'
|
||||
dialog:
|
||||
enumerable: true
|
||||
get: -> require '../dialog'
|
||||
ipcMain:
|
||||
enumerable: true
|
||||
get: -> require '../ipc-main'
|
||||
globalShortcut:
|
||||
enumerable: true
|
||||
get: -> require '../global-shortcut'
|
||||
Menu:
|
||||
enumerable: true
|
||||
get: -> require '../menu'
|
||||
MenuItem:
|
||||
enumerable: true
|
||||
get: -> require '../menu-item'
|
||||
powerMonitor:
|
||||
enumerable: true
|
||||
get: -> require '../power-monitor'
|
||||
powerSaveBlocker:
|
||||
enumerable: true
|
||||
get: -> require '../power-save-blocker'
|
||||
protocol:
|
||||
enumerable: true
|
||||
get: -> require '../protocol'
|
||||
screen:
|
||||
enumerable: true
|
||||
get: -> require '../screen'
|
||||
session:
|
||||
enumerable: true
|
||||
get: -> require '../session'
|
||||
Tray:
|
||||
enumerable: true
|
||||
get: -> require '../tray'
|
||||
# The internal modules, invisible unless you know their names.
|
||||
NavigationController:
|
||||
get: -> require '../navigation-controller'
|
||||
webContents:
|
||||
get: -> require '../web-contents'
|
115
atom/browser/api/lib/exports/electron.js
Normal file
115
atom/browser/api/lib/exports/electron.js
Normal file
|
@ -0,0 +1,115 @@
|
|||
var common;
|
||||
|
||||
common = require('../../../../common/api/lib/exports/electron');
|
||||
|
||||
|
||||
/* Import common modules. */
|
||||
|
||||
common.defineProperties(exports);
|
||||
|
||||
Object.defineProperties(exports, {
|
||||
|
||||
/* Browser side modules, please sort with alphabet order. */
|
||||
app: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../app');
|
||||
}
|
||||
},
|
||||
autoUpdater: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../auto-updater');
|
||||
}
|
||||
},
|
||||
BrowserWindow: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../browser-window');
|
||||
}
|
||||
},
|
||||
contentTracing: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../content-tracing');
|
||||
}
|
||||
},
|
||||
dialog: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../dialog');
|
||||
}
|
||||
},
|
||||
ipcMain: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../ipc-main');
|
||||
}
|
||||
},
|
||||
globalShortcut: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../global-shortcut');
|
||||
}
|
||||
},
|
||||
Menu: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../menu');
|
||||
}
|
||||
},
|
||||
MenuItem: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../menu-item');
|
||||
}
|
||||
},
|
||||
powerMonitor: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../power-monitor');
|
||||
}
|
||||
},
|
||||
powerSaveBlocker: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../power-save-blocker');
|
||||
}
|
||||
},
|
||||
protocol: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../protocol');
|
||||
}
|
||||
},
|
||||
screen: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../screen');
|
||||
}
|
||||
},
|
||||
session: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../session');
|
||||
}
|
||||
},
|
||||
Tray: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../tray');
|
||||
}
|
||||
},
|
||||
|
||||
/* The internal modules, invisible unless you know their names. */
|
||||
NavigationController: {
|
||||
get: function() {
|
||||
return require('../navigation-controller');
|
||||
}
|
||||
},
|
||||
webContents: {
|
||||
get: function() {
|
||||
return require('../web-contents');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
{globalShortcut} = process.atomBinding 'global_shortcut'
|
||||
|
||||
module.exports = globalShortcut
|
5
atom/browser/api/lib/global-shortcut.js
Normal file
5
atom/browser/api/lib/global-shortcut.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
var globalShortcut;
|
||||
|
||||
globalShortcut = process.atomBinding('global_shortcut').globalShortcut;
|
||||
|
||||
module.exports = globalShortcut;
|
|
@ -1,3 +0,0 @@
|
|||
{EventEmitter} = require 'events'
|
||||
|
||||
module.exports = new EventEmitter
|
5
atom/browser/api/lib/ipc-main.js
Normal file
5
atom/browser/api/lib/ipc-main.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
var EventEmitter;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
module.exports = new EventEmitter;
|
|
@ -1,6 +0,0 @@
|
|||
{deprecate, ipcMain} = require 'electron'
|
||||
|
||||
# This module is deprecated, we mirror everything from ipcMain.
|
||||
deprecate.warn 'ipc module', 'require("electron").ipcMain'
|
||||
|
||||
module.exports = ipcMain
|
10
atom/browser/api/lib/ipc.js
Normal file
10
atom/browser/api/lib/ipc.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
var deprecate, ipcMain, ref;
|
||||
|
||||
ref = require('electron'), deprecate = ref.deprecate, ipcMain = ref.ipcMain;
|
||||
|
||||
|
||||
/* This module is deprecated, we mirror everything from ipcMain. */
|
||||
|
||||
deprecate.warn('ipc module', 'require("electron").ipcMain');
|
||||
|
||||
module.exports = ipcMain;
|
|
@ -1,73 +0,0 @@
|
|||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
nextCommandId = 0
|
||||
|
||||
# Maps role to methods of webContents
|
||||
rolesMap =
|
||||
undo: 'undo'
|
||||
redo: 'redo'
|
||||
cut: 'cut'
|
||||
copy: 'copy'
|
||||
paste: 'paste'
|
||||
selectall: 'selectAll'
|
||||
minimize: 'minimize'
|
||||
close: 'close'
|
||||
|
||||
# Maps methods that should be called directly on the BrowserWindow instance
|
||||
methodInBrowserWindow =
|
||||
minimize: true
|
||||
close: true
|
||||
|
||||
class MenuItem
|
||||
@types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
|
||||
|
||||
constructor: (options) ->
|
||||
{Menu} = require 'electron'
|
||||
|
||||
{click, @selector, @type, @role, @label, @sublabel, @accelerator, @icon, @enabled, @visible, @checked, @submenu} = options
|
||||
|
||||
if @submenu? and @submenu.constructor isnt Menu
|
||||
@submenu = Menu.buildFromTemplate @submenu
|
||||
@type = 'submenu' if not @type? and @submenu?
|
||||
throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu
|
||||
|
||||
@overrideReadOnlyProperty 'type', 'normal'
|
||||
@overrideReadOnlyProperty 'role'
|
||||
@overrideReadOnlyProperty 'accelerator'
|
||||
@overrideReadOnlyProperty 'icon'
|
||||
@overrideReadOnlyProperty 'submenu'
|
||||
@overrideProperty 'label', ''
|
||||
@overrideProperty 'sublabel', ''
|
||||
@overrideProperty 'enabled', true
|
||||
@overrideProperty 'visible', true
|
||||
@overrideProperty 'checked', false
|
||||
|
||||
throw new Error("Unknown menu type #{@type}") if MenuItem.types.indexOf(@type) is -1
|
||||
|
||||
@commandId = ++nextCommandId
|
||||
@click = (focusedWindow) =>
|
||||
# Manually flip the checked flags when clicked.
|
||||
@checked = !@checked if @type in ['checkbox', 'radio']
|
||||
|
||||
if @role and rolesMap[@role] and process.platform isnt 'darwin' and focusedWindow?
|
||||
methodName = rolesMap[@role]
|
||||
if methodInBrowserWindow[methodName]
|
||||
focusedWindow[methodName]()
|
||||
else
|
||||
focusedWindow.webContents?[methodName]()
|
||||
else if typeof click is 'function'
|
||||
click this, focusedWindow
|
||||
else if typeof @selector is 'string'
|
||||
Menu.sendActionToFirstResponder @selector
|
||||
|
||||
overrideProperty: (name, defaultValue=null) ->
|
||||
this[name] ?= defaultValue
|
||||
|
||||
overrideReadOnlyProperty: (name, defaultValue=null) ->
|
||||
this[name] ?= defaultValue
|
||||
Object.defineProperty this, name,
|
||||
enumerable: true
|
||||
writable: false
|
||||
value: this[name]
|
||||
|
||||
module.exports = MenuItem
|
108
atom/browser/api/lib/menu-item.js
Normal file
108
atom/browser/api/lib/menu-item.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
var MenuItem, methodInBrowserWindow, nextCommandId, rolesMap, v8Util;
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
nextCommandId = 0;
|
||||
|
||||
|
||||
/* Maps role to methods of webContents */
|
||||
|
||||
rolesMap = {
|
||||
undo: 'undo',
|
||||
redo: 'redo',
|
||||
cut: 'cut',
|
||||
copy: 'copy',
|
||||
paste: 'paste',
|
||||
selectall: 'selectAll',
|
||||
minimize: 'minimize',
|
||||
close: 'close'
|
||||
};
|
||||
|
||||
|
||||
/* Maps methods that should be called directly on the BrowserWindow instance */
|
||||
|
||||
methodInBrowserWindow = {
|
||||
minimize: true,
|
||||
close: true
|
||||
};
|
||||
|
||||
MenuItem = (function() {
|
||||
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'];
|
||||
|
||||
function MenuItem(options) {
|
||||
var Menu, click, ref;
|
||||
Menu = require('electron').Menu;
|
||||
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);
|
||||
}
|
||||
if ((this.type == null) && (this.submenu != null)) {
|
||||
this.type = 'submenu';
|
||||
}
|
||||
if (this.type === 'submenu' && ((ref = this.submenu) != null ? ref.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 = (function(_this) {
|
||||
return function(focusedWindow) {
|
||||
|
||||
/* Manually flip the checked flags when clicked. */
|
||||
var methodName, ref1, ref2;
|
||||
if ((ref1 = _this.type) === 'checkbox' || ref1 === 'radio') {
|
||||
_this.checked = !_this.checked;
|
||||
}
|
||||
if (_this.role && rolesMap[_this.role] && process.platform !== 'darwin' && (focusedWindow != null)) {
|
||||
methodName = rolesMap[_this.role];
|
||||
if (methodInBrowserWindow[methodName]) {
|
||||
return focusedWindow[methodName]();
|
||||
} else {
|
||||
return (ref2 = focusedWindow.webContents) != null ? ref2[methodName]() : void 0;
|
||||
}
|
||||
} else if (typeof click === 'function') {
|
||||
return click(_this, focusedWindow);
|
||||
} else if (typeof _this.selector === 'string') {
|
||||
return Menu.sendActionToFirstResponder(_this.selector);
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
}
|
||||
|
||||
MenuItem.prototype.overrideProperty = function(name, defaultValue) {
|
||||
if (defaultValue == null) {
|
||||
defaultValue = null;
|
||||
}
|
||||
return this[name] != null ? this[name] : this[name] = defaultValue;
|
||||
};
|
||||
|
||||
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]
|
||||
});
|
||||
};
|
||||
|
||||
return MenuItem;
|
||||
|
||||
})();
|
||||
|
||||
module.exports = MenuItem;
|
|
@ -1,178 +0,0 @@
|
|||
{BrowserWindow, MenuItem} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
bindings = process.atomBinding 'menu'
|
||||
|
||||
# Automatically generated radio menu item's group id.
|
||||
nextGroupId = 0
|
||||
|
||||
# Search between seperators to find a radio menu item and return its group id,
|
||||
# otherwise generate a group id.
|
||||
generateGroupId = (items, pos) ->
|
||||
if pos > 0
|
||||
for i in [pos - 1..0]
|
||||
item = items[i]
|
||||
return item.groupId if item.type is 'radio'
|
||||
break if item.type is 'separator'
|
||||
else if pos < items.length
|
||||
for i in [pos..items.length - 1]
|
||||
item = items[i]
|
||||
return item.groupId if item.type is 'radio'
|
||||
break if item.type is 'separator'
|
||||
++nextGroupId
|
||||
|
||||
# Returns the index of item according to |id|.
|
||||
indexOfItemById = (items, id) ->
|
||||
return i for item, i in items when item.id is id
|
||||
-1
|
||||
|
||||
# Returns the index of where to insert the item according to |position|.
|
||||
indexToInsertByPosition = (items, position) ->
|
||||
return items.length unless position
|
||||
|
||||
[query, id] = position.split '='
|
||||
insertIndex = indexOfItemById items, id
|
||||
if insertIndex is -1 and query isnt 'endof'
|
||||
console.warn "Item with id '#{id}' is not found"
|
||||
return items.length
|
||||
|
||||
switch query
|
||||
when 'after'
|
||||
insertIndex++
|
||||
when 'endof'
|
||||
# If the |id| doesn't exist, then create a new group with the |id|.
|
||||
if insertIndex is -1
|
||||
items.push id: id, type: 'separator'
|
||||
insertIndex = items.length - 1
|
||||
|
||||
# Find the end of the group.
|
||||
insertIndex++
|
||||
while insertIndex < items.length and items[insertIndex].type isnt 'separator'
|
||||
insertIndex++
|
||||
|
||||
insertIndex
|
||||
|
||||
Menu = bindings.Menu
|
||||
Menu::__proto__ = EventEmitter.prototype
|
||||
|
||||
Menu::_init = ->
|
||||
@commandsMap = {}
|
||||
@groupsMap = {}
|
||||
@items = []
|
||||
@delegate =
|
||||
isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
|
||||
isCommandIdEnabled: (commandId) => @commandsMap[commandId]?.enabled
|
||||
isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible
|
||||
getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
|
||||
getIconForCommandId: (commandId) => @commandsMap[commandId]?.icon
|
||||
executeCommand: (commandId) =>
|
||||
@commandsMap[commandId]?.click BrowserWindow.getFocusedWindow()
|
||||
menuWillShow: =>
|
||||
# Make sure radio groups have at least one menu item seleted.
|
||||
for id, group of @groupsMap
|
||||
checked = false
|
||||
for radioItem in group when radioItem.checked
|
||||
checked = true
|
||||
break
|
||||
v8Util.setHiddenValue group[0], 'checked', true unless checked
|
||||
|
||||
Menu::popup = (window, x, y) ->
|
||||
unless window?.constructor is BrowserWindow
|
||||
# Shift.
|
||||
y = x
|
||||
x = window
|
||||
window = BrowserWindow.getFocusedWindow()
|
||||
if x? and y?
|
||||
@_popupAt(window, x, y)
|
||||
else
|
||||
@_popup window
|
||||
|
||||
Menu::append = (item) ->
|
||||
@insert @getItemCount(), item
|
||||
|
||||
Menu::insert = (pos, item) ->
|
||||
throw new TypeError('Invalid item') unless item?.constructor is MenuItem
|
||||
|
||||
switch item.type
|
||||
when 'normal' then @insertItem pos, item.commandId, item.label
|
||||
when 'checkbox' then @insertCheckItem pos, item.commandId, item.label
|
||||
when 'separator' then @insertSeparator pos
|
||||
when 'submenu' then @insertSubMenu pos, item.commandId, item.label, item.submenu
|
||||
when 'radio'
|
||||
# Grouping radio menu items.
|
||||
item.overrideReadOnlyProperty 'groupId', generateGroupId(@items, pos)
|
||||
@groupsMap[item.groupId] ?= []
|
||||
@groupsMap[item.groupId].push item
|
||||
|
||||
# Setting a radio menu item should flip other items in the group.
|
||||
v8Util.setHiddenValue item, 'checked', item.checked
|
||||
Object.defineProperty item, 'checked',
|
||||
enumerable: true
|
||||
get: -> v8Util.getHiddenValue item, 'checked'
|
||||
set: (val) =>
|
||||
for otherItem in @groupsMap[item.groupId] when otherItem isnt item
|
||||
v8Util.setHiddenValue otherItem, 'checked', false
|
||||
v8Util.setHiddenValue item, 'checked', true
|
||||
|
||||
@insertRadioItem pos, item.commandId, item.label, item.groupId
|
||||
|
||||
@setSublabel pos, item.sublabel if item.sublabel?
|
||||
@setIcon pos, item.icon if item.icon?
|
||||
@setRole pos, item.role if item.role?
|
||||
|
||||
# Make menu accessable to items.
|
||||
item.overrideReadOnlyProperty 'menu', this
|
||||
|
||||
# Remember the items.
|
||||
@items.splice pos, 0, item
|
||||
@commandsMap[item.commandId] = item
|
||||
|
||||
# Force menuWillShow to be called
|
||||
Menu::_callMenuWillShow = ->
|
||||
@delegate?.menuWillShow()
|
||||
item.submenu._callMenuWillShow() for item in @items when item.submenu?
|
||||
|
||||
applicationMenu = null
|
||||
Menu.setApplicationMenu = (menu) ->
|
||||
throw new TypeError('Invalid menu') unless menu is null or menu.constructor is Menu
|
||||
applicationMenu = menu # Keep a reference.
|
||||
|
||||
if process.platform is 'darwin'
|
||||
return if menu is null
|
||||
menu._callMenuWillShow()
|
||||
bindings.setApplicationMenu menu
|
||||
else
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
w.setMenu menu for w in windows
|
||||
|
||||
Menu.getApplicationMenu = -> applicationMenu
|
||||
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
|
||||
|
||||
Menu.buildFromTemplate = (template) ->
|
||||
throw new TypeError('Invalid template for Menu') unless Array.isArray template
|
||||
|
||||
positionedTemplate = []
|
||||
insertIndex = 0
|
||||
|
||||
for item in template
|
||||
if item.position
|
||||
insertIndex = indexToInsertByPosition positionedTemplate, item.position
|
||||
else
|
||||
# If no |position| is specified, insert after last item.
|
||||
insertIndex++
|
||||
positionedTemplate.splice insertIndex, 0, item
|
||||
|
||||
menu = new Menu
|
||||
|
||||
for item in positionedTemplate
|
||||
throw new TypeError('Invalid template for MenuItem') unless typeof item is 'object'
|
||||
|
||||
menuItem = new MenuItem(item)
|
||||
menuItem[key] ?= value for key, value of item
|
||||
menu.append menuItem
|
||||
|
||||
menu
|
||||
|
||||
module.exports = Menu
|
350
atom/browser/api/lib/menu.js
Normal file
350
atom/browser/api/lib/menu.js
Normal file
|
@ -0,0 +1,350 @@
|
|||
var BrowserWindow, EventEmitter, Menu, MenuItem, applicationMenu, bindings, generateGroupId, indexOfItemById, indexToInsertByPosition, nextGroupId, ref, v8Util;
|
||||
|
||||
ref = require('electron'), BrowserWindow = ref.BrowserWindow, MenuItem = ref.MenuItem;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
bindings = process.atomBinding('menu');
|
||||
|
||||
|
||||
/* Automatically generated radio menu item's group id. */
|
||||
|
||||
nextGroupId = 0;
|
||||
|
||||
|
||||
/* Search between seperators to find a radio menu item and return its group id, */
|
||||
|
||||
|
||||
/* otherwise generate a group id. */
|
||||
|
||||
generateGroupId = function(items, pos) {
|
||||
var i, item, j, k, ref1, ref2, ref3;
|
||||
if (pos > 0) {
|
||||
for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) {
|
||||
item = items[i];
|
||||
if (item.type === 'radio') {
|
||||
return item.groupId;
|
||||
}
|
||||
if (item.type === 'separator') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (pos < items.length) {
|
||||
for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) {
|
||||
item = items[i];
|
||||
if (item.type === 'radio') {
|
||||
return item.groupId;
|
||||
}
|
||||
if (item.type === 'separator') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ++nextGroupId;
|
||||
};
|
||||
|
||||
|
||||
/* Returns the index of item according to |id|. */
|
||||
|
||||
indexOfItemById = function(items, id) {
|
||||
var i, item, j, len;
|
||||
for (i = j = 0, len = items.length; j < len; i = ++j) {
|
||||
item = items[i];
|
||||
if (item.id === id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
|
||||
/* Returns the index of where to insert the item according to |position|. */
|
||||
|
||||
indexToInsertByPosition = function(items, position) {
|
||||
var id, insertIndex, query, ref1;
|
||||
if (!position) {
|
||||
return items.length;
|
||||
}
|
||||
ref1 = position.split('='), query = ref1[0], id = ref1[1];
|
||||
insertIndex = indexOfItemById(items, id);
|
||||
if (insertIndex === -1 && query !== 'endof') {
|
||||
console.warn("Item with id '" + id + "' is not found");
|
||||
return items.length;
|
||||
}
|
||||
switch (query) {
|
||||
case 'after':
|
||||
insertIndex++;
|
||||
break;
|
||||
case 'endof':
|
||||
|
||||
/* If the |id| doesn't exist, then create a new group with the |id|. */
|
||||
if (insertIndex === -1) {
|
||||
items.push({
|
||||
id: id,
|
||||
type: 'separator'
|
||||
});
|
||||
insertIndex = items.length - 1;
|
||||
}
|
||||
|
||||
/* Find the end of the group. */
|
||||
insertIndex++;
|
||||
while (insertIndex < items.length && items[insertIndex].type !== 'separator') {
|
||||
insertIndex++;
|
||||
}
|
||||
}
|
||||
return insertIndex;
|
||||
};
|
||||
|
||||
Menu = bindings.Menu;
|
||||
|
||||
Menu.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
Menu.prototype._init = function() {
|
||||
this.commandsMap = {};
|
||||
this.groupsMap = {};
|
||||
this.items = [];
|
||||
return this.delegate = {
|
||||
isCommandIdChecked: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.checked : void 0;
|
||||
};
|
||||
})(this),
|
||||
isCommandIdEnabled: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.enabled : void 0;
|
||||
};
|
||||
})(this),
|
||||
isCommandIdVisible: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.visible : void 0;
|
||||
};
|
||||
})(this),
|
||||
getAcceleratorForCommandId: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.accelerator : void 0;
|
||||
};
|
||||
})(this),
|
||||
getIconForCommandId: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.icon : void 0;
|
||||
};
|
||||
})(this),
|
||||
executeCommand: (function(_this) {
|
||||
return function(commandId) {
|
||||
var ref1;
|
||||
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.click(BrowserWindow.getFocusedWindow()) : void 0;
|
||||
};
|
||||
})(this),
|
||||
menuWillShow: (function(_this) {
|
||||
return function() {
|
||||
|
||||
/* Make sure radio groups have at least one menu item seleted. */
|
||||
var checked, group, id, j, len, radioItem, ref1, results;
|
||||
ref1 = _this.groupsMap;
|
||||
results = [];
|
||||
for (id in ref1) {
|
||||
group = ref1[id];
|
||||
checked = false;
|
||||
for (j = 0, len = group.length; j < len; j++) {
|
||||
radioItem = group[j];
|
||||
if (!radioItem.checked) {
|
||||
continue;
|
||||
}
|
||||
checked = true;
|
||||
break;
|
||||
}
|
||||
if (!checked) {
|
||||
results.push(v8Util.setHiddenValue(group[0], 'checked', true));
|
||||
} else {
|
||||
results.push(void 0);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
})(this)
|
||||
};
|
||||
};
|
||||
|
||||
Menu.prototype.popup = function(window, x, y) {
|
||||
if ((window != null ? window.constructor : void 0) !== BrowserWindow) {
|
||||
|
||||
/* Shift. */
|
||||
y = x;
|
||||
x = window;
|
||||
window = BrowserWindow.getFocusedWindow();
|
||||
}
|
||||
if ((x != null) && (y != null)) {
|
||||
return this._popupAt(window, x, y);
|
||||
} else {
|
||||
return this._popup(window);
|
||||
}
|
||||
};
|
||||
|
||||
Menu.prototype.append = function(item) {
|
||||
return this.insert(this.getItemCount(), item);
|
||||
};
|
||||
|
||||
Menu.prototype.insert = function(pos, item) {
|
||||
var base, name;
|
||||
if ((item != null ? item.constructor : void 0) !== MenuItem) {
|
||||
throw new TypeError('Invalid item');
|
||||
}
|
||||
switch (item.type) {
|
||||
case 'normal':
|
||||
this.insertItem(pos, item.commandId, item.label);
|
||||
break;
|
||||
case 'checkbox':
|
||||
this.insertCheckItem(pos, item.commandId, item.label);
|
||||
break;
|
||||
case 'separator':
|
||||
this.insertSeparator(pos);
|
||||
break;
|
||||
case 'submenu':
|
||||
this.insertSubMenu(pos, item.commandId, item.label, item.submenu);
|
||||
break;
|
||||
case 'radio':
|
||||
|
||||
/* Grouping radio menu items. */
|
||||
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos));
|
||||
if ((base = this.groupsMap)[name = item.groupId] == null) {
|
||||
base[name] = [];
|
||||
}
|
||||
this.groupsMap[item.groupId].push(item);
|
||||
|
||||
/* Setting a radio menu item should flip other items in the group. */
|
||||
v8Util.setHiddenValue(item, 'checked', item.checked);
|
||||
Object.defineProperty(item, 'checked', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return v8Util.getHiddenValue(item, 'checked');
|
||||
},
|
||||
set: (function(_this) {
|
||||
return function(val) {
|
||||
var j, len, otherItem, ref1;
|
||||
ref1 = _this.groupsMap[item.groupId];
|
||||
for (j = 0, len = ref1.length; j < len; j++) {
|
||||
otherItem = ref1[j];
|
||||
if (otherItem !== item) {
|
||||
v8Util.setHiddenValue(otherItem, 'checked', false);
|
||||
}
|
||||
}
|
||||
return v8Util.setHiddenValue(item, 'checked', true);
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
|
||||
}
|
||||
if (item.sublabel != null) {
|
||||
this.setSublabel(pos, item.sublabel);
|
||||
}
|
||||
if (item.icon != null) {
|
||||
this.setIcon(pos, item.icon);
|
||||
}
|
||||
if (item.role != null) {
|
||||
this.setRole(pos, item.role);
|
||||
}
|
||||
|
||||
/* Make menu accessable to items. */
|
||||
item.overrideReadOnlyProperty('menu', this);
|
||||
|
||||
/* Remember the items. */
|
||||
this.items.splice(pos, 0, item);
|
||||
return this.commandsMap[item.commandId] = item;
|
||||
};
|
||||
|
||||
|
||||
/* Force menuWillShow to be called */
|
||||
|
||||
Menu.prototype._callMenuWillShow = function() {
|
||||
var item, j, len, ref1, ref2, results;
|
||||
if ((ref1 = this.delegate) != null) {
|
||||
ref1.menuWillShow();
|
||||
}
|
||||
ref2 = this.items;
|
||||
results = [];
|
||||
for (j = 0, len = ref2.length; j < len; j++) {
|
||||
item = ref2[j];
|
||||
if (item.submenu != null) {
|
||||
results.push(item.submenu._callMenuWillShow());
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
applicationMenu = null;
|
||||
|
||||
Menu.setApplicationMenu = function(menu) {
|
||||
var j, len, results, w, windows;
|
||||
if (!(menu === null || menu.constructor === Menu)) {
|
||||
throw new TypeError('Invalid menu');
|
||||
}
|
||||
|
||||
/* Keep a reference. */
|
||||
applicationMenu = menu;
|
||||
if (process.platform === 'darwin') {
|
||||
if (menu === null) {
|
||||
return;
|
||||
}
|
||||
menu._callMenuWillShow();
|
||||
return bindings.setApplicationMenu(menu);
|
||||
} else {
|
||||
windows = BrowserWindow.getAllWindows();
|
||||
results = [];
|
||||
for (j = 0, len = windows.length; j < len; j++) {
|
||||
w = windows[j];
|
||||
results.push(w.setMenu(menu));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
Menu.getApplicationMenu = function() {
|
||||
return applicationMenu;
|
||||
};
|
||||
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
|
||||
|
||||
Menu.buildFromTemplate = function(template) {
|
||||
var insertIndex, item, j, k, key, len, len1, menu, menuItem, positionedTemplate, value;
|
||||
if (!Array.isArray(template)) {
|
||||
throw new TypeError('Invalid template for Menu');
|
||||
}
|
||||
positionedTemplate = [];
|
||||
insertIndex = 0;
|
||||
for (j = 0, len = template.length; j < len; j++) {
|
||||
item = template[j];
|
||||
if (item.position) {
|
||||
insertIndex = indexToInsertByPosition(positionedTemplate, item.position);
|
||||
} else {
|
||||
|
||||
/* If no |position| is specified, insert after last item. */
|
||||
insertIndex++;
|
||||
}
|
||||
positionedTemplate.splice(insertIndex, 0, item);
|
||||
}
|
||||
menu = new Menu;
|
||||
for (k = 0, len1 = positionedTemplate.length; k < len1; k++) {
|
||||
item = positionedTemplate[k];
|
||||
if (typeof item !== 'object') {
|
||||
throw new TypeError('Invalid template for MenuItem');
|
||||
}
|
||||
menuItem = new MenuItem(item);
|
||||
for (key in item) {
|
||||
value = item[key];
|
||||
if (menuItem[key] == null) {
|
||||
menuItem[key] = value;
|
||||
}
|
||||
}
|
||||
menu.append(menuItem);
|
||||
}
|
||||
return menu;
|
||||
};
|
||||
|
||||
module.exports = Menu;
|
|
@ -1,122 +0,0 @@
|
|||
{ipcMain} = require 'electron'
|
||||
|
||||
# The history operation in renderer is redirected to browser.
|
||||
ipcMain.on 'ATOM_SHELL_NAVIGATION_CONTROLLER', (event, method, args...) ->
|
||||
event.sender[method] args...
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', (event, method, args...) ->
|
||||
event.returnValue = event.sender[method] args...
|
||||
|
||||
# JavaScript implementation of Chromium's NavigationController.
|
||||
# Instead of relying on Chromium for history control, we compeletely do history
|
||||
# control on user land, and only rely on WebContents.loadURL for navigation.
|
||||
# This helps us avoid Chromium's various optimizations so we can ensure renderer
|
||||
# process is restarted everytime.
|
||||
class NavigationController
|
||||
constructor: (@webContents) ->
|
||||
@clearHistory()
|
||||
|
||||
# webContents may have already navigated to a page.
|
||||
if @webContents._getURL()
|
||||
@currentIndex++
|
||||
@history.push @webContents._getURL()
|
||||
|
||||
@webContents.on 'navigation-entry-commited', (event, url, inPage, replaceEntry) =>
|
||||
if @inPageIndex > -1 and not inPage
|
||||
# Navigated to a new page, clear in-page mark.
|
||||
@inPageIndex = -1
|
||||
else if @inPageIndex is -1 and inPage
|
||||
# Started in-page navigations.
|
||||
@inPageIndex = @currentIndex
|
||||
|
||||
if @pendingIndex >= 0 # Go to index.
|
||||
@currentIndex = @pendingIndex
|
||||
@pendingIndex = -1
|
||||
@history[@currentIndex] = url
|
||||
else if replaceEntry # Non-user initialized navigation.
|
||||
@history[@currentIndex] = url
|
||||
else # Normal navigation.
|
||||
@history = @history.slice 0, @currentIndex + 1 # Clear history.
|
||||
currentEntry = @history[@currentIndex]
|
||||
if currentEntry?.url isnt url
|
||||
@currentIndex++
|
||||
@history.push url
|
||||
|
||||
loadURL: (url, options={}) ->
|
||||
@pendingIndex = -1
|
||||
@webContents._loadURL url, options
|
||||
@webContents.emit 'load-url', url, options
|
||||
|
||||
getURL: ->
|
||||
if @currentIndex is -1
|
||||
''
|
||||
else
|
||||
@history[@currentIndex]
|
||||
|
||||
stop: ->
|
||||
@pendingIndex = -1
|
||||
@webContents._stop()
|
||||
|
||||
reload: ->
|
||||
@pendingIndex = @currentIndex
|
||||
@webContents._loadURL @getURL(), {}
|
||||
|
||||
reloadIgnoringCache: ->
|
||||
@pendingIndex = @currentIndex
|
||||
@webContents._loadURL @getURL(), {extraHeaders: "pragma: no-cache\n"}
|
||||
|
||||
canGoBack: ->
|
||||
@getActiveIndex() > 0
|
||||
|
||||
canGoForward: ->
|
||||
@getActiveIndex() < @history.length - 1
|
||||
|
||||
canGoToIndex: (index) ->
|
||||
index >=0 and index < @history.length
|
||||
|
||||
canGoToOffset: (offset) ->
|
||||
@canGoToIndex @currentIndex + offset
|
||||
|
||||
clearHistory: ->
|
||||
@history = []
|
||||
@currentIndex = -1
|
||||
@pendingIndex = -1
|
||||
@inPageIndex = -1
|
||||
|
||||
goBack: ->
|
||||
return unless @canGoBack()
|
||||
@pendingIndex = @getActiveIndex() - 1
|
||||
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
|
||||
@webContents._goBack()
|
||||
else
|
||||
@webContents._loadURL @history[@pendingIndex], {}
|
||||
|
||||
goForward: ->
|
||||
return unless @canGoForward()
|
||||
@pendingIndex = @getActiveIndex() + 1
|
||||
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
|
||||
@webContents._goForward()
|
||||
else
|
||||
@webContents._loadURL @history[@pendingIndex], {}
|
||||
|
||||
goToIndex: (index) ->
|
||||
return unless @canGoToIndex index
|
||||
@pendingIndex = index
|
||||
@webContents._loadURL @history[@pendingIndex], {}
|
||||
|
||||
goToOffset: (offset) ->
|
||||
return unless @canGoToOffset offset
|
||||
pendingIndex = @currentIndex + offset
|
||||
if @inPageIndex > -1 and pendingIndex >= @inPageIndex
|
||||
@pendingIndex = pendingIndex
|
||||
@webContents._goToOffset offset
|
||||
else
|
||||
@goToIndex pendingIndex
|
||||
|
||||
getActiveIndex: ->
|
||||
if @pendingIndex is -1 then @currentIndex else @pendingIndex
|
||||
|
||||
length: ->
|
||||
@history.length
|
||||
|
||||
module.exports = NavigationController
|
195
atom/browser/api/lib/navigation-controller.js
Normal file
195
atom/browser/api/lib/navigation-controller.js
Normal file
|
@ -0,0 +1,195 @@
|
|||
var NavigationController, ipcMain,
|
||||
slice = [].slice;
|
||||
|
||||
ipcMain = require('electron').ipcMain;
|
||||
|
||||
|
||||
/* The history operation in renderer is redirected to browser. */
|
||||
|
||||
ipcMain.on('ATOM_SHELL_NAVIGATION_CONTROLLER', function() {
|
||||
var args, event, method, ref;
|
||||
event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
return (ref = event.sender)[method].apply(ref, args);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', function() {
|
||||
var args, event, method, ref;
|
||||
event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
return event.returnValue = (ref = event.sender)[method].apply(ref, args);
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
JavaScript implementation of Chromium's NavigationController.
|
||||
Instead of relying on Chromium for history control, we compeletely do history
|
||||
control on user land, and only rely on WebContents.loadURL for navigation.
|
||||
This helps us avoid Chromium's various optimizations so we can ensure renderer
|
||||
process is restarted everytime.
|
||||
*/
|
||||
|
||||
NavigationController = (function() {
|
||||
function NavigationController(webContents) {
|
||||
this.webContents = webContents;
|
||||
this.clearHistory();
|
||||
|
||||
/* webContents may have already navigated to a page. */
|
||||
if (this.webContents._getURL()) {
|
||||
this.currentIndex++;
|
||||
this.history.push(this.webContents._getURL());
|
||||
}
|
||||
this.webContents.on('navigation-entry-commited', (function(_this) {
|
||||
return function(event, url, inPage, replaceEntry) {
|
||||
var currentEntry;
|
||||
if (_this.inPageIndex > -1 && !inPage) {
|
||||
|
||||
/* Navigated to a new page, clear in-page mark. */
|
||||
_this.inPageIndex = -1;
|
||||
} else if (_this.inPageIndex === -1 && inPage) {
|
||||
|
||||
/* Started in-page navigations. */
|
||||
_this.inPageIndex = _this.currentIndex;
|
||||
}
|
||||
if (_this.pendingIndex >= 0) {
|
||||
|
||||
/* Go to index. */
|
||||
_this.currentIndex = _this.pendingIndex;
|
||||
_this.pendingIndex = -1;
|
||||
return _this.history[_this.currentIndex] = url;
|
||||
} else if (replaceEntry) {
|
||||
|
||||
/* Non-user initialized navigation. */
|
||||
return _this.history[_this.currentIndex] = url;
|
||||
} else {
|
||||
|
||||
/* Normal navigation. Clear history. */
|
||||
_this.history = _this.history.slice(0, _this.currentIndex + 1);
|
||||
currentEntry = _this.history[_this.currentIndex];
|
||||
if ((currentEntry != null ? currentEntry.url : void 0) !== url) {
|
||||
_this.currentIndex++;
|
||||
return _this.history.push(url);
|
||||
}
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
|
||||
NavigationController.prototype.loadURL = function(url, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
this.pendingIndex = -1;
|
||||
this.webContents._loadURL(url, options);
|
||||
return this.webContents.emit('load-url', url, options);
|
||||
};
|
||||
|
||||
NavigationController.prototype.getURL = function() {
|
||||
if (this.currentIndex === -1) {
|
||||
return '';
|
||||
} else {
|
||||
return this.history[this.currentIndex];
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.stop = function() {
|
||||
this.pendingIndex = -1;
|
||||
return this.webContents._stop();
|
||||
};
|
||||
|
||||
NavigationController.prototype.reload = function() {
|
||||
this.pendingIndex = this.currentIndex;
|
||||
return this.webContents._loadURL(this.getURL(), {});
|
||||
};
|
||||
|
||||
NavigationController.prototype.reloadIgnoringCache = function() {
|
||||
this.pendingIndex = this.currentIndex;
|
||||
return this.webContents._loadURL(this.getURL(), {
|
||||
extraHeaders: "pragma: no-cache\n"
|
||||
});
|
||||
};
|
||||
|
||||
NavigationController.prototype.canGoBack = function() {
|
||||
return this.getActiveIndex() > 0;
|
||||
};
|
||||
|
||||
NavigationController.prototype.canGoForward = function() {
|
||||
return this.getActiveIndex() < this.history.length - 1;
|
||||
};
|
||||
|
||||
NavigationController.prototype.canGoToIndex = function(index) {
|
||||
return index >= 0 && index < this.history.length;
|
||||
};
|
||||
|
||||
NavigationController.prototype.canGoToOffset = function(offset) {
|
||||
return this.canGoToIndex(this.currentIndex + offset);
|
||||
};
|
||||
|
||||
NavigationController.prototype.clearHistory = function() {
|
||||
this.history = [];
|
||||
this.currentIndex = -1;
|
||||
this.pendingIndex = -1;
|
||||
return this.inPageIndex = -1;
|
||||
};
|
||||
|
||||
NavigationController.prototype.goBack = function() {
|
||||
if (!this.canGoBack()) {
|
||||
return;
|
||||
}
|
||||
this.pendingIndex = this.getActiveIndex() - 1;
|
||||
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
|
||||
return this.webContents._goBack();
|
||||
} else {
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.goForward = function() {
|
||||
if (!this.canGoForward()) {
|
||||
return;
|
||||
}
|
||||
this.pendingIndex = this.getActiveIndex() + 1;
|
||||
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
|
||||
return this.webContents._goForward();
|
||||
} else {
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.goToIndex = function(index) {
|
||||
if (!this.canGoToIndex(index)) {
|
||||
return;
|
||||
}
|
||||
this.pendingIndex = index;
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
};
|
||||
|
||||
NavigationController.prototype.goToOffset = function(offset) {
|
||||
var pendingIndex;
|
||||
if (!this.canGoToOffset(offset)) {
|
||||
return;
|
||||
}
|
||||
pendingIndex = this.currentIndex + offset;
|
||||
if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
|
||||
this.pendingIndex = pendingIndex;
|
||||
return this.webContents._goToOffset(offset);
|
||||
} else {
|
||||
return this.goToIndex(pendingIndex);
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.getActiveIndex = function() {
|
||||
if (this.pendingIndex === -1) {
|
||||
return this.currentIndex;
|
||||
} else {
|
||||
return this.pendingIndex;
|
||||
}
|
||||
};
|
||||
|
||||
NavigationController.prototype.length = function() {
|
||||
return this.history.length;
|
||||
};
|
||||
|
||||
return NavigationController;
|
||||
|
||||
})();
|
||||
|
||||
module.exports = NavigationController;
|
|
@ -1,7 +0,0 @@
|
|||
{EventEmitter} = require 'events'
|
||||
|
||||
{powerMonitor} = process.atomBinding 'power_monitor'
|
||||
|
||||
powerMonitor.__proto__ = EventEmitter.prototype
|
||||
|
||||
module.exports = powerMonitor
|
9
atom/browser/api/lib/power-monitor.js
Normal file
9
atom/browser/api/lib/power-monitor.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
var EventEmitter, powerMonitor;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
powerMonitor = process.atomBinding('power_monitor').powerMonitor;
|
||||
|
||||
powerMonitor.__proto__ = EventEmitter.prototype;
|
||||
|
||||
module.exports = powerMonitor;
|
|
@ -1,3 +0,0 @@
|
|||
{powerSaveBlocker} = process.atomBinding 'power_save_blocker'
|
||||
|
||||
module.exports = powerSaveBlocker
|
5
atom/browser/api/lib/power-save-blocker.js
Normal file
5
atom/browser/api/lib/power-save-blocker.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
var powerSaveBlocker;
|
||||
|
||||
powerSaveBlocker = process.atomBinding('power_save_blocker').powerSaveBlocker;
|
||||
|
||||
module.exports = powerSaveBlocker;
|
|
@ -1,25 +0,0 @@
|
|||
{app} = require 'electron'
|
||||
|
||||
throw new Error('Can not initialize protocol module before app is ready') unless app.isReady()
|
||||
|
||||
{protocol} = process.atomBinding 'protocol'
|
||||
|
||||
# Warn about removed APIs.
|
||||
logAndThrow = (callback, message) ->
|
||||
console.error message
|
||||
if callback then callback(new Error(message)) else throw new Error(message)
|
||||
protocol.registerProtocol = (scheme, handler, callback) ->
|
||||
logAndThrow callback,
|
||||
'registerProtocol API has been replaced by the
|
||||
register[File/Http/Buffer/String]Protocol API family, please
|
||||
switch to the new APIs.'
|
||||
protocol.isHandledProtocol = (scheme, callback) ->
|
||||
logAndThrow callback,
|
||||
'isHandledProtocol API has been replaced by isProtocolHandled.'
|
||||
protocol.interceptProtocol = (scheme, handler, callback) ->
|
||||
logAndThrow callback,
|
||||
'interceptProtocol API has been replaced by the
|
||||
intercept[File/Http/Buffer/String]Protocol API family, please
|
||||
switch to the new APIs.'
|
||||
|
||||
module.exports = protocol
|
35
atom/browser/api/lib/protocol.js
Normal file
35
atom/browser/api/lib/protocol.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
var app, logAndThrow, protocol;
|
||||
|
||||
app = require('electron').app;
|
||||
|
||||
if (!app.isReady()) {
|
||||
throw new Error('Can not initialize protocol module before app is ready');
|
||||
}
|
||||
|
||||
protocol = process.atomBinding('protocol').protocol;
|
||||
|
||||
|
||||
/* Warn about removed APIs. */
|
||||
|
||||
logAndThrow = function(callback, message) {
|
||||
console.error(message);
|
||||
if (callback) {
|
||||
return callback(new Error(message));
|
||||
} else {
|
||||
throw new Error(message);
|
||||
}
|
||||
};
|
||||
|
||||
protocol.registerProtocol = function(scheme, handler, callback) {
|
||||
return logAndThrow(callback, 'registerProtocol API has been replaced by the register[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.');
|
||||
};
|
||||
|
||||
protocol.isHandledProtocol = function(scheme, callback) {
|
||||
return logAndThrow(callback, 'isHandledProtocol API has been replaced by isProtocolHandled.');
|
||||
};
|
||||
|
||||
protocol.interceptProtocol = function(scheme, handler, callback) {
|
||||
return logAndThrow(callback, 'interceptProtocol API has been replaced by the intercept[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.');
|
||||
};
|
||||
|
||||
module.exports = protocol;
|
|
@ -1,6 +0,0 @@
|
|||
{EventEmitter} = require 'events'
|
||||
{screen} = process.atomBinding 'screen'
|
||||
|
||||
screen.__proto__ = EventEmitter.prototype
|
||||
|
||||
module.exports = screen
|
9
atom/browser/api/lib/screen.js
Normal file
9
atom/browser/api/lib/screen.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
var EventEmitter, screen;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
screen = process.atomBinding('screen').screen;
|
||||
|
||||
screen.__proto__ = EventEmitter.prototype;
|
||||
|
||||
module.exports = screen;
|
|
@ -1,24 +0,0 @@
|
|||
{EventEmitter} = require 'events'
|
||||
|
||||
bindings = process.atomBinding 'session'
|
||||
|
||||
PERSIST_PERFIX = 'persist:'
|
||||
|
||||
# Returns the Session from |partition| string.
|
||||
exports.fromPartition = (partition='') ->
|
||||
return exports.defaultSession if partition is ''
|
||||
if partition.startsWith PERSIST_PERFIX
|
||||
bindings.fromPartition partition.substr(PERSIST_PERFIX.length), false
|
||||
else
|
||||
bindings.fromPartition partition, true
|
||||
|
||||
# Returns the default session.
|
||||
Object.defineProperty exports, 'defaultSession',
|
||||
enumerable: true
|
||||
get: -> bindings.fromPartition '', false
|
||||
|
||||
wrapSession = (session) ->
|
||||
# session is an EventEmitter.
|
||||
session.__proto__ = EventEmitter.prototype
|
||||
|
||||
bindings._setWrapSession wrapSession
|
42
atom/browser/api/lib/session.js
Normal file
42
atom/browser/api/lib/session.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
var EventEmitter, PERSIST_PERFIX, bindings, wrapSession;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
bindings = process.atomBinding('session');
|
||||
|
||||
PERSIST_PERFIX = 'persist:';
|
||||
|
||||
|
||||
/* Returns the Session from |partition| string. */
|
||||
|
||||
exports.fromPartition = function(partition) {
|
||||
if (partition == null) {
|
||||
partition = '';
|
||||
}
|
||||
if (partition === '') {
|
||||
return exports.defaultSession;
|
||||
}
|
||||
if (partition.startsWith(PERSIST_PERFIX)) {
|
||||
return bindings.fromPartition(partition.substr(PERSIST_PERFIX.length), false);
|
||||
} else {
|
||||
return bindings.fromPartition(partition, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Returns the default session. */
|
||||
|
||||
Object.defineProperty(exports, 'defaultSession', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return bindings.fromPartition('', false);
|
||||
}
|
||||
});
|
||||
|
||||
wrapSession = function(session) {
|
||||
|
||||
/* session is an EventEmitter. */
|
||||
return session.__proto__ = EventEmitter.prototype;
|
||||
};
|
||||
|
||||
bindings._setWrapSession(wrapSession);
|
|
@ -1,19 +0,0 @@
|
|||
{deprecate} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
{Tray} = process.atomBinding 'tray'
|
||||
Tray::__proto__ = EventEmitter.prototype
|
||||
|
||||
Tray::_init = ->
|
||||
# Deprecated.
|
||||
deprecate.rename this, 'popContextMenu', 'popUpContextMenu'
|
||||
deprecate.event this, 'clicked', 'click'
|
||||
deprecate.event this, 'double-clicked', 'double-click'
|
||||
deprecate.event this, 'right-clicked', 'right-click'
|
||||
deprecate.event this, 'balloon-clicked', 'balloon-click'
|
||||
|
||||
Tray::setContextMenu = (menu) ->
|
||||
@_setContextMenu menu
|
||||
@menu = menu # Keep a strong reference of menu.
|
||||
|
||||
module.exports = Tray
|
28
atom/browser/api/lib/tray.js
Normal file
28
atom/browser/api/lib/tray.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
var EventEmitter, Tray, deprecate;
|
||||
|
||||
deprecate = require('electron').deprecate;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
Tray = process.atomBinding('tray').Tray;
|
||||
|
||||
Tray.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
Tray.prototype._init = function() {
|
||||
|
||||
/* Deprecated. */
|
||||
deprecate.rename(this, 'popContextMenu', 'popUpContextMenu');
|
||||
deprecate.event(this, 'clicked', 'click');
|
||||
deprecate.event(this, 'double-clicked', 'double-click');
|
||||
deprecate.event(this, 'right-clicked', 'right-click');
|
||||
return deprecate.event(this, 'balloon-clicked', 'balloon-click');
|
||||
};
|
||||
|
||||
Tray.prototype.setContextMenu = function(menu) {
|
||||
this._setContextMenu(menu);
|
||||
|
||||
/* Keep a strong reference of menu. */
|
||||
return this.menu = menu;
|
||||
};
|
||||
|
||||
module.exports = Tray;
|
|
@ -1,137 +0,0 @@
|
|||
{EventEmitter} = require 'events'
|
||||
{deprecate, ipcMain, session, NavigationController, Menu} = require 'electron'
|
||||
|
||||
binding = process.atomBinding 'web_contents'
|
||||
|
||||
nextId = 0
|
||||
getNextId = -> ++nextId
|
||||
|
||||
PDFPageSize =
|
||||
A5:
|
||||
custom_display_name: "A5"
|
||||
height_microns: 210000
|
||||
name: "ISO_A5"
|
||||
width_microns: 148000
|
||||
A4:
|
||||
custom_display_name: "A4"
|
||||
height_microns: 297000
|
||||
name: "ISO_A4"
|
||||
is_default: "true"
|
||||
width_microns: 210000
|
||||
A3:
|
||||
custom_display_name: "A3"
|
||||
height_microns: 420000
|
||||
name: "ISO_A3"
|
||||
width_microns: 297000
|
||||
Legal:
|
||||
custom_display_name: "Legal"
|
||||
height_microns: 355600
|
||||
name: "NA_LEGAL"
|
||||
width_microns: 215900
|
||||
Letter:
|
||||
custom_display_name: "Letter"
|
||||
height_microns: 279400
|
||||
name: "NA_LETTER"
|
||||
width_microns: 215900
|
||||
Tabloid:
|
||||
height_microns: 431800
|
||||
name: "NA_LEDGER"
|
||||
width_microns: 279400
|
||||
custom_display_name: "Tabloid"
|
||||
|
||||
wrapWebContents = (webContents) ->
|
||||
# webContents is an EventEmitter.
|
||||
webContents.__proto__ = EventEmitter.prototype
|
||||
|
||||
# WebContents::send(channel, args..)
|
||||
webContents.send = (channel, args...) ->
|
||||
@_send channel, [args...]
|
||||
|
||||
# Make sure webContents.executeJavaScript would run the code only when the
|
||||
# web contents has been loaded.
|
||||
webContents.executeJavaScript = (code, hasUserGesture=false) ->
|
||||
if @getURL() and not @isLoading()
|
||||
@_executeJavaScript code, hasUserGesture
|
||||
else
|
||||
webContents.once 'did-finish-load', @_executeJavaScript.bind(this, code, hasUserGesture)
|
||||
|
||||
# The navigation controller.
|
||||
controller = new NavigationController(webContents)
|
||||
for name, method of NavigationController.prototype when method instanceof Function
|
||||
do (name, method) ->
|
||||
webContents[name] = -> method.apply controller, arguments
|
||||
|
||||
# Dispatch IPC messages to the ipc module.
|
||||
webContents.on 'ipc-message', (event, packed) ->
|
||||
[channel, args...] = packed
|
||||
ipcMain.emit channel, event, args...
|
||||
webContents.on 'ipc-message-sync', (event, packed) ->
|
||||
[channel, args...] = packed
|
||||
Object.defineProperty event, 'returnValue', set: (value) -> event.sendReply JSON.stringify(value)
|
||||
ipcMain.emit channel, event, args...
|
||||
|
||||
# Handle context menu action request from pepper plugin.
|
||||
webContents.on 'pepper-context-menu', (event, params) ->
|
||||
menu = Menu.buildFromTemplate params.menu
|
||||
menu.popup params.x, params.y
|
||||
|
||||
# This error occurs when host could not be found.
|
||||
webContents.on 'did-fail-provisional-load', (args...) ->
|
||||
# Calling loadURL during this event might cause crash, so delay the event
|
||||
# until next tick.
|
||||
setImmediate => @emit 'did-fail-load', args...
|
||||
|
||||
# Delays the page-title-updated event to next tick.
|
||||
webContents.on '-page-title-updated', (args...) ->
|
||||
setImmediate => @emit 'page-title-updated', args...
|
||||
|
||||
# Deprecated.
|
||||
deprecate.rename webContents, 'loadUrl', 'loadURL'
|
||||
deprecate.rename webContents, 'getUrl', 'getURL'
|
||||
deprecate.event webContents, 'page-title-set', 'page-title-updated', (args...) ->
|
||||
@emit 'page-title-set', args...
|
||||
|
||||
webContents.printToPDF = (options, callback) ->
|
||||
printingSetting =
|
||||
pageRage: []
|
||||
mediaSize: {}
|
||||
landscape: false
|
||||
color: 2
|
||||
headerFooterEnabled: false
|
||||
marginsType: 0
|
||||
isFirstRequest: false
|
||||
requestID: getNextId()
|
||||
previewModifiable: true
|
||||
printToPDF: true
|
||||
printWithCloudPrint: false
|
||||
printWithPrivet: false
|
||||
printWithExtension: false
|
||||
deviceName: "Save as PDF"
|
||||
generateDraftData: true
|
||||
fitToPageEnabled: false
|
||||
duplex: 0
|
||||
copies: 1
|
||||
collate: true
|
||||
shouldPrintBackgrounds: false
|
||||
shouldPrintSelectionOnly: false
|
||||
|
||||
if options.landscape
|
||||
printingSetting.landscape = options.landscape
|
||||
if options.marginsType
|
||||
printingSetting.marginsType = options.marginsType
|
||||
if options.printSelectionOnly
|
||||
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
|
||||
if options.printBackground
|
||||
printingSetting.shouldPrintBackgrounds = options.printBackground
|
||||
|
||||
if options.pageSize and PDFPageSize[options.pageSize]
|
||||
printingSetting.mediaSize = PDFPageSize[options.pageSize]
|
||||
else
|
||||
printingSetting.mediaSize = PDFPageSize['A4']
|
||||
|
||||
@_printToPDF printingSetting, callback
|
||||
|
||||
binding._setWrapWebContents wrapWebContents
|
||||
|
||||
module.exports.create = (options={}) ->
|
||||
binding.create(options)
|
210
atom/browser/api/lib/web-contents.js
Normal file
210
atom/browser/api/lib/web-contents.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
var EventEmitter, Menu, NavigationController, PDFPageSize, binding, deprecate, getNextId, ipcMain, nextId, ref, session, wrapWebContents,
|
||||
slice = [].slice;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
ref = require('electron'), deprecate = ref.deprecate, ipcMain = ref.ipcMain, session = ref.session, NavigationController = ref.NavigationController, Menu = ref.Menu;
|
||||
|
||||
binding = process.atomBinding('web_contents');
|
||||
|
||||
nextId = 0;
|
||||
|
||||
getNextId = function() {
|
||||
return ++nextId;
|
||||
};
|
||||
|
||||
PDFPageSize = {
|
||||
A5: {
|
||||
custom_display_name: "A5",
|
||||
height_microns: 210000,
|
||||
name: "ISO_A5",
|
||||
width_microns: 148000
|
||||
},
|
||||
A4: {
|
||||
custom_display_name: "A4",
|
||||
height_microns: 297000,
|
||||
name: "ISO_A4",
|
||||
is_default: "true",
|
||||
width_microns: 210000
|
||||
},
|
||||
A3: {
|
||||
custom_display_name: "A3",
|
||||
height_microns: 420000,
|
||||
name: "ISO_A3",
|
||||
width_microns: 297000
|
||||
},
|
||||
Legal: {
|
||||
custom_display_name: "Legal",
|
||||
height_microns: 355600,
|
||||
name: "NA_LEGAL",
|
||||
width_microns: 215900
|
||||
},
|
||||
Letter: {
|
||||
custom_display_name: "Letter",
|
||||
height_microns: 279400,
|
||||
name: "NA_LETTER",
|
||||
width_microns: 215900
|
||||
},
|
||||
Tabloid: {
|
||||
height_microns: 431800,
|
||||
name: "NA_LEDGER",
|
||||
width_microns: 279400,
|
||||
custom_display_name: "Tabloid"
|
||||
}
|
||||
};
|
||||
|
||||
wrapWebContents = function(webContents) {
|
||||
|
||||
/* webContents is an EventEmitter. */
|
||||
var controller, method, name, ref1;
|
||||
webContents.__proto__ = EventEmitter.prototype;
|
||||
|
||||
/* WebContents::send(channel, args..) */
|
||||
webContents.send = function() {
|
||||
var args, channel;
|
||||
channel = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
return this._send(channel, slice.call(args));
|
||||
};
|
||||
|
||||
/*
|
||||
Make sure webContents.executeJavaScript would run the code only when the
|
||||
web contents has been loaded.
|
||||
*/
|
||||
webContents.executeJavaScript = function(code, hasUserGesture) {
|
||||
if (hasUserGesture == null) {
|
||||
hasUserGesture = false;
|
||||
}
|
||||
if (this.getURL() && !this.isLoading()) {
|
||||
return this._executeJavaScript(code, hasUserGesture);
|
||||
} else {
|
||||
return webContents.once('did-finish-load', this._executeJavaScript.bind(this, code, hasUserGesture));
|
||||
}
|
||||
};
|
||||
|
||||
/* The navigation controller. */
|
||||
controller = new NavigationController(webContents);
|
||||
ref1 = NavigationController.prototype;
|
||||
for (name in ref1) {
|
||||
method = ref1[name];
|
||||
if (method instanceof Function) {
|
||||
(function(name, method) {
|
||||
return webContents[name] = function() {
|
||||
return method.apply(controller, arguments);
|
||||
};
|
||||
})(name, method);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispatch IPC messages to the ipc module. */
|
||||
webContents.on('ipc-message', function(event, packed) {
|
||||
var args, channel;
|
||||
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
|
||||
return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
|
||||
});
|
||||
webContents.on('ipc-message-sync', function(event, packed) {
|
||||
var args, channel;
|
||||
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
|
||||
Object.defineProperty(event, 'returnValue', {
|
||||
set: function(value) {
|
||||
return event.sendReply(JSON.stringify(value));
|
||||
}
|
||||
});
|
||||
return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
|
||||
});
|
||||
|
||||
/* Handle context menu action request from pepper plugin. */
|
||||
webContents.on('pepper-context-menu', function(event, params) {
|
||||
var menu;
|
||||
menu = Menu.buildFromTemplate(params.menu);
|
||||
return menu.popup(params.x, params.y);
|
||||
});
|
||||
|
||||
/* This error occurs when host could not be found. */
|
||||
webContents.on('did-fail-provisional-load', function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
|
||||
/*
|
||||
Calling loadURL during this event might cause crash, so delay the event
|
||||
until next tick.
|
||||
*/
|
||||
return setImmediate((function(_this) {
|
||||
return function() {
|
||||
return _this.emit.apply(_this, ['did-fail-load'].concat(slice.call(args)));
|
||||
};
|
||||
})(this));
|
||||
});
|
||||
|
||||
/* Delays the page-title-updated event to next tick. */
|
||||
webContents.on('-page-title-updated', function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return setImmediate((function(_this) {
|
||||
return function() {
|
||||
return _this.emit.apply(_this, ['page-title-updated'].concat(slice.call(args)));
|
||||
};
|
||||
})(this));
|
||||
});
|
||||
|
||||
/* Deprecated. */
|
||||
deprecate.rename(webContents, 'loadUrl', 'loadURL');
|
||||
deprecate.rename(webContents, 'getUrl', 'getURL');
|
||||
deprecate.event(webContents, 'page-title-set', 'page-title-updated', function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return this.emit.apply(this, ['page-title-set'].concat(slice.call(args)));
|
||||
});
|
||||
return webContents.printToPDF = function(options, callback) {
|
||||
var printingSetting;
|
||||
printingSetting = {
|
||||
pageRage: [],
|
||||
mediaSize: {},
|
||||
landscape: false,
|
||||
color: 2,
|
||||
headerFooterEnabled: false,
|
||||
marginsType: 0,
|
||||
isFirstRequest: false,
|
||||
requestID: getNextId(),
|
||||
previewModifiable: true,
|
||||
printToPDF: true,
|
||||
printWithCloudPrint: false,
|
||||
printWithPrivet: false,
|
||||
printWithExtension: false,
|
||||
deviceName: "Save as PDF",
|
||||
generateDraftData: true,
|
||||
fitToPageEnabled: false,
|
||||
duplex: 0,
|
||||
copies: 1,
|
||||
collate: true,
|
||||
shouldPrintBackgrounds: false,
|
||||
shouldPrintSelectionOnly: false
|
||||
};
|
||||
if (options.landscape) {
|
||||
printingSetting.landscape = options.landscape;
|
||||
}
|
||||
if (options.marginsType) {
|
||||
printingSetting.marginsType = options.marginsType;
|
||||
}
|
||||
if (options.printSelectionOnly) {
|
||||
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly;
|
||||
}
|
||||
if (options.printBackground) {
|
||||
printingSetting.shouldPrintBackgrounds = options.printBackground;
|
||||
}
|
||||
if (options.pageSize && PDFPageSize[options.pageSize]) {
|
||||
printingSetting.mediaSize = PDFPageSize[options.pageSize];
|
||||
} else {
|
||||
printingSetting.mediaSize = PDFPageSize['A4'];
|
||||
}
|
||||
return this._printToPDF(printingSetting, callback);
|
||||
};
|
||||
};
|
||||
|
||||
binding._setWrapWebContents(wrapWebContents);
|
||||
|
||||
module.exports.create = function(options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return binding.create(options);
|
||||
};
|
|
@ -1,96 +0,0 @@
|
|||
electron = require 'electron'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
url = require 'url'
|
||||
|
||||
# Mapping between hostname and file path.
|
||||
hostPathMap = {}
|
||||
hostPathMapNextKey = 0
|
||||
|
||||
getHostForPath = (path) ->
|
||||
key = "extension-#{++hostPathMapNextKey}"
|
||||
hostPathMap[key] = path
|
||||
key
|
||||
|
||||
getPathForHost = (host) ->
|
||||
hostPathMap[host]
|
||||
|
||||
# Cache extensionInfo.
|
||||
extensionInfoMap = {}
|
||||
|
||||
getExtensionInfoFromPath = (srcDirectory) ->
|
||||
manifest = JSON.parse fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
|
||||
unless extensionInfoMap[manifest.name]?
|
||||
# We can not use 'file://' directly because all resources in the extension
|
||||
# will be treated as relative to the root in Chrome.
|
||||
page = url.format
|
||||
protocol: 'chrome-extension'
|
||||
slashes: true
|
||||
hostname: getHostForPath srcDirectory
|
||||
pathname: manifest.devtools_page
|
||||
extensionInfoMap[manifest.name] =
|
||||
startPage: page
|
||||
name: manifest.name
|
||||
srcDirectory: srcDirectory
|
||||
exposeExperimentalAPIs: true
|
||||
extensionInfoMap[manifest.name]
|
||||
|
||||
# The loaded extensions cache and its persistent path.
|
||||
loadedExtensions = null
|
||||
loadedExtensionsPath = null
|
||||
|
||||
# Persistent loaded extensions.
|
||||
{app} = electron
|
||||
app.on 'will-quit', ->
|
||||
try
|
||||
loadedExtensions = Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key].srcDirectory
|
||||
try
|
||||
fs.mkdirSync path.dirname(loadedExtensionsPath)
|
||||
catch e
|
||||
fs.writeFileSync loadedExtensionsPath, JSON.stringify(loadedExtensions)
|
||||
catch e
|
||||
|
||||
# We can not use protocol or BrowserWindow until app is ready.
|
||||
app.once 'ready', ->
|
||||
{protocol, BrowserWindow} = electron
|
||||
|
||||
# Load persistented extensions.
|
||||
loadedExtensionsPath = path.join app.getPath('userData'), 'DevTools Extensions'
|
||||
|
||||
try
|
||||
loadedExtensions = JSON.parse fs.readFileSync(loadedExtensionsPath)
|
||||
loadedExtensions = [] unless Array.isArray loadedExtensions
|
||||
# Preheat the extensionInfo cache.
|
||||
getExtensionInfoFromPath srcDirectory for srcDirectory in loadedExtensions
|
||||
catch e
|
||||
|
||||
# The chrome-extension: can map a extension URL request to real file path.
|
||||
chromeExtensionHandler = (request, callback) ->
|
||||
parsed = url.parse request.url
|
||||
return callback() unless parsed.hostname and parsed.path?
|
||||
return callback() unless /extension-\d+/.test parsed.hostname
|
||||
|
||||
directory = getPathForHost parsed.hostname
|
||||
return callback() unless directory?
|
||||
callback path.join(directory, parsed.path)
|
||||
protocol.registerFileProtocol 'chrome-extension', chromeExtensionHandler, (error) ->
|
||||
console.error 'Unable to register chrome-extension protocol' if error
|
||||
|
||||
BrowserWindow::_loadDevToolsExtensions = (extensionInfoArray) ->
|
||||
@devToolsWebContents?.executeJavaScript "DevToolsAPI.addExtensions(#{JSON.stringify(extensionInfoArray)});"
|
||||
|
||||
BrowserWindow.addDevToolsExtension = (srcDirectory) ->
|
||||
extensionInfo = getExtensionInfoFromPath srcDirectory
|
||||
if extensionInfo
|
||||
window._loadDevToolsExtensions [extensionInfo] for window in BrowserWindow.getAllWindows()
|
||||
extensionInfo.name
|
||||
|
||||
BrowserWindow.removeDevToolsExtension = (name) ->
|
||||
delete extensionInfoMap[name]
|
||||
|
||||
# Load persistented extensions when devtools is opened.
|
||||
init = BrowserWindow::_init
|
||||
BrowserWindow::_init = ->
|
||||
init.call this
|
||||
@on 'devtools-opened', ->
|
||||
@_loadDevToolsExtensions Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key]
|
163
atom/browser/lib/chrome-extension.js
Normal file
163
atom/browser/lib/chrome-extension.js
Normal file
|
@ -0,0 +1,163 @@
|
|||
var app, electron, extensionInfoMap, fs, getExtensionInfoFromPath, getHostForPath, getPathForHost, hostPathMap, hostPathMapNextKey, loadedExtensions, loadedExtensionsPath, path, url;
|
||||
|
||||
electron = require('electron');
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
url = require('url');
|
||||
|
||||
|
||||
/* Mapping between hostname and file path. */
|
||||
|
||||
hostPathMap = {};
|
||||
|
||||
hostPathMapNextKey = 0;
|
||||
|
||||
getHostForPath = function(path) {
|
||||
var key;
|
||||
key = "extension-" + (++hostPathMapNextKey);
|
||||
hostPathMap[key] = path;
|
||||
return key;
|
||||
};
|
||||
|
||||
getPathForHost = function(host) {
|
||||
return hostPathMap[host];
|
||||
};
|
||||
|
||||
|
||||
/* Cache extensionInfo. */
|
||||
|
||||
extensionInfoMap = {};
|
||||
|
||||
getExtensionInfoFromPath = function(srcDirectory) {
|
||||
var manifest, page;
|
||||
manifest = JSON.parse(fs.readFileSync(path.join(srcDirectory, 'manifest.json')));
|
||||
if (extensionInfoMap[manifest.name] == null) {
|
||||
|
||||
/*
|
||||
We can not use 'file://' directly because all resources in the extension
|
||||
will be treated as relative to the root in Chrome.
|
||||
*/
|
||||
page = url.format({
|
||||
protocol: 'chrome-extension',
|
||||
slashes: true,
|
||||
hostname: getHostForPath(srcDirectory),
|
||||
pathname: manifest.devtools_page
|
||||
});
|
||||
extensionInfoMap[manifest.name] = {
|
||||
startPage: page,
|
||||
name: manifest.name,
|
||||
srcDirectory: srcDirectory,
|
||||
exposeExperimentalAPIs: true
|
||||
};
|
||||
return extensionInfoMap[manifest.name];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* The loaded extensions cache and its persistent path. */
|
||||
|
||||
loadedExtensions = null;
|
||||
|
||||
loadedExtensionsPath = null;
|
||||
|
||||
|
||||
/* Persistent loaded extensions. */
|
||||
|
||||
app = electron.app;
|
||||
|
||||
app.on('will-quit', function() {
|
||||
var e, error1, error2;
|
||||
try {
|
||||
loadedExtensions = Object.keys(extensionInfoMap).map(function(key) {
|
||||
return extensionInfoMap[key].srcDirectory;
|
||||
});
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(loadedExtensionsPath));
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
}
|
||||
return fs.writeFileSync(loadedExtensionsPath, JSON.stringify(loadedExtensions));
|
||||
} catch (error2) {
|
||||
e = error2;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/* We can not use protocol or BrowserWindow until app is ready. */
|
||||
|
||||
app.once('ready', function() {
|
||||
var BrowserWindow, chromeExtensionHandler, e, error1, i, init, len, protocol, srcDirectory;
|
||||
protocol = electron.protocol, BrowserWindow = electron.BrowserWindow;
|
||||
|
||||
/* Load persistented extensions. */
|
||||
loadedExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions');
|
||||
try {
|
||||
loadedExtensions = JSON.parse(fs.readFileSync(loadedExtensionsPath));
|
||||
if (!Array.isArray(loadedExtensions)) {
|
||||
loadedExtensions = [];
|
||||
}
|
||||
|
||||
/* Preheat the extensionInfo cache. */
|
||||
for (i = 0, len = loadedExtensions.length; i < len; i++) {
|
||||
srcDirectory = loadedExtensions[i];
|
||||
getExtensionInfoFromPath(srcDirectory);
|
||||
}
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
}
|
||||
|
||||
/* The chrome-extension: can map a extension URL request to real file path. */
|
||||
chromeExtensionHandler = function(request, callback) {
|
||||
var directory, parsed;
|
||||
parsed = url.parse(request.url);
|
||||
if (!(parsed.hostname && (parsed.path != null))) {
|
||||
return callback();
|
||||
}
|
||||
if (!/extension-\d+/.test(parsed.hostname)) {
|
||||
return callback();
|
||||
}
|
||||
directory = getPathForHost(parsed.hostname);
|
||||
if (directory == null) {
|
||||
return callback();
|
||||
}
|
||||
return callback(path.join(directory, parsed.path));
|
||||
};
|
||||
protocol.registerFileProtocol('chrome-extension', chromeExtensionHandler, function(error) {
|
||||
if (error) {
|
||||
return console.error('Unable to register chrome-extension protocol');
|
||||
}
|
||||
});
|
||||
BrowserWindow.prototype._loadDevToolsExtensions = function(extensionInfoArray) {
|
||||
var ref;
|
||||
return (ref = this.devToolsWebContents) != null ? ref.executeJavaScript("DevToolsAPI.addExtensions(" + (JSON.stringify(extensionInfoArray)) + ");") : void 0;
|
||||
};
|
||||
BrowserWindow.addDevToolsExtension = function(srcDirectory) {
|
||||
var extensionInfo, j, len1, ref, window;
|
||||
extensionInfo = getExtensionInfoFromPath(srcDirectory);
|
||||
if (extensionInfo) {
|
||||
ref = BrowserWindow.getAllWindows();
|
||||
for (j = 0, len1 = ref.length; j < len1; j++) {
|
||||
window = ref[j];
|
||||
window._loadDevToolsExtensions([extensionInfo]);
|
||||
}
|
||||
return extensionInfo.name;
|
||||
}
|
||||
};
|
||||
BrowserWindow.removeDevToolsExtension = function(name) {
|
||||
return delete extensionInfoMap[name];
|
||||
};
|
||||
|
||||
/* Load persistented extensions when devtools is opened. */
|
||||
init = BrowserWindow.prototype._init;
|
||||
return BrowserWindow.prototype._init = function() {
|
||||
init.call(this);
|
||||
return this.on('devtools-opened', function() {
|
||||
return this._loadDevToolsExtensions(Object.keys(extensionInfoMap).map(function(key) {
|
||||
return extensionInfoMap[key];
|
||||
}));
|
||||
});
|
||||
};
|
||||
});
|
|
@ -1,37 +0,0 @@
|
|||
{ipcMain} = require 'electron'
|
||||
{desktopCapturer} = process.atomBinding 'desktop_capturer'
|
||||
|
||||
deepEqual = (opt1, opt2) ->
|
||||
return JSON.stringify(opt1) is JSON.stringify(opt2)
|
||||
|
||||
# A queue for holding all requests from renderer process.
|
||||
requestsQueue = []
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, id) ->
|
||||
request = id: id, options: {captureWindow, captureScreen, thumbnailSize}, webContents: event.sender
|
||||
requestsQueue.push request
|
||||
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize if requestsQueue.length is 1
|
||||
# If the WebContents is destroyed before receiving result, just remove the
|
||||
# reference from requestsQueue to make the module not send the result to it.
|
||||
event.sender.once 'destroyed', ->
|
||||
request.webContents = null
|
||||
|
||||
desktopCapturer.emit = (event, name, sources) ->
|
||||
# Receiving sources result from main process, now send them back to renderer.
|
||||
handledRequest = requestsQueue.shift 0
|
||||
result = ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataUrl() } for source in sources)
|
||||
handledRequest.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{handledRequest.id}", result
|
||||
|
||||
# Check the queue to see whether there is other same request. If has, handle
|
||||
# it for reducing redunplicated `desktopCaptuer.startHandling` calls.
|
||||
unhandledRequestsQueue = []
|
||||
for request in requestsQueue
|
||||
if deepEqual handledRequest.options, request.options
|
||||
request.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{request.id}", errorMessage, result
|
||||
else
|
||||
unhandledRequestsQueue.push request
|
||||
requestsQueue = unhandledRequestsQueue
|
||||
# If the requestsQueue is not empty, start a new request handling.
|
||||
if requestsQueue.length > 0
|
||||
{captureWindow, captureScreen, thumbnailSize} = requestsQueue[0].options
|
||||
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize
|
85
atom/browser/lib/desktop-capturer.js
Normal file
85
atom/browser/lib/desktop-capturer.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
var deepEqual, desktopCapturer, ipcMain, requestsQueue;
|
||||
|
||||
ipcMain = require('electron').ipcMain;
|
||||
|
||||
desktopCapturer = process.atomBinding('desktop_capturer').desktopCapturer;
|
||||
|
||||
deepEqual = function(opt1, opt2) {
|
||||
return JSON.stringify(opt1) === JSON.stringify(opt2);
|
||||
};
|
||||
|
||||
|
||||
/* A queue for holding all requests from renderer process. */
|
||||
|
||||
requestsQueue = [];
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function(event, captureWindow, captureScreen, thumbnailSize, id) {
|
||||
var request;
|
||||
request = {
|
||||
id: id,
|
||||
options: {
|
||||
captureWindow: captureWindow,
|
||||
captureScreen: captureScreen,
|
||||
thumbnailSize: thumbnailSize
|
||||
},
|
||||
webContents: event.sender
|
||||
};
|
||||
requestsQueue.push(request);
|
||||
if (requestsQueue.length === 1) {
|
||||
desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize);
|
||||
}
|
||||
|
||||
/*
|
||||
If the WebContents is destroyed before receiving result, just remove the
|
||||
reference from requestsQueue to make the module not send the result to it.
|
||||
*/
|
||||
return event.sender.once('destroyed', function() {
|
||||
return request.webContents = null;
|
||||
});
|
||||
});
|
||||
|
||||
desktopCapturer.emit = function(event, name, sources) {
|
||||
|
||||
/* Receiving sources result from main process, now send them back to renderer. */
|
||||
var captureScreen, captureWindow, handledRequest, i, len, ref, ref1, ref2, request, result, source, thumbnailSize, unhandledRequestsQueue;
|
||||
handledRequest = requestsQueue.shift(0);
|
||||
result = (function() {
|
||||
var i, len, results;
|
||||
results = [];
|
||||
for (i = 0, len = sources.length; i < len; i++) {
|
||||
source = sources[i];
|
||||
results.push({
|
||||
id: source.id,
|
||||
name: source.name,
|
||||
thumbnail: source.thumbnail.toDataUrl()
|
||||
});
|
||||
}
|
||||
return results;
|
||||
})();
|
||||
if ((ref = handledRequest.webContents) != null) {
|
||||
ref.send("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + handledRequest.id, result);
|
||||
}
|
||||
|
||||
/*
|
||||
Check the queue to see whether there is other same request. If has, handle
|
||||
it for reducing redunplicated `desktopCaptuer.startHandling` calls.
|
||||
*/
|
||||
unhandledRequestsQueue = [];
|
||||
for (i = 0, len = requestsQueue.length; i < len; i++) {
|
||||
request = requestsQueue[i];
|
||||
if (deepEqual(handledRequest.options, request.options)) {
|
||||
if ((ref1 = request.webContents) != null) {
|
||||
ref1.send("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + request.id, errorMessage, result);
|
||||
}
|
||||
} else {
|
||||
unhandledRequestsQueue.push(request);
|
||||
}
|
||||
}
|
||||
requestsQueue = unhandledRequestsQueue;
|
||||
|
||||
/* If the requestsQueue is not empty, start a new request handling. */
|
||||
if (requestsQueue.length > 0) {
|
||||
ref2 = requestsQueue[0].options, captureWindow = ref2.captureWindow, captureScreen = ref2.captureScreen, thumbnailSize = ref2.thumbnailSize;
|
||||
return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize);
|
||||
}
|
||||
};
|
|
@ -1,174 +0,0 @@
|
|||
{ipcMain, webContents} = require 'electron'
|
||||
|
||||
webViewManager = null # Doesn't exist in early initialization.
|
||||
|
||||
supportedWebViewEvents = [
|
||||
'load-commit'
|
||||
'did-finish-load'
|
||||
'did-fail-load'
|
||||
'did-frame-finish-load'
|
||||
'did-start-loading'
|
||||
'did-stop-loading'
|
||||
'did-get-response-details'
|
||||
'did-get-redirect-request'
|
||||
'dom-ready'
|
||||
'console-message'
|
||||
'devtools-opened'
|
||||
'devtools-closed'
|
||||
'devtools-focused'
|
||||
'new-window'
|
||||
'will-navigate'
|
||||
'did-navigate'
|
||||
'did-navigate-in-page'
|
||||
'close'
|
||||
'crashed'
|
||||
'gpu-crashed'
|
||||
'plugin-crashed'
|
||||
'destroyed'
|
||||
'page-title-updated'
|
||||
'page-favicon-updated'
|
||||
'enter-html-full-screen'
|
||||
'leave-html-full-screen'
|
||||
'media-started-playing'
|
||||
'media-paused'
|
||||
'found-in-page'
|
||||
'did-change-theme-color'
|
||||
]
|
||||
|
||||
nextInstanceId = 0
|
||||
guestInstances = {}
|
||||
embedderElementsMap = {}
|
||||
reverseEmbedderElementsMap = {}
|
||||
|
||||
# Moves the last element of array to the first one.
|
||||
moveLastToFirst = (list) ->
|
||||
list.unshift list.pop()
|
||||
|
||||
# Generate guestInstanceId.
|
||||
getNextInstanceId = (webContents) ->
|
||||
++nextInstanceId
|
||||
|
||||
# Create a new guest instance.
|
||||
createGuest = (embedder, params) ->
|
||||
webViewManager ?= process.atomBinding 'web_view_manager'
|
||||
|
||||
id = getNextInstanceId embedder
|
||||
guest = webContents.create {isGuest: true, partition: params.partition, embedder}
|
||||
guestInstances[id] = {guest, embedder}
|
||||
|
||||
# Destroy guest when the embedder is gone or navigated.
|
||||
destroyEvents = ['destroyed', 'crashed', 'did-navigate']
|
||||
destroy = ->
|
||||
destroyGuest embedder, id if guestInstances[id]?
|
||||
for event in destroyEvents
|
||||
embedder.once event, destroy
|
||||
# Users might also listen to the crashed event, so We must ensure the guest
|
||||
# is destroyed before users' listener gets called. It is done by moving our
|
||||
# listener to the first one in queue.
|
||||
listeners = embedder._events[event]
|
||||
moveLastToFirst listeners if Array.isArray listeners
|
||||
guest.once 'destroyed', ->
|
||||
embedder.removeListener event, destroy for event in destroyEvents
|
||||
|
||||
# Init guest web view after attached.
|
||||
guest.once 'did-attach', ->
|
||||
params = @attachParams
|
||||
delete @attachParams
|
||||
|
||||
@viewInstanceId = params.instanceId
|
||||
@setSize
|
||||
normal:
|
||||
width: params.elementWidth, height: params.elementHeight
|
||||
enableAutoSize: params.autosize
|
||||
min:
|
||||
width: params.minwidth, height: params.minheight
|
||||
max:
|
||||
width: params.maxwidth, height: params.maxheight
|
||||
|
||||
if params.src
|
||||
opts = {}
|
||||
opts.httpReferrer = params.httpreferrer if params.httpreferrer
|
||||
opts.userAgent = params.useragent if params.useragent
|
||||
@loadURL params.src, opts
|
||||
|
||||
if params.allowtransparency?
|
||||
@setAllowTransparency params.allowtransparency
|
||||
|
||||
guest.allowPopups = params.allowpopups
|
||||
|
||||
# Dispatch events to embedder.
|
||||
for event in supportedWebViewEvents
|
||||
do (event) ->
|
||||
guest.on event, (_, args...) ->
|
||||
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{guest.viewInstanceId}", event, args...
|
||||
|
||||
# Dispatch guest's IPC messages to embedder.
|
||||
guest.on 'ipc-message-host', (_, packed) ->
|
||||
[channel, args...] = packed
|
||||
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{guest.viewInstanceId}", channel, args...
|
||||
|
||||
# Autosize.
|
||||
guest.on 'size-changed', (_, args...) ->
|
||||
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{guest.viewInstanceId}", args...
|
||||
|
||||
id
|
||||
|
||||
# Attach the guest to an element of embedder.
|
||||
attachGuest = (embedder, elementInstanceId, guestInstanceId, params) ->
|
||||
guest = guestInstances[guestInstanceId].guest
|
||||
|
||||
# Destroy the old guest when attaching.
|
||||
key = "#{embedder.getId()}-#{elementInstanceId}"
|
||||
oldGuestInstanceId = embedderElementsMap[key]
|
||||
if oldGuestInstanceId?
|
||||
# Reattachment to the same guest is not currently supported.
|
||||
return unless oldGuestInstanceId != guestInstanceId
|
||||
|
||||
return unless guestInstances[oldGuestInstanceId]?
|
||||
destroyGuest embedder, oldGuestInstanceId
|
||||
|
||||
webPreferences =
|
||||
guestInstanceId: guestInstanceId
|
||||
nodeIntegration: params.nodeintegration ? false
|
||||
plugins: params.plugins
|
||||
webSecurity: !params.disablewebsecurity
|
||||
webPreferences.preloadURL = params.preload if params.preload
|
||||
webViewManager.addGuest guestInstanceId, elementInstanceId, embedder, guest, webPreferences
|
||||
|
||||
guest.attachParams = params
|
||||
embedderElementsMap[key] = guestInstanceId
|
||||
reverseEmbedderElementsMap[guestInstanceId] = key
|
||||
|
||||
# Destroy an existing guest instance.
|
||||
destroyGuest = (embedder, id) ->
|
||||
webViewManager.removeGuest embedder, id
|
||||
guestInstances[id].guest.destroy()
|
||||
delete guestInstances[id]
|
||||
|
||||
key = reverseEmbedderElementsMap[id]
|
||||
if key?
|
||||
delete reverseEmbedderElementsMap[id]
|
||||
delete embedderElementsMap[key]
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', (event, params, requestId) ->
|
||||
event.sender.send "ATOM_SHELL_RESPONSE_#{requestId}", createGuest(event.sender, params)
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', (event, elementInstanceId, guestInstanceId, params) ->
|
||||
attachGuest event.sender, elementInstanceId, guestInstanceId, params
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', (event, id) ->
|
||||
destroyGuest event.sender, id
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', (event, id, params) ->
|
||||
guestInstances[id]?.guest.setSize params
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', (event, id, allowtransparency) ->
|
||||
guestInstances[id]?.guest.setAllowTransparency allowtransparency
|
||||
|
||||
# Returns WebContents from its guest id.
|
||||
exports.getGuest = (id) ->
|
||||
guestInstances[id]?.guest
|
||||
|
||||
# Returns the embedder of the guest.
|
||||
exports.getEmbedder = (id) ->
|
||||
guestInstances[id]?.embedder
|
238
atom/browser/lib/guest-view-manager.js
Normal file
238
atom/browser/lib/guest-view-manager.js
Normal file
|
@ -0,0 +1,238 @@
|
|||
var attachGuest, createGuest, destroyGuest, embedderElementsMap, getNextInstanceId, guestInstances, ipcMain, moveLastToFirst, nextInstanceId, ref, reverseEmbedderElementsMap, supportedWebViewEvents, webContents, webViewManager,
|
||||
slice = [].slice;
|
||||
|
||||
ref = require('electron'), ipcMain = ref.ipcMain, webContents = ref.webContents;
|
||||
|
||||
|
||||
/* Doesn't exist in early initialization. */
|
||||
|
||||
webViewManager = null;
|
||||
|
||||
supportedWebViewEvents = ['load-commit', 'did-finish-load', 'did-fail-load', 'did-frame-finish-load', 'did-start-loading', 'did-stop-loading', 'did-get-response-details', 'did-get-redirect-request', 'dom-ready', 'console-message', 'devtools-opened', 'devtools-closed', 'devtools-focused', 'new-window', 'will-navigate', 'did-navigate', 'did-navigate-in-page', 'close', 'crashed', 'gpu-crashed', 'plugin-crashed', 'destroyed', 'page-title-updated', 'page-favicon-updated', 'enter-html-full-screen', 'leave-html-full-screen', 'media-started-playing', 'media-paused', 'found-in-page', 'did-change-theme-color'];
|
||||
|
||||
nextInstanceId = 0;
|
||||
|
||||
guestInstances = {};
|
||||
|
||||
embedderElementsMap = {};
|
||||
|
||||
reverseEmbedderElementsMap = {};
|
||||
|
||||
|
||||
/* Moves the last element of array to the first one. */
|
||||
|
||||
moveLastToFirst = function(list) {
|
||||
return list.unshift(list.pop());
|
||||
};
|
||||
|
||||
|
||||
/* Generate guestInstanceId. */
|
||||
|
||||
getNextInstanceId = function(webContents) {
|
||||
return ++nextInstanceId;
|
||||
};
|
||||
|
||||
|
||||
/* Create a new guest instance. */
|
||||
|
||||
createGuest = function(embedder, params) {
|
||||
var destroy, destroyEvents, event, fn, guest, i, id, j, len, len1, listeners;
|
||||
if (webViewManager == null) {
|
||||
webViewManager = process.atomBinding('web_view_manager');
|
||||
}
|
||||
id = getNextInstanceId(embedder);
|
||||
guest = webContents.create({
|
||||
isGuest: true,
|
||||
partition: params.partition,
|
||||
embedder: embedder
|
||||
});
|
||||
guestInstances[id] = {
|
||||
guest: guest,
|
||||
embedder: embedder
|
||||
};
|
||||
|
||||
/* Destroy guest when the embedder is gone or navigated. */
|
||||
destroyEvents = ['destroyed', 'crashed', 'did-navigate'];
|
||||
destroy = function() {
|
||||
if (guestInstances[id] != null) {
|
||||
return destroyGuest(embedder, id);
|
||||
}
|
||||
};
|
||||
for (i = 0, len = destroyEvents.length; i < len; i++) {
|
||||
event = destroyEvents[i];
|
||||
embedder.once(event, destroy);
|
||||
|
||||
/*
|
||||
Users might also listen to the crashed event, so We must ensure the guest
|
||||
is destroyed before users' listener gets called. It is done by moving our
|
||||
listener to the first one in queue.
|
||||
*/
|
||||
listeners = embedder._events[event];
|
||||
if (Array.isArray(listeners)) {
|
||||
moveLastToFirst(listeners);
|
||||
}
|
||||
}
|
||||
guest.once('destroyed', function() {
|
||||
var j, len1, results;
|
||||
results = [];
|
||||
for (j = 0, len1 = destroyEvents.length; j < len1; j++) {
|
||||
event = destroyEvents[j];
|
||||
results.push(embedder.removeListener(event, destroy));
|
||||
}
|
||||
return results;
|
||||
});
|
||||
|
||||
/* Init guest web view after attached. */
|
||||
guest.once('did-attach', function() {
|
||||
var opts;
|
||||
params = this.attachParams;
|
||||
delete this.attachParams;
|
||||
this.viewInstanceId = params.instanceId;
|
||||
this.setSize({
|
||||
normal: {
|
||||
width: params.elementWidth,
|
||||
height: params.elementHeight
|
||||
},
|
||||
enableAutoSize: params.autosize,
|
||||
min: {
|
||||
width: params.minwidth,
|
||||
height: params.minheight
|
||||
},
|
||||
max: {
|
||||
width: params.maxwidth,
|
||||
height: params.maxheight
|
||||
}
|
||||
});
|
||||
if (params.src) {
|
||||
opts = {};
|
||||
if (params.httpreferrer) {
|
||||
opts.httpReferrer = params.httpreferrer;
|
||||
}
|
||||
if (params.useragent) {
|
||||
opts.userAgent = params.useragent;
|
||||
}
|
||||
this.loadURL(params.src, opts);
|
||||
}
|
||||
if (params.allowtransparency != null) {
|
||||
this.setAllowTransparency(params.allowtransparency);
|
||||
}
|
||||
return guest.allowPopups = params.allowpopups;
|
||||
});
|
||||
|
||||
/* Dispatch events to embedder. */
|
||||
fn = function(event) {
|
||||
return guest.on(event, function() {
|
||||
var _, args;
|
||||
_ = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + guest.viewInstanceId, event].concat(slice.call(args)));
|
||||
});
|
||||
};
|
||||
for (j = 0, len1 = supportedWebViewEvents.length; j < len1; j++) {
|
||||
event = supportedWebViewEvents[j];
|
||||
fn(event);
|
||||
}
|
||||
|
||||
/* Dispatch guest's IPC messages to embedder. */
|
||||
guest.on('ipc-message-host', function(_, packed) {
|
||||
var args, channel;
|
||||
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
|
||||
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + guest.viewInstanceId, channel].concat(slice.call(args)));
|
||||
});
|
||||
|
||||
/* Autosize. */
|
||||
guest.on('size-changed', function() {
|
||||
var _, args;
|
||||
_ = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + guest.viewInstanceId].concat(slice.call(args)));
|
||||
});
|
||||
return id;
|
||||
};
|
||||
|
||||
|
||||
/* Attach the guest to an element of embedder. */
|
||||
|
||||
attachGuest = function(embedder, elementInstanceId, guestInstanceId, params) {
|
||||
var guest, key, oldGuestInstanceId, ref1, webPreferences;
|
||||
guest = guestInstances[guestInstanceId].guest;
|
||||
|
||||
/* Destroy the old guest when attaching. */
|
||||
key = (embedder.getId()) + "-" + elementInstanceId;
|
||||
oldGuestInstanceId = embedderElementsMap[key];
|
||||
if (oldGuestInstanceId != null) {
|
||||
|
||||
/* Reattachment to the same guest is not currently supported. */
|
||||
if (oldGuestInstanceId === guestInstanceId) {
|
||||
return;
|
||||
}
|
||||
if (guestInstances[oldGuestInstanceId] == null) {
|
||||
return;
|
||||
}
|
||||
destroyGuest(embedder, oldGuestInstanceId);
|
||||
}
|
||||
webPreferences = {
|
||||
guestInstanceId: guestInstanceId,
|
||||
nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false,
|
||||
plugins: params.plugins,
|
||||
webSecurity: !params.disablewebsecurity
|
||||
};
|
||||
if (params.preload) {
|
||||
webPreferences.preloadURL = params.preload;
|
||||
}
|
||||
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences);
|
||||
guest.attachParams = params;
|
||||
embedderElementsMap[key] = guestInstanceId;
|
||||
return reverseEmbedderElementsMap[guestInstanceId] = key;
|
||||
};
|
||||
|
||||
|
||||
/* Destroy an existing guest instance. */
|
||||
|
||||
destroyGuest = function(embedder, id) {
|
||||
var key;
|
||||
webViewManager.removeGuest(embedder, id);
|
||||
guestInstances[id].guest.destroy();
|
||||
delete guestInstances[id];
|
||||
key = reverseEmbedderElementsMap[id];
|
||||
if (key != null) {
|
||||
delete reverseEmbedderElementsMap[id];
|
||||
return delete embedderElementsMap[key];
|
||||
}
|
||||
};
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', function(event, params, requestId) {
|
||||
return event.sender.send("ATOM_SHELL_RESPONSE_" + requestId, createGuest(event.sender, params));
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', function(event, elementInstanceId, guestInstanceId, params) {
|
||||
return attachGuest(event.sender, elementInstanceId, guestInstanceId, params);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', function(event, id) {
|
||||
return destroyGuest(event.sender, id);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', function(event, id, params) {
|
||||
var ref1;
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0;
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', function(event, id, allowtransparency) {
|
||||
var ref1;
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.guest.setAllowTransparency(allowtransparency) : void 0;
|
||||
});
|
||||
|
||||
|
||||
/* Returns WebContents from its guest id. */
|
||||
|
||||
exports.getGuest = function(id) {
|
||||
var ref1;
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.guest : void 0;
|
||||
};
|
||||
|
||||
|
||||
/* Returns the embedder of the guest. */
|
||||
|
||||
exports.getEmbedder = function(id) {
|
||||
var ref1;
|
||||
return (ref1 = guestInstances[id]) != null ? ref1.embedder : void 0;
|
||||
};
|
|
@ -1,86 +0,0 @@
|
|||
{ipcMain, BrowserWindow} = require 'electron'
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
frameToGuest = {}
|
||||
|
||||
# Copy attribute of |parent| to |child| if it is not defined in |child|.
|
||||
mergeOptions = (child, parent) ->
|
||||
for own key, value of parent when key not of child
|
||||
if typeof value is 'object'
|
||||
child[key] = mergeOptions {}, value
|
||||
else
|
||||
child[key] = value
|
||||
child
|
||||
|
||||
# Merge |options| with the |embedder|'s window's options.
|
||||
mergeBrowserWindowOptions = (embedder, options) ->
|
||||
if embedder.browserWindowOptions?
|
||||
# Inherit the original options if it is a BrowserWindow.
|
||||
mergeOptions options, embedder.browserWindowOptions
|
||||
else
|
||||
# Or only inherit web-preferences if it is a webview.
|
||||
options.webPreferences ?= {}
|
||||
mergeOptions options.webPreferences, embedder.getWebPreferences()
|
||||
options
|
||||
|
||||
# Create a new guest created by |embedder| with |options|.
|
||||
createGuest = (embedder, url, frameName, options) ->
|
||||
guest = frameToGuest[frameName]
|
||||
if frameName and guest?
|
||||
guest.loadURL url
|
||||
return guest.id
|
||||
|
||||
# Remember the embedder window's id.
|
||||
options.webPreferences ?= {}
|
||||
options.webPreferences.openerId = BrowserWindow.fromWebContents(embedder)?.id
|
||||
|
||||
guest = new BrowserWindow(options)
|
||||
guest.loadURL url
|
||||
|
||||
# When |embedder| is destroyed we should also destroy attached guest, and if
|
||||
# guest is closed by user then we should prevent |embedder| from double
|
||||
# closing guest.
|
||||
guestId = guest.id
|
||||
closedByEmbedder = ->
|
||||
guest.removeListener 'closed', closedByUser
|
||||
guest.destroy()
|
||||
closedByUser = ->
|
||||
embedder.send "ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_#{guestId}"
|
||||
embedder.removeListener 'render-view-deleted', closedByEmbedder
|
||||
embedder.once 'render-view-deleted', closedByEmbedder
|
||||
guest.once 'closed', closedByUser
|
||||
|
||||
if frameName
|
||||
frameToGuest[frameName] = guest
|
||||
guest.frameName = frameName
|
||||
guest.once 'closed', ->
|
||||
delete frameToGuest[frameName]
|
||||
|
||||
guest.id
|
||||
|
||||
# Routed window.open messages.
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, args...) ->
|
||||
[url, frameName, options] = args
|
||||
options = mergeBrowserWindowOptions event.sender, options
|
||||
event.sender.emit 'new-window', event, url, frameName, 'new-window', options
|
||||
if (event.sender.isGuest() and not event.sender.allowPopups) or event.defaultPrevented
|
||||
event.returnValue = null
|
||||
else
|
||||
event.returnValue = createGuest event.sender, url, frameName, options
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', (event, guestId) ->
|
||||
BrowserWindow.fromId(guestId)?.destroy()
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestId, method, args...) ->
|
||||
BrowserWindow.fromId(guestId)?[method] args...
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestId, message, targetOrigin, sourceOrigin) ->
|
||||
sourceId = BrowserWindow.fromWebContents(event.sender)?.id
|
||||
return unless sourceId?
|
||||
|
||||
guestContents = BrowserWindow.fromId(guestId)?.webContents
|
||||
if guestContents?.getURL().indexOf(targetOrigin) is 0 or targetOrigin is '*'
|
||||
guestContents?.send 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin
|
||||
|
||||
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestId, method, args...) ->
|
||||
BrowserWindow.fromId(guestId)?.webContents?[method] args...
|
137
atom/browser/lib/guest-window-manager.js
Normal file
137
atom/browser/lib/guest-window-manager.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
var BrowserWindow, createGuest, frameToGuest, ipcMain, mergeBrowserWindowOptions, mergeOptions, ref, v8Util,
|
||||
hasProp = {}.hasOwnProperty,
|
||||
slice = [].slice;
|
||||
|
||||
ref = require('electron'), ipcMain = ref.ipcMain, BrowserWindow = ref.BrowserWindow;
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
frameToGuest = {};
|
||||
|
||||
|
||||
/* Copy attribute of |parent| to |child| if it is not defined in |child|. */
|
||||
|
||||
mergeOptions = function(child, parent) {
|
||||
var key, value;
|
||||
for (key in parent) {
|
||||
if (!hasProp.call(parent, key)) continue;
|
||||
value = parent[key];
|
||||
if (!(key in child)) {
|
||||
if (typeof value === 'object') {
|
||||
child[key] = mergeOptions({}, value);
|
||||
} else {
|
||||
child[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return child;
|
||||
};
|
||||
|
||||
|
||||
/* Merge |options| with the |embedder|'s window's options. */
|
||||
|
||||
mergeBrowserWindowOptions = function(embedder, options) {
|
||||
if (embedder.browserWindowOptions != null) {
|
||||
|
||||
/* Inherit the original options if it is a BrowserWindow. */
|
||||
mergeOptions(options, embedder.browserWindowOptions);
|
||||
} else {
|
||||
|
||||
/* Or only inherit web-preferences if it is a webview. */
|
||||
if (options.webPreferences == null) {
|
||||
options.webPreferences = {};
|
||||
}
|
||||
mergeOptions(options.webPreferences, embedder.getWebPreferences());
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
|
||||
/* Create a new guest created by |embedder| with |options|. */
|
||||
|
||||
createGuest = function(embedder, url, frameName, options) {
|
||||
var closedByEmbedder, closedByUser, guest, guestId, ref1;
|
||||
guest = frameToGuest[frameName];
|
||||
if (frameName && (guest != null)) {
|
||||
guest.loadURL(url);
|
||||
return guest.id;
|
||||
}
|
||||
|
||||
/* Remember the embedder window's id. */
|
||||
if (options.webPreferences == null) {
|
||||
options.webPreferences = {};
|
||||
}
|
||||
options.webPreferences.openerId = (ref1 = BrowserWindow.fromWebContents(embedder)) != null ? ref1.id : void 0;
|
||||
guest = new BrowserWindow(options);
|
||||
guest.loadURL(url);
|
||||
|
||||
/*
|
||||
When |embedder| is destroyed we should also destroy attached guest, and if
|
||||
guest is closed by user then we should prevent |embedder| from double
|
||||
closing guest.
|
||||
*/
|
||||
guestId = guest.id;
|
||||
closedByEmbedder = function() {
|
||||
guest.removeListener('closed', closedByUser);
|
||||
return guest.destroy();
|
||||
};
|
||||
closedByUser = function() {
|
||||
embedder.send("ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_" + guestId);
|
||||
return embedder.removeListener('render-view-deleted', closedByEmbedder);
|
||||
};
|
||||
embedder.once('render-view-deleted', closedByEmbedder);
|
||||
guest.once('closed', closedByUser);
|
||||
if (frameName) {
|
||||
frameToGuest[frameName] = guest;
|
||||
guest.frameName = frameName;
|
||||
guest.once('closed', function() {
|
||||
return delete frameToGuest[frameName];
|
||||
});
|
||||
}
|
||||
return guest.id;
|
||||
};
|
||||
|
||||
|
||||
/* Routed window.open messages. */
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function() {
|
||||
var args, event, frameName, options, url;
|
||||
event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
url = args[0], frameName = args[1], options = args[2];
|
||||
options = mergeBrowserWindowOptions(event.sender, options);
|
||||
event.sender.emit('new-window', event, url, frameName, 'new-window', options);
|
||||
if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
|
||||
return event.returnValue = null;
|
||||
} else {
|
||||
return event.returnValue = createGuest(event.sender, url, frameName, options);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function(event, guestId) {
|
||||
var ref1;
|
||||
return (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1.destroy() : void 0;
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function() {
|
||||
var args, event, guestId, method, ref1;
|
||||
event = arguments[0], guestId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
|
||||
return (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1[method].apply(ref1, args) : void 0;
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function(event, guestId, message, targetOrigin, sourceOrigin) {
|
||||
var guestContents, ref1, ref2, sourceId;
|
||||
sourceId = (ref1 = BrowserWindow.fromWebContents(event.sender)) != null ? ref1.id : void 0;
|
||||
if (sourceId == null) {
|
||||
return;
|
||||
}
|
||||
guestContents = (ref2 = BrowserWindow.fromId(guestId)) != null ? ref2.webContents : void 0;
|
||||
if ((guestContents != null ? guestContents.getURL().indexOf(targetOrigin) : void 0) === 0 || targetOrigin === '*') {
|
||||
return guestContents != null ? guestContents.send('ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) : void 0;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function() {
|
||||
var args, event, guestId, method, ref1, ref2;
|
||||
event = arguments[0], guestId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
|
||||
return (ref1 = BrowserWindow.fromId(guestId)) != null ? (ref2 = ref1.webContents) != null ? ref2[method].apply(ref2, args) : void 0 : void 0;
|
||||
});
|
|
@ -1,118 +0,0 @@
|
|||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
util = require 'util'
|
||||
Module = require 'module'
|
||||
|
||||
# We modified the original process.argv to let node.js load the atom.js,
|
||||
# we need to restore it here.
|
||||
process.argv.splice 1, 1
|
||||
|
||||
# Clear search paths.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths')
|
||||
|
||||
# Import common settings.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')
|
||||
|
||||
globalPaths = Module.globalPaths
|
||||
unless process.env.ELECTRON_HIDE_INTERNAL_MODULES
|
||||
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib')
|
||||
|
||||
# Expose public APIs.
|
||||
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib', 'exports')
|
||||
|
||||
if process.platform is 'win32'
|
||||
# Redirect node's console to use our own implementations, since node can not
|
||||
# handle console output when running as GUI program.
|
||||
consoleLog = (args...) ->
|
||||
process.log util.format(args...) + "\n"
|
||||
streamWrite = (chunk, encoding, callback) ->
|
||||
chunk = chunk.toString(encoding) if Buffer.isBuffer chunk
|
||||
process.log chunk
|
||||
callback() if callback
|
||||
true
|
||||
console.log = console.error = console.warn = consoleLog
|
||||
process.stdout.write = process.stderr.write = streamWrite
|
||||
|
||||
# Always returns EOF for stdin stream.
|
||||
Readable = require('stream').Readable
|
||||
stdin = new Readable
|
||||
stdin.push null
|
||||
process.__defineGetter__ 'stdin', -> stdin
|
||||
|
||||
# Don't quit on fatal error.
|
||||
process.on 'uncaughtException', (error) ->
|
||||
# Do nothing if the user has a custom uncaught exception handler.
|
||||
if process.listeners('uncaughtException').length > 1
|
||||
return
|
||||
|
||||
# Show error in GUI.
|
||||
{dialog} = require 'electron'
|
||||
stack = error.stack ? "#{error.name}: #{error.message}"
|
||||
message = "Uncaught Exception:\n#{stack}"
|
||||
dialog.showErrorBox 'A JavaScript error occurred in the main process', message
|
||||
|
||||
# Emit 'exit' event on quit.
|
||||
{app} = require 'electron'
|
||||
app.on 'quit', (event, exitCode) ->
|
||||
process.emit 'exit', exitCode
|
||||
|
||||
# Map process.exit to app.exit, which quits gracefully.
|
||||
process.exit = app.exit
|
||||
|
||||
# Load the RPC server.
|
||||
require './rpc-server'
|
||||
|
||||
# Load the guest view manager.
|
||||
require './guest-view-manager'
|
||||
require './guest-window-manager'
|
||||
|
||||
# Now we try to load app's package.json.
|
||||
packageJson = null
|
||||
|
||||
searchPaths = [ 'app', 'app.asar', 'default_app' ]
|
||||
for packagePath in searchPaths
|
||||
try
|
||||
packagePath = path.join process.resourcesPath, packagePath
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
break
|
||||
catch e
|
||||
continue
|
||||
|
||||
unless packageJson?
|
||||
process.nextTick -> process.exit 1
|
||||
throw new Error("Unable to find a valid app")
|
||||
|
||||
# Set application's version.
|
||||
app.setVersion packageJson.version if packageJson.version?
|
||||
|
||||
# Set application's name.
|
||||
if packageJson.productName?
|
||||
app.setName packageJson.productName
|
||||
else if packageJson.name?
|
||||
app.setName packageJson.name
|
||||
|
||||
# Set application's desktop name.
|
||||
if packageJson.desktopName?
|
||||
app.setDesktopName packageJson.desktopName
|
||||
else
|
||||
app.setDesktopName "#{app.getName()}.desktop"
|
||||
|
||||
# Chrome 42 disables NPAPI plugins by default, reenable them here
|
||||
app.commandLine.appendSwitch 'enable-npapi'
|
||||
|
||||
# Set the user path according to application's name.
|
||||
app.setPath 'userData', path.join(app.getPath('appData'), app.getName())
|
||||
app.setPath 'userCache', path.join(app.getPath('cache'), app.getName())
|
||||
app.setAppPath packagePath
|
||||
|
||||
# Load the chrome extension support.
|
||||
require './chrome-extension'
|
||||
|
||||
# Load internal desktop-capturer module.
|
||||
require './desktop-capturer'
|
||||
|
||||
# Set main startup script of the app.
|
||||
mainStartupScript = packageJson.main or 'index.js'
|
||||
|
||||
# Finally load app's main.js and transfer control to C++.
|
||||
Module._load path.join(packagePath, mainStartupScript), Module, true
|
201
atom/browser/lib/init.js
Normal file
201
atom/browser/lib/init.js
Normal file
|
@ -0,0 +1,201 @@
|
|||
var Module, Readable, app, consoleLog, e, error1, fs, globalPaths, i, len, mainStartupScript, packageJson, packagePath, path, searchPaths, stdin, streamWrite, util,
|
||||
slice = [].slice;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
util = require('util');
|
||||
|
||||
Module = require('module');
|
||||
|
||||
|
||||
/* We modified the original process.argv to let node.js load the atom.js, */
|
||||
|
||||
|
||||
/* we need to restore it here. */
|
||||
|
||||
process.argv.splice(1, 1);
|
||||
|
||||
|
||||
/* Clear search paths. */
|
||||
|
||||
require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths'));
|
||||
|
||||
|
||||
/* Import common settings. */
|
||||
|
||||
require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'init'));
|
||||
|
||||
globalPaths = Module.globalPaths;
|
||||
|
||||
if (!process.env.ELECTRON_HIDE_INTERNAL_MODULES) {
|
||||
globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib'));
|
||||
}
|
||||
|
||||
|
||||
/* Expose public APIs. */
|
||||
|
||||
globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib', 'exports'));
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
|
||||
/*
|
||||
Redirect node's console to use our own implementations, since node can not
|
||||
handle console output when running as GUI program.
|
||||
*/
|
||||
consoleLog = function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return process.log(util.format.apply(util, args) + "\n");
|
||||
};
|
||||
streamWrite = function(chunk, encoding, callback) {
|
||||
if (Buffer.isBuffer(chunk)) {
|
||||
chunk = chunk.toString(encoding);
|
||||
}
|
||||
process.log(chunk);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
console.log = console.error = console.warn = consoleLog;
|
||||
process.stdout.write = process.stderr.write = streamWrite;
|
||||
|
||||
/* Always returns EOF for stdin stream. */
|
||||
Readable = require('stream').Readable;
|
||||
stdin = new Readable;
|
||||
stdin.push(null);
|
||||
process.__defineGetter__('stdin', function() {
|
||||
return stdin;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* Don't quit on fatal error. */
|
||||
|
||||
process.on('uncaughtException', function(error) {
|
||||
|
||||
/* Do nothing if the user has a custom uncaught exception handler. */
|
||||
var dialog, message, ref, stack;
|
||||
if (process.listeners('uncaughtException').length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Show error in GUI. */
|
||||
dialog = require('electron').dialog;
|
||||
stack = (ref = error.stack) != null ? ref : error.name + ": " + error.message;
|
||||
message = "Uncaught Exception:\n" + stack;
|
||||
return dialog.showErrorBox('A JavaScript error occurred in the main process', message);
|
||||
});
|
||||
|
||||
|
||||
/* Emit 'exit' event on quit. */
|
||||
|
||||
app = require('electron').app;
|
||||
|
||||
app.on('quit', function(event, exitCode) {
|
||||
return process.emit('exit', exitCode);
|
||||
});
|
||||
|
||||
|
||||
/* Map process.exit to app.exit, which quits gracefully. */
|
||||
|
||||
process.exit = app.exit;
|
||||
|
||||
|
||||
/* Load the RPC server. */
|
||||
|
||||
require('./rpc-server');
|
||||
|
||||
|
||||
/* Load the guest view manager. */
|
||||
|
||||
require('./guest-view-manager');
|
||||
|
||||
require('./guest-window-manager');
|
||||
|
||||
|
||||
/* Now we try to load app's package.json. */
|
||||
|
||||
packageJson = null;
|
||||
|
||||
searchPaths = ['app', 'app.asar', 'default_app'];
|
||||
|
||||
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')));
|
||||
break;
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (packageJson == null) {
|
||||
process.nextTick(function() {
|
||||
return process.exit(1);
|
||||
});
|
||||
throw new Error("Unable to find a valid app");
|
||||
}
|
||||
|
||||
|
||||
/* Set application's version. */
|
||||
|
||||
if (packageJson.version != null) {
|
||||
app.setVersion(packageJson.version);
|
||||
}
|
||||
|
||||
|
||||
/* Set application's name. */
|
||||
|
||||
if (packageJson.productName != null) {
|
||||
app.setName(packageJson.productName);
|
||||
} else if (packageJson.name != null) {
|
||||
app.setName(packageJson.name);
|
||||
}
|
||||
|
||||
|
||||
/* Set application's desktop name. */
|
||||
|
||||
if (packageJson.desktopName != null) {
|
||||
app.setDesktopName(packageJson.desktopName);
|
||||
} else {
|
||||
app.setDesktopName((app.getName()) + ".desktop");
|
||||
}
|
||||
|
||||
|
||||
/* Chrome 42 disables NPAPI plugins by default, reenable them here */
|
||||
|
||||
app.commandLine.appendSwitch('enable-npapi');
|
||||
|
||||
|
||||
/* Set the user path according to application's name. */
|
||||
|
||||
app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
|
||||
|
||||
app.setPath('userCache', path.join(app.getPath('cache'), app.getName()));
|
||||
|
||||
app.setAppPath(packagePath);
|
||||
|
||||
|
||||
/* Load the chrome extension support. */
|
||||
|
||||
require('./chrome-extension');
|
||||
|
||||
|
||||
/* Load internal desktop-capturer module. */
|
||||
|
||||
require('./desktop-capturer');
|
||||
|
||||
|
||||
/* Set main startup script of the app. */
|
||||
|
||||
mainStartupScript = packageJson.main || 'index.js';
|
||||
|
||||
|
||||
/* Finally load app's main.js and transfer control to C++. */
|
||||
|
||||
Module._load(path.join(packagePath, mainStartupScript), Module, true);
|
|
@ -1,67 +0,0 @@
|
|||
{EventEmitter} = require 'events'
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
class ObjectsRegistry extends EventEmitter
|
||||
constructor: ->
|
||||
@setMaxListeners Number.MAX_VALUE
|
||||
@nextId = 0
|
||||
|
||||
# Stores all objects by ref-counting.
|
||||
# (id) => {object, count}
|
||||
@storage = {}
|
||||
|
||||
# Stores the IDs of objects referenced by WebContents.
|
||||
# (webContentsId) => {(id) => (count)}
|
||||
@owners = {}
|
||||
|
||||
# Register a new object, the object would be kept referenced until you release
|
||||
# it explicitly.
|
||||
add: (webContentsId, obj) ->
|
||||
id = @saveToStorage obj
|
||||
# Remember the owner.
|
||||
@owners[webContentsId] ?= {}
|
||||
@owners[webContentsId][id] ?= 0
|
||||
@owners[webContentsId][id]++
|
||||
# Returns object's id
|
||||
id
|
||||
|
||||
# Get an object according to its ID.
|
||||
get: (id) ->
|
||||
@storage[id]?.object
|
||||
|
||||
# Dereference an object according to its ID.
|
||||
remove: (webContentsId, id) ->
|
||||
@dereference id, 1
|
||||
# Also reduce the count in owner.
|
||||
pointer = @owners[webContentsId]
|
||||
return unless pointer?
|
||||
--pointer[id]
|
||||
delete pointer[id] if pointer[id] is 0
|
||||
|
||||
# Clear all references to objects refrenced by the WebContents.
|
||||
clear: (webContentsId) ->
|
||||
@emit "clear-#{webContentsId}"
|
||||
return unless @owners[webContentsId]?
|
||||
@dereference id, count for id, count of @owners[webContentsId]
|
||||
delete @owners[webContentsId]
|
||||
|
||||
# Private: Saves the object into storage and assigns an ID for it.
|
||||
saveToStorage: (object) ->
|
||||
id = v8Util.getHiddenValue object, 'atomId'
|
||||
unless id
|
||||
id = ++@nextId
|
||||
@storage[id] = {count: 0, object}
|
||||
v8Util.setHiddenValue object, 'atomId', id
|
||||
++@storage[id].count
|
||||
id
|
||||
|
||||
# Private: Dereference the object from store.
|
||||
dereference: (id, count) ->
|
||||
pointer = @storage[id]
|
||||
return unless pointer?
|
||||
pointer.count -= count
|
||||
if pointer.count is 0
|
||||
v8Util.deleteHiddenValue pointer.object, 'atomId'
|
||||
delete @storage[id]
|
||||
|
||||
module.exports = new ObjectsRegistry
|
133
atom/browser/lib/objects-registry.js
Normal file
133
atom/browser/lib/objects-registry.js
Normal file
|
@ -0,0 +1,133 @@
|
|||
var EventEmitter, ObjectsRegistry, v8Util,
|
||||
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
hasProp = {}.hasOwnProperty;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
ObjectsRegistry = (function(superClass) {
|
||||
extend(ObjectsRegistry, superClass);
|
||||
|
||||
function ObjectsRegistry() {
|
||||
this.setMaxListeners(Number.MAX_VALUE);
|
||||
this.nextId = 0;
|
||||
|
||||
/*
|
||||
Stores all objects by ref-counting.
|
||||
(id) => {object, count}
|
||||
*/
|
||||
this.storage = {};
|
||||
|
||||
/*
|
||||
Stores the IDs of objects referenced by WebContents.
|
||||
(webContentsId) => {(id) => (count)}
|
||||
*/
|
||||
this.owners = {};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Register a new object, the object would be kept referenced until you release
|
||||
it explicitly.
|
||||
*/
|
||||
|
||||
ObjectsRegistry.prototype.add = function(webContentsId, obj) {
|
||||
var base, base1, id;
|
||||
id = this.saveToStorage(obj);
|
||||
|
||||
/* Remember the owner. */
|
||||
if ((base = this.owners)[webContentsId] == null) {
|
||||
base[webContentsId] = {};
|
||||
}
|
||||
if ((base1 = this.owners[webContentsId])[id] == null) {
|
||||
base1[id] = 0;
|
||||
}
|
||||
this.owners[webContentsId][id]++;
|
||||
|
||||
/* Returns object's id */
|
||||
return id;
|
||||
};
|
||||
|
||||
|
||||
/* Get an object according to its ID. */
|
||||
|
||||
ObjectsRegistry.prototype.get = function(id) {
|
||||
var ref;
|
||||
return (ref = this.storage[id]) != null ? ref.object : void 0;
|
||||
};
|
||||
|
||||
|
||||
/* Dereference an object according to its ID. */
|
||||
|
||||
ObjectsRegistry.prototype.remove = function(webContentsId, id) {
|
||||
var pointer;
|
||||
this.dereference(id, 1);
|
||||
|
||||
/* Also reduce the count in owner. */
|
||||
pointer = this.owners[webContentsId];
|
||||
if (pointer == null) {
|
||||
return;
|
||||
}
|
||||
--pointer[id];
|
||||
if (pointer[id] === 0) {
|
||||
return delete pointer[id];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Clear all references to objects refrenced by the WebContents. */
|
||||
|
||||
ObjectsRegistry.prototype.clear = function(webContentsId) {
|
||||
var count, id, ref;
|
||||
this.emit("clear-" + webContentsId);
|
||||
if (this.owners[webContentsId] == null) {
|
||||
return;
|
||||
}
|
||||
ref = this.owners[webContentsId];
|
||||
for (id in ref) {
|
||||
count = ref[id];
|
||||
this.dereference(id, count);
|
||||
}
|
||||
return delete this.owners[webContentsId];
|
||||
};
|
||||
|
||||
|
||||
/* Private: Saves the object into storage and assigns an ID for it. */
|
||||
|
||||
ObjectsRegistry.prototype.saveToStorage = function(object) {
|
||||
var id;
|
||||
id = v8Util.getHiddenValue(object, 'atomId');
|
||||
if (!id) {
|
||||
id = ++this.nextId;
|
||||
this.storage[id] = {
|
||||
count: 0,
|
||||
object: object
|
||||
};
|
||||
v8Util.setHiddenValue(object, 'atomId', id);
|
||||
}
|
||||
++this.storage[id].count;
|
||||
return id;
|
||||
};
|
||||
|
||||
|
||||
/* Private: Dereference the object from store. */
|
||||
|
||||
ObjectsRegistry.prototype.dereference = function(id, count) {
|
||||
var pointer;
|
||||
pointer = this.storage[id];
|
||||
if (pointer == null) {
|
||||
return;
|
||||
}
|
||||
pointer.count -= count;
|
||||
if (pointer.count === 0) {
|
||||
v8Util.deleteHiddenValue(pointer.object, 'atomId');
|
||||
return delete this.storage[id];
|
||||
}
|
||||
};
|
||||
|
||||
return ObjectsRegistry;
|
||||
|
||||
})(EventEmitter);
|
||||
|
||||
module.exports = new ObjectsRegistry;
|
|
@ -1,231 +0,0 @@
|
|||
path = require 'path'
|
||||
|
||||
electron = require 'electron'
|
||||
{ipcMain} = electron
|
||||
objectsRegistry = require './objects-registry'
|
||||
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
{IDWeakMap} = process.atomBinding 'id_weak_map'
|
||||
|
||||
# Convert a real value into meta data.
|
||||
valueToMeta = (sender, value, optimizeSimpleObject=false) ->
|
||||
meta = type: typeof value
|
||||
|
||||
meta.type = 'buffer' if Buffer.isBuffer value
|
||||
meta.type = 'value' if value is null
|
||||
meta.type = 'array' if Array.isArray value
|
||||
meta.type = 'error' if value instanceof Error
|
||||
meta.type = 'date' if value instanceof Date
|
||||
meta.type = 'promise' if value?.constructor.name is 'Promise'
|
||||
|
||||
# Treat simple objects as value.
|
||||
if optimizeSimpleObject and meta.type is 'object' and v8Util.getHiddenValue value, 'simple'
|
||||
meta.type = 'value'
|
||||
|
||||
# Treat the arguments object as array.
|
||||
meta.type = 'array' if meta.type is 'object' and value.callee? and value.length?
|
||||
|
||||
if meta.type is 'array'
|
||||
meta.members = []
|
||||
meta.members.push valueToMeta(sender, el) for el in value
|
||||
else if meta.type is 'object' or meta.type is 'function'
|
||||
meta.name = value.constructor.name
|
||||
|
||||
# Reference the original value if it's an object, because when it's
|
||||
# passed to renderer we would assume the renderer keeps a reference of
|
||||
# it.
|
||||
meta.id = objectsRegistry.add sender.getId(), value
|
||||
|
||||
meta.members = ({name, type: typeof field} for name, field of value)
|
||||
else if meta.type is 'buffer'
|
||||
meta.value = Array::slice.call value, 0
|
||||
else if meta.type is 'promise'
|
||||
meta.then = valueToMeta sender, value.then.bind(value)
|
||||
else if meta.type is 'error'
|
||||
meta.members = plainObjectToMeta value
|
||||
# Error.name is not part of own properties.
|
||||
meta.members.push {name: 'name', value: value.name}
|
||||
else if meta.type is 'date'
|
||||
meta.value = value.getTime()
|
||||
else
|
||||
meta.type = 'value'
|
||||
meta.value = value
|
||||
|
||||
meta
|
||||
|
||||
# Convert object to meta by value.
|
||||
plainObjectToMeta = (obj) ->
|
||||
Object.getOwnPropertyNames(obj).map (name) -> {name, value: obj[name]}
|
||||
|
||||
# Convert Error into meta data.
|
||||
exceptionToMeta = (error) ->
|
||||
type: 'exception', message: error.message, stack: (error.stack || error)
|
||||
|
||||
# Convert array of meta data from renderer into array of real values.
|
||||
unwrapArgs = (sender, args) ->
|
||||
metaToValue = (meta) ->
|
||||
switch meta.type
|
||||
when 'value' then meta.value
|
||||
when 'remote-object' then objectsRegistry.get meta.id
|
||||
when 'array' then unwrapArgs sender, meta.value
|
||||
when 'buffer' then new Buffer(meta.value)
|
||||
when 'date' then new Date(meta.value)
|
||||
when 'promise' then Promise.resolve(then: metaToValue(meta.then))
|
||||
when 'object'
|
||||
ret = v8Util.createObjectWithName meta.name
|
||||
for member in meta.members
|
||||
ret[member.name] = metaToValue(member.value)
|
||||
ret
|
||||
when 'function-with-return-value'
|
||||
returnValue = metaToValue meta.value
|
||||
-> returnValue
|
||||
when 'function'
|
||||
# Cache the callbacks in renderer.
|
||||
unless sender.callbacks
|
||||
sender.callbacks = new IDWeakMap
|
||||
sender.on 'render-view-deleted', ->
|
||||
sender.callbacks.clear()
|
||||
return sender.callbacks.get meta.id if sender.callbacks.has meta.id
|
||||
|
||||
rendererReleased = false
|
||||
objectsRegistry.once "clear-#{sender.getId()}", ->
|
||||
rendererReleased = true
|
||||
|
||||
ret = ->
|
||||
if rendererReleased
|
||||
throw new Error("Attempting to call a function in a renderer window
|
||||
that has been closed or released. Function provided here: #{meta.location}.")
|
||||
sender.send 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, arguments)
|
||||
v8Util.setDestructor ret, ->
|
||||
return if rendererReleased
|
||||
sender.callbacks.remove meta.id
|
||||
sender.send 'ATOM_RENDERER_RELEASE_CALLBACK', meta.id
|
||||
sender.callbacks.set meta.id, ret
|
||||
ret
|
||||
else throw new TypeError("Unknown type: #{meta.type}")
|
||||
|
||||
args.map metaToValue
|
||||
|
||||
# Call a function and send reply asynchronously if it's a an asynchronous
|
||||
# style function and the caller didn't pass a callback.
|
||||
callFunction = (event, func, caller, args) ->
|
||||
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
|
||||
funcPassedCallback = typeof args[args.length - 1] is 'function'
|
||||
|
||||
try
|
||||
if funcMarkedAsync and not funcPassedCallback
|
||||
args.push (ret) ->
|
||||
event.returnValue = valueToMeta event.sender, ret, true
|
||||
func.apply caller, args
|
||||
else
|
||||
ret = func.apply caller, args
|
||||
event.returnValue = valueToMeta event.sender, ret, true
|
||||
catch e
|
||||
# Catch functions thrown further down in function invocation and wrap
|
||||
# them with the function name so it's easier to trace things like
|
||||
# `Error processing argument -1.`
|
||||
funcName = func.name ? "anonymous"
|
||||
throw new Error("Could not call remote function `#{funcName}`.
|
||||
Check that the function signature is correct.
|
||||
Underlying error: #{e.message}")
|
||||
|
||||
# Send by BrowserWindow when its render view is deleted.
|
||||
process.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (id) ->
|
||||
objectsRegistry.clear id
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_REQUIRE', (event, module) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, process.mainModule.require(module)
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_GET_BUILTIN', (event, module) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, electron[module]
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_GLOBAL', (event, name) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, global[name]
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow()
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) ->
|
||||
event.returnValue = valueToMeta event.sender, event.sender
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
|
||||
try
|
||||
args = unwrapArgs event.sender, args
|
||||
constructor = objectsRegistry.get id
|
||||
# Call new with array of arguments.
|
||||
# http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
|
||||
obj = new (Function::bind.apply(constructor, [null].concat(args)))
|
||||
event.returnValue = valueToMeta event.sender, obj
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
|
||||
try
|
||||
args = unwrapArgs event.sender, args
|
||||
func = objectsRegistry.get id
|
||||
callFunction event, func, global, args
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
|
||||
try
|
||||
args = unwrapArgs event.sender, args
|
||||
constructor = objectsRegistry.get(id)[method]
|
||||
# Call new with array of arguments.
|
||||
obj = new (Function::bind.apply(constructor, [null].concat(args)))
|
||||
event.returnValue = valueToMeta event.sender, obj
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
|
||||
try
|
||||
args = unwrapArgs event.sender, args
|
||||
obj = objectsRegistry.get id
|
||||
callFunction event, obj[method], obj, args
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
|
||||
try
|
||||
obj = objectsRegistry.get id
|
||||
obj[name] = value
|
||||
event.returnValue = null
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
|
||||
try
|
||||
obj = objectsRegistry.get id
|
||||
event.returnValue = valueToMeta event.sender, obj[name]
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_DEREFERENCE', (event, id) ->
|
||||
objectsRegistry.remove event.sender.getId(), id
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) ->
|
||||
try
|
||||
guestViewManager = require './guest-view-manager'
|
||||
event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId)
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipcMain.on 'ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', (event, guestInstanceId, method, args...) ->
|
||||
try
|
||||
guestViewManager = require './guest-view-manager'
|
||||
guest = guestViewManager.getGuest(guestInstanceId)
|
||||
guest[method].apply(guest, args)
|
||||
catch e
|
||||
event.returnValue = exceptionToMeta e
|
389
atom/browser/lib/rpc-server.js
Normal file
389
atom/browser/lib/rpc-server.js
Normal file
|
@ -0,0 +1,389 @@
|
|||
var IDWeakMap, callFunction, electron, exceptionToMeta, ipcMain, objectsRegistry, path, plainObjectToMeta, unwrapArgs, v8Util, valueToMeta,
|
||||
slice = [].slice;
|
||||
|
||||
path = require('path');
|
||||
|
||||
electron = require('electron');
|
||||
|
||||
ipcMain = electron.ipcMain;
|
||||
|
||||
objectsRegistry = require('./objects-registry');
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap;
|
||||
|
||||
|
||||
/* Convert a real value into meta data. */
|
||||
|
||||
valueToMeta = function(sender, value, optimizeSimpleObject) {
|
||||
var el, field, i, len, meta, name;
|
||||
if (optimizeSimpleObject == null) {
|
||||
optimizeSimpleObject = false;
|
||||
}
|
||||
meta = {
|
||||
type: typeof value
|
||||
};
|
||||
if (Buffer.isBuffer(value)) {
|
||||
meta.type = 'buffer';
|
||||
}
|
||||
if (value === null) {
|
||||
meta.type = 'value';
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
meta.type = 'array';
|
||||
}
|
||||
if (value instanceof Error) {
|
||||
meta.type = 'error';
|
||||
}
|
||||
if (value instanceof Date) {
|
||||
meta.type = 'date';
|
||||
}
|
||||
if ((value != null ? value.constructor.name : void 0) === 'Promise') {
|
||||
meta.type = 'promise';
|
||||
}
|
||||
|
||||
/* Treat simple objects as value. */
|
||||
if (optimizeSimpleObject && meta.type === 'object' && v8Util.getHiddenValue(value, 'simple')) {
|
||||
meta.type = 'value';
|
||||
}
|
||||
|
||||
/* Treat the arguments object as array. */
|
||||
if (meta.type === 'object' && (value.callee != null) && (value.length != null)) {
|
||||
meta.type = 'array';
|
||||
}
|
||||
if (meta.type === 'array') {
|
||||
meta.members = [];
|
||||
for (i = 0, len = value.length; i < len; i++) {
|
||||
el = value[i];
|
||||
meta.members.push(valueToMeta(sender, el));
|
||||
}
|
||||
} else if (meta.type === 'object' || meta.type === 'function') {
|
||||
meta.name = value.constructor.name;
|
||||
|
||||
/*
|
||||
Reference the original value if it's an object, because when it's
|
||||
passed to renderer we would assume the renderer keeps a reference of
|
||||
it.
|
||||
*/
|
||||
meta.id = objectsRegistry.add(sender.getId(), value);
|
||||
meta.members = (function() {
|
||||
var results;
|
||||
results = [];
|
||||
for (name in value) {
|
||||
field = value[name];
|
||||
results.push({
|
||||
name: name,
|
||||
type: typeof field
|
||||
});
|
||||
}
|
||||
return results;
|
||||
})();
|
||||
} else if (meta.type === 'buffer') {
|
||||
meta.value = Array.prototype.slice.call(value, 0);
|
||||
} else if (meta.type === 'promise') {
|
||||
meta.then = valueToMeta(sender, value.then.bind(value));
|
||||
} else if (meta.type === 'error') {
|
||||
meta.members = plainObjectToMeta(value);
|
||||
|
||||
/* Error.name is not part of own properties. */
|
||||
meta.members.push({
|
||||
name: 'name',
|
||||
value: value.name
|
||||
});
|
||||
} else if (meta.type === 'date') {
|
||||
meta.value = value.getTime();
|
||||
} else {
|
||||
meta.type = 'value';
|
||||
meta.value = value;
|
||||
}
|
||||
return meta;
|
||||
};
|
||||
|
||||
|
||||
/* Convert object to meta by value. */
|
||||
|
||||
plainObjectToMeta = function(obj) {
|
||||
return Object.getOwnPropertyNames(obj).map(function(name) {
|
||||
return {
|
||||
name: name,
|
||||
value: obj[name]
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Convert Error into meta data. */
|
||||
|
||||
exceptionToMeta = function(error) {
|
||||
return {
|
||||
type: 'exception',
|
||||
message: error.message,
|
||||
stack: error.stack || error
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* Convert array of meta data from renderer into array of real values. */
|
||||
|
||||
unwrapArgs = function(sender, args) {
|
||||
var metaToValue;
|
||||
metaToValue = function(meta) {
|
||||
var i, len, member, ref, rendererReleased, ret, returnValue;
|
||||
switch (meta.type) {
|
||||
case 'value':
|
||||
return meta.value;
|
||||
case 'remote-object':
|
||||
return objectsRegistry.get(meta.id);
|
||||
case 'array':
|
||||
return unwrapArgs(sender, meta.value);
|
||||
case 'buffer':
|
||||
return new Buffer(meta.value);
|
||||
case 'date':
|
||||
return new Date(meta.value);
|
||||
case 'promise':
|
||||
return Promise.resolve({
|
||||
then: metaToValue(meta.then)
|
||||
});
|
||||
case 'object':
|
||||
ret = v8Util.createObjectWithName(meta.name);
|
||||
ref = meta.members;
|
||||
for (i = 0, len = ref.length; i < len; i++) {
|
||||
member = ref[i];
|
||||
ret[member.name] = metaToValue(member.value);
|
||||
}
|
||||
return ret;
|
||||
case 'function-with-return-value':
|
||||
returnValue = metaToValue(meta.value);
|
||||
return function() {
|
||||
return returnValue;
|
||||
};
|
||||
case 'function':
|
||||
|
||||
/* Cache the callbacks in renderer. */
|
||||
if (!sender.callbacks) {
|
||||
sender.callbacks = new IDWeakMap;
|
||||
sender.on('render-view-deleted', function() {
|
||||
return sender.callbacks.clear();
|
||||
});
|
||||
}
|
||||
if (sender.callbacks.has(meta.id)) {
|
||||
return sender.callbacks.get(meta.id);
|
||||
}
|
||||
rendererReleased = false;
|
||||
objectsRegistry.once("clear-" + (sender.getId()), function() {
|
||||
return rendererReleased = true;
|
||||
});
|
||||
ret = function() {
|
||||
if (rendererReleased) {
|
||||
throw new Error("Attempting to call a function in a renderer window that has been closed or released. Function provided here: " + meta.location + ".");
|
||||
}
|
||||
return sender.send('ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, arguments));
|
||||
};
|
||||
v8Util.setDestructor(ret, function() {
|
||||
if (rendererReleased) {
|
||||
return;
|
||||
}
|
||||
sender.callbacks.remove(meta.id);
|
||||
return sender.send('ATOM_RENDERER_RELEASE_CALLBACK', meta.id);
|
||||
});
|
||||
sender.callbacks.set(meta.id, ret);
|
||||
return ret;
|
||||
default:
|
||||
throw new TypeError("Unknown type: " + meta.type);
|
||||
}
|
||||
};
|
||||
return args.map(metaToValue);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Call a function and send reply asynchronously if it's a an asynchronous
|
||||
style function and the caller didn't pass a callback.
|
||||
*/
|
||||
|
||||
callFunction = function(event, func, caller, args) {
|
||||
var e, error1, funcMarkedAsync, funcName, funcPassedCallback, ref, ret;
|
||||
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous');
|
||||
funcPassedCallback = typeof args[args.length - 1] === 'function';
|
||||
try {
|
||||
if (funcMarkedAsync && !funcPassedCallback) {
|
||||
args.push(function(ret) {
|
||||
return event.returnValue = valueToMeta(event.sender, ret, true);
|
||||
});
|
||||
return func.apply(caller, args);
|
||||
} else {
|
||||
ret = func.apply(caller, args);
|
||||
return event.returnValue = valueToMeta(event.sender, ret, true);
|
||||
}
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
|
||||
/*
|
||||
Catch functions thrown further down in function invocation and wrap
|
||||
them with the function name so it's easier to trace things like
|
||||
`Error processing argument -1.`
|
||||
*/
|
||||
funcName = (ref = func.name) != null ? ref : "anonymous";
|
||||
throw new Error("Could not call remote function `" + funcName + "`. Check that the function signature is correct. Underlying error: " + e.message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Send by BrowserWindow when its render view is deleted. */
|
||||
|
||||
process.on('ATOM_BROWSER_RELEASE_RENDER_VIEW', function(id) {
|
||||
return objectsRegistry.clear(id);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_REQUIRE', function(event, module) {
|
||||
var e, error1;
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, process.mainModule.require(module));
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_GET_BUILTIN', function(event, module) {
|
||||
var e, error1;
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, electron[module]);
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_GLOBAL', function(event, name) {
|
||||
var e, error1;
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, global[name]);
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_CURRENT_WINDOW', function(event) {
|
||||
var e, error1;
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow());
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_CURRENT_WEB_CONTENTS', function(event) {
|
||||
return event.returnValue = valueToMeta(event.sender, event.sender);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_CONSTRUCTOR', function(event, id, args) {
|
||||
var constructor, e, error1, obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
constructor = objectsRegistry.get(id);
|
||||
|
||||
/*
|
||||
Call new with array of arguments.
|
||||
http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
|
||||
*/
|
||||
obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
|
||||
return event.returnValue = valueToMeta(event.sender, obj);
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_FUNCTION_CALL', function(event, id, args) {
|
||||
var e, error1, func;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
func = objectsRegistry.get(id);
|
||||
return callFunction(event, func, global, args);
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_CONSTRUCTOR', function(event, id, method, args) {
|
||||
var constructor, e, error1, obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
constructor = objectsRegistry.get(id)[method];
|
||||
|
||||
/* Call new with array of arguments. */
|
||||
obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
|
||||
return event.returnValue = valueToMeta(event.sender, obj);
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_CALL', function(event, id, method, args) {
|
||||
var e, error1, obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
obj = objectsRegistry.get(id);
|
||||
return callFunction(event, obj[method], obj, args);
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_SET', function(event, id, name, value) {
|
||||
var e, error1, obj;
|
||||
try {
|
||||
obj = objectsRegistry.get(id);
|
||||
obj[name] = value;
|
||||
return event.returnValue = null;
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_GET', function(event, id, name) {
|
||||
var e, error1, obj;
|
||||
try {
|
||||
obj = objectsRegistry.get(id);
|
||||
return event.returnValue = valueToMeta(event.sender, obj[name]);
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_DEREFERENCE', function(event, id) {
|
||||
return objectsRegistry.remove(event.sender.getId(), id);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_GUEST_WEB_CONTENTS', function(event, guestInstanceId) {
|
||||
var e, error1, guestViewManager;
|
||||
try {
|
||||
guestViewManager = require('./guest-view-manager');
|
||||
return event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId));
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function() {
|
||||
var args, e, error1, event, guest, guestInstanceId, guestViewManager, method;
|
||||
event = arguments[0], guestInstanceId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
|
||||
try {
|
||||
guestViewManager = require('./guest-view-manager');
|
||||
guest = guestViewManager.getGuest(guestInstanceId);
|
||||
return guest[method].apply(guest, args);
|
||||
} catch (error1) {
|
||||
e = error1;
|
||||
return event.returnValue = exceptionToMeta(e);
|
||||
}
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
module.exports =
|
||||
class CallbacksRegistry
|
||||
constructor: ->
|
||||
@nextId = 0
|
||||
@callbacks = {}
|
||||
|
||||
add: (callback) ->
|
||||
# The callback is already added.
|
||||
id = v8Util.getHiddenValue callback, 'callbackId'
|
||||
return id if id?
|
||||
|
||||
id = ++@nextId
|
||||
|
||||
# Capture the location of the function and put it in the ID string,
|
||||
# so that release errors can be tracked down easily.
|
||||
regexp = /at (.*)/gi
|
||||
stackString = (new Error).stack
|
||||
|
||||
while (match = regexp.exec(stackString)) isnt null
|
||||
[x, location] = match
|
||||
continue if location.indexOf('(native)') isnt -1
|
||||
continue if location.indexOf('atom.asar') isnt -1
|
||||
[x, filenameAndLine] = /([^/^\)]*)\)?$/gi.exec(location)
|
||||
break
|
||||
|
||||
@callbacks[id] = callback
|
||||
v8Util.setHiddenValue callback, 'callbackId', id
|
||||
v8Util.setHiddenValue callback, 'location', filenameAndLine
|
||||
id
|
||||
|
||||
get: (id) ->
|
||||
@callbacks[id] ? ->
|
||||
|
||||
call: (id, args...) ->
|
||||
@get(id).call global, args...
|
||||
|
||||
apply: (id, args...) ->
|
||||
@get(id).apply global, args...
|
||||
|
||||
remove: (id) ->
|
||||
delete @callbacks[id]
|
68
atom/common/api/lib/callbacks-registry.js
Normal file
68
atom/common/api/lib/callbacks-registry.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
var CallbacksRegistry, v8Util,
|
||||
slice = [].slice;
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
module.exports = CallbacksRegistry = (function() {
|
||||
function CallbacksRegistry() {
|
||||
this.nextId = 0;
|
||||
this.callbacks = {};
|
||||
}
|
||||
|
||||
CallbacksRegistry.prototype.add = function(callback) {
|
||||
|
||||
/* The callback is already added. */
|
||||
var filenameAndLine, id, location, match, ref, regexp, stackString, x;
|
||||
id = v8Util.getHiddenValue(callback, 'callbackId');
|
||||
if (id != null) {
|
||||
return id;
|
||||
}
|
||||
id = ++this.nextId;
|
||||
|
||||
/*
|
||||
Capture the location of the function and put it in the ID string,
|
||||
so that release errors can be tracked down easily.
|
||||
*/
|
||||
regexp = /at (.*)/gi;
|
||||
stackString = (new Error).stack;
|
||||
while ((match = regexp.exec(stackString)) !== null) {
|
||||
x = match[0], location = match[1];
|
||||
if (location.indexOf('(native)') !== -1) {
|
||||
continue;
|
||||
}
|
||||
if (location.indexOf('atom.asar') !== -1) {
|
||||
continue;
|
||||
}
|
||||
ref = /([^\/^\)]*)\)?$/gi.exec(location), x = ref[0], filenameAndLine = ref[1];
|
||||
break;
|
||||
}
|
||||
this.callbacks[id] = callback;
|
||||
v8Util.setHiddenValue(callback, 'callbackId', id);
|
||||
v8Util.setHiddenValue(callback, 'location', filenameAndLine);
|
||||
return id;
|
||||
};
|
||||
|
||||
CallbacksRegistry.prototype.get = function(id) {
|
||||
var ref;
|
||||
return (ref = this.callbacks[id]) != null ? ref : function() {};
|
||||
};
|
||||
|
||||
CallbacksRegistry.prototype.call = function() {
|
||||
var args, id, ref;
|
||||
id = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
return (ref = this.get(id)).call.apply(ref, [global].concat(slice.call(args)));
|
||||
};
|
||||
|
||||
CallbacksRegistry.prototype.apply = function() {
|
||||
var args, id, ref;
|
||||
id = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
|
||||
return (ref = this.get(id)).apply.apply(ref, [global].concat(slice.call(args)));
|
||||
};
|
||||
|
||||
CallbacksRegistry.prototype.remove = function(id) {
|
||||
return delete this.callbacks[id];
|
||||
};
|
||||
|
||||
return CallbacksRegistry;
|
||||
|
||||
})();
|
|
@ -1,5 +0,0 @@
|
|||
if process.platform is 'linux' and process.type is 'renderer'
|
||||
# On Linux we could not access clipboard in renderer process.
|
||||
module.exports = require('electron').remote.clipboard
|
||||
else
|
||||
module.exports = process.atomBinding 'clipboard'
|
7
atom/common/api/lib/clipboard.js
Normal file
7
atom/common/api/lib/clipboard.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
if (process.platform === 'linux' && process.type === 'renderer') {
|
||||
|
||||
/* On Linux we could not access clipboard in renderer process. */
|
||||
module.exports = require('electron').remote.clipboard;
|
||||
} else {
|
||||
module.exports = process.atomBinding('clipboard');
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
fs = require 'fs'
|
||||
os = require 'os'
|
||||
path = require 'path'
|
||||
{spawn} = require 'child_process'
|
||||
|
||||
electron = require 'electron'
|
||||
binding = process.atomBinding 'crash_reporter'
|
||||
|
||||
class CrashReporter
|
||||
start: (options={}) ->
|
||||
{@productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra} = options
|
||||
|
||||
# Deprecated.
|
||||
{deprecate} = electron
|
||||
if options.submitUrl
|
||||
submitURL ?= options.submitUrl
|
||||
deprecate.warn 'submitUrl', 'submitURL'
|
||||
|
||||
{app} = if process.type is 'browser' then electron else electron.remote
|
||||
|
||||
@productName ?= app.getName()
|
||||
autoSubmit ?= true
|
||||
ignoreSystemCrashHandler ?= false
|
||||
extra ?= {}
|
||||
|
||||
extra._productName ?= @productName
|
||||
extra._companyName ?= companyName
|
||||
extra._version ?= app.getVersion()
|
||||
|
||||
unless companyName?
|
||||
deprecate.log('companyName is now a required option to crashReporter.start')
|
||||
return
|
||||
|
||||
unless submitURL?
|
||||
deprecate.log('submitURL is now a required option to crashReporter.start')
|
||||
return
|
||||
|
||||
start = => binding.start @productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra
|
||||
|
||||
if process.platform is 'win32'
|
||||
args = [
|
||||
"--reporter-url=#{submitURL}"
|
||||
"--application-name=#{@productName}"
|
||||
"--v=1"
|
||||
]
|
||||
env = ATOM_SHELL_INTERNAL_CRASH_SERVICE: 1
|
||||
|
||||
spawn process.execPath, args, {env, detached: true}
|
||||
start()
|
||||
|
||||
getLastCrashReport: ->
|
||||
reports = this.getUploadedReports()
|
||||
if reports.length > 0 then reports[0] else null
|
||||
|
||||
getUploadedReports: ->
|
||||
tmpdir =
|
||||
if process.platform is 'win32'
|
||||
os.tmpdir()
|
||||
else
|
||||
'/tmp'
|
||||
log =
|
||||
if process.platform is 'darwin'
|
||||
path.join tmpdir, "#{@productName} Crashes"
|
||||
else
|
||||
path.join tmpdir, "#{@productName} Crashes", 'uploads.log'
|
||||
binding._getUploadedReports log
|
||||
|
||||
crashRepoter = new CrashReporter
|
||||
module.exports = crashRepoter
|
104
atom/common/api/lib/crash-reporter.js
Normal file
104
atom/common/api/lib/crash-reporter.js
Normal file
|
@ -0,0 +1,104 @@
|
|||
var CrashReporter, binding, crashRepoter, electron, fs, os, path, spawn;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
os = require('os');
|
||||
|
||||
path = require('path');
|
||||
|
||||
spawn = require('child_process').spawn;
|
||||
|
||||
electron = require('electron');
|
||||
|
||||
binding = process.atomBinding('crash_reporter');
|
||||
|
||||
CrashReporter = (function() {
|
||||
function CrashReporter() {}
|
||||
|
||||
CrashReporter.prototype.start = function(options) {
|
||||
var app, args, autoSubmit, companyName, deprecate, env, extra, ignoreSystemCrashHandler, start, submitURL;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
this.productName = options.productName, companyName = options.companyName, submitURL = options.submitURL, autoSubmit = options.autoSubmit, ignoreSystemCrashHandler = options.ignoreSystemCrashHandler, extra = options.extra;
|
||||
|
||||
/* Deprecated. */
|
||||
deprecate = electron.deprecate;
|
||||
if (options.submitUrl) {
|
||||
if (submitURL == null) {
|
||||
submitURL = options.submitUrl;
|
||||
}
|
||||
deprecate.warn('submitUrl', 'submitURL');
|
||||
}
|
||||
app = (process.type === 'browser' ? electron : electron.remote).app;
|
||||
if (this.productName == null) {
|
||||
this.productName = app.getName();
|
||||
}
|
||||
if (autoSubmit == null) {
|
||||
autoSubmit = true;
|
||||
}
|
||||
if (ignoreSystemCrashHandler == null) {
|
||||
ignoreSystemCrashHandler = false;
|
||||
}
|
||||
if (extra == null) {
|
||||
extra = {};
|
||||
}
|
||||
if (extra._productName == null) {
|
||||
extra._productName = this.productName;
|
||||
}
|
||||
if (extra._companyName == null) {
|
||||
extra._companyName = companyName;
|
||||
}
|
||||
if (extra._version == null) {
|
||||
extra._version = app.getVersion();
|
||||
}
|
||||
if (companyName == null) {
|
||||
deprecate.log('companyName is now a required option to crashReporter.start');
|
||||
return;
|
||||
}
|
||||
if (submitURL == null) {
|
||||
deprecate.log('submitURL is now a required option to crashReporter.start');
|
||||
return;
|
||||
}
|
||||
start = (function(_this) {
|
||||
return function() {
|
||||
return binding.start(_this.productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra);
|
||||
};
|
||||
})(this);
|
||||
if (process.platform === 'win32') {
|
||||
args = ["--reporter-url=" + submitURL, "--application-name=" + this.productName, "--v=1"];
|
||||
env = {
|
||||
ATOM_SHELL_INTERNAL_CRASH_SERVICE: 1
|
||||
};
|
||||
spawn(process.execPath, args, {
|
||||
env: env,
|
||||
detached: true
|
||||
});
|
||||
}
|
||||
return start();
|
||||
};
|
||||
|
||||
CrashReporter.prototype.getLastCrashReport = function() {
|
||||
var reports;
|
||||
reports = this.getUploadedReports();
|
||||
if (reports.length > 0) {
|
||||
return reports[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
CrashReporter.prototype.getUploadedReports = function() {
|
||||
var log, tmpdir;
|
||||
tmpdir = process.platform === 'win32' ? os.tmpdir() : '/tmp';
|
||||
log = process.platform === 'darwin' ? path.join(tmpdir, this.productName + " Crashes") : path.join(tmpdir, this.productName + " Crashes", 'uploads.log');
|
||||
return binding._getUploadedReports(log);
|
||||
};
|
||||
|
||||
return CrashReporter;
|
||||
|
||||
})();
|
||||
|
||||
crashRepoter = new CrashReporter;
|
||||
|
||||
module.exports = crashRepoter;
|
|
@ -1,68 +0,0 @@
|
|||
# Deprecate a method.
|
||||
deprecate = (oldName, newName, fn) ->
|
||||
warned = false
|
||||
->
|
||||
unless warned or process.noDeprecation
|
||||
warned = true
|
||||
deprecate.warn oldName, newName
|
||||
fn.apply this, arguments
|
||||
|
||||
# The method is renamed.
|
||||
deprecate.rename = (object, oldName, newName) ->
|
||||
warned = false
|
||||
newMethod = ->
|
||||
unless warned or process.noDeprecation
|
||||
warned = true
|
||||
deprecate.warn oldName, newName
|
||||
this[newName].apply this, arguments
|
||||
if typeof object is 'function'
|
||||
object.prototype[oldName] = newMethod
|
||||
else
|
||||
object[oldName] = newMethod
|
||||
|
||||
# Forward the method to member.
|
||||
deprecate.member = (object, method, member) ->
|
||||
warned = false
|
||||
object.prototype[method] = ->
|
||||
unless warned or process.noDeprecation
|
||||
warned = true
|
||||
deprecate.warn method, "#{member}.#{method}"
|
||||
this[member][method].apply this[member], arguments
|
||||
|
||||
# Deprecate a property.
|
||||
deprecate.property = (object, property, method) ->
|
||||
Object.defineProperty object, property,
|
||||
get: ->
|
||||
warned = false
|
||||
unless warned or process.noDeprecation
|
||||
warned = true
|
||||
deprecate.warn "#{property} property", "#{method} method"
|
||||
this[method]()
|
||||
|
||||
# Deprecate an event.
|
||||
deprecate.event = (emitter, oldName, newName, fn) ->
|
||||
warned = false
|
||||
emitter.on newName, (args...) ->
|
||||
if @listenerCount(oldName) > 0 # there is listeners for old API.
|
||||
unless warned or process.noDeprecation
|
||||
warned = true
|
||||
deprecate.warn "'#{oldName}' event", "'#{newName}' event"
|
||||
if fn?
|
||||
fn.apply this, arguments
|
||||
else
|
||||
@emit oldName, args...
|
||||
|
||||
# Print deprecation warning.
|
||||
deprecate.warn = (oldName, newName) ->
|
||||
deprecate.log "#{oldName} is deprecated. Use #{newName} instead."
|
||||
|
||||
# Print deprecation message.
|
||||
deprecate.log = (message) ->
|
||||
if process.throwDeprecation
|
||||
throw new Error(message)
|
||||
else if process.traceDeprecation
|
||||
console.trace message
|
||||
else
|
||||
console.warn "(electron) #{message}"
|
||||
|
||||
module.exports = deprecate
|
115
atom/common/api/lib/deprecate.js
Normal file
115
atom/common/api/lib/deprecate.js
Normal file
|
@ -0,0 +1,115 @@
|
|||
|
||||
/* Deprecate a method. */
|
||||
var deprecate,
|
||||
slice = [].slice;
|
||||
|
||||
deprecate = function(oldName, newName, fn) {
|
||||
var warned;
|
||||
warned = false;
|
||||
return function() {
|
||||
if (!(warned || process.noDeprecation)) {
|
||||
warned = true;
|
||||
deprecate.warn(oldName, newName);
|
||||
}
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* The method is renamed. */
|
||||
|
||||
deprecate.rename = function(object, oldName, newName) {
|
||||
var newMethod, warned;
|
||||
warned = false;
|
||||
newMethod = function() {
|
||||
if (!(warned || process.noDeprecation)) {
|
||||
warned = true;
|
||||
deprecate.warn(oldName, newName);
|
||||
}
|
||||
return this[newName].apply(this, arguments);
|
||||
};
|
||||
if (typeof object === 'function') {
|
||||
return object.prototype[oldName] = newMethod;
|
||||
} else {
|
||||
return object[oldName] = newMethod;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Forward the method to member. */
|
||||
|
||||
deprecate.member = function(object, method, member) {
|
||||
var warned;
|
||||
warned = false;
|
||||
return object.prototype[method] = function() {
|
||||
if (!(warned || process.noDeprecation)) {
|
||||
warned = true;
|
||||
deprecate.warn(method, member + "." + method);
|
||||
}
|
||||
return this[member][method].apply(this[member], arguments);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* Deprecate a property. */
|
||||
|
||||
deprecate.property = function(object, property, method) {
|
||||
return Object.defineProperty(object, property, {
|
||||
get: function() {
|
||||
var warned;
|
||||
warned = false;
|
||||
if (!(warned || process.noDeprecation)) {
|
||||
warned = true;
|
||||
deprecate.warn(property + " property", method + " method");
|
||||
}
|
||||
return this[method]();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Deprecate an event. */
|
||||
|
||||
deprecate.event = function(emitter, oldName, newName, fn) {
|
||||
var warned;
|
||||
warned = false;
|
||||
return emitter.on(newName, function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
|
||||
/* there is listeners for old API. */
|
||||
if (this.listenerCount(oldName) > 0) {
|
||||
if (!(warned || process.noDeprecation)) {
|
||||
warned = true;
|
||||
deprecate.warn("'" + oldName + "' event", "'" + newName + "' event");
|
||||
}
|
||||
if (fn != null) {
|
||||
return fn.apply(this, arguments);
|
||||
} else {
|
||||
return this.emit.apply(this, [oldName].concat(slice.call(args)));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Print deprecation warning. */
|
||||
|
||||
deprecate.warn = function(oldName, newName) {
|
||||
return deprecate.log(oldName + " is deprecated. Use " + newName + " instead.");
|
||||
};
|
||||
|
||||
|
||||
/* Print deprecation message. */
|
||||
|
||||
deprecate.log = function(message) {
|
||||
if (process.throwDeprecation) {
|
||||
throw new Error(message);
|
||||
} else if (process.traceDeprecation) {
|
||||
return console.trace(message);
|
||||
} else {
|
||||
return console.warn("(electron) " + message);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = deprecate;
|
|
@ -1,29 +0,0 @@
|
|||
# Do not expose the internal modules to `require`.
|
||||
exports.hideInternalModules = ->
|
||||
{globalPaths} = require 'module'
|
||||
if globalPaths.length is 3
|
||||
# Remove the "common/api/lib" and "browser-or-renderer/api/lib".
|
||||
globalPaths.splice 0, 2
|
||||
|
||||
# Attaches properties to |exports|.
|
||||
exports.defineProperties = (exports) ->
|
||||
Object.defineProperties exports,
|
||||
# Common modules, please sort with alphabet order.
|
||||
clipboard:
|
||||
# Must be enumerable, otherwise it woulde be invisible to remote module.
|
||||
enumerable: true
|
||||
get: -> require '../clipboard'
|
||||
crashReporter:
|
||||
enumerable: true
|
||||
get: -> require '../crash-reporter'
|
||||
nativeImage:
|
||||
enumerable: true
|
||||
get: -> require '../native-image'
|
||||
shell:
|
||||
enumerable: true
|
||||
get: -> require '../shell'
|
||||
# The internal modules, invisible unless you know their names.
|
||||
CallbacksRegistry:
|
||||
get: -> require '../callbacks-registry'
|
||||
deprecate:
|
||||
get: -> require '../deprecate'
|
59
atom/common/api/lib/exports/electron.js
Normal file
59
atom/common/api/lib/exports/electron.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
|
||||
/* Do not expose the internal modules to `require`. */
|
||||
exports.hideInternalModules = function() {
|
||||
var globalPaths;
|
||||
globalPaths = require('module').globalPaths;
|
||||
if (globalPaths.length === 3) {
|
||||
|
||||
/* Remove the "common/api/lib" and "browser-or-renderer/api/lib". */
|
||||
return globalPaths.splice(0, 2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Attaches properties to |exports|. */
|
||||
|
||||
exports.defineProperties = function(exports) {
|
||||
return Object.defineProperties(exports, {
|
||||
|
||||
/* Common modules, please sort with alphabet order. */
|
||||
clipboard: {
|
||||
|
||||
/* Must be enumerable, otherwise it woulde be invisible to remote module. */
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../clipboard');
|
||||
}
|
||||
},
|
||||
crashReporter: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../crash-reporter');
|
||||
}
|
||||
},
|
||||
nativeImage: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../native-image');
|
||||
}
|
||||
},
|
||||
shell: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../shell');
|
||||
}
|
||||
},
|
||||
|
||||
/* The internal modules, invisible unless you know their names. */
|
||||
CallbacksRegistry: {
|
||||
get: function() {
|
||||
return require('../callbacks-registry');
|
||||
}
|
||||
},
|
||||
deprecate: {
|
||||
get: function() {
|
||||
return require('../deprecate');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
{deprecate} = require 'electron'
|
||||
nativeImage = process.atomBinding 'native_image'
|
||||
|
||||
# Deprecated.
|
||||
deprecate.rename nativeImage, 'createFromDataUrl', 'createFromDataURL'
|
||||
|
||||
module.exports = nativeImage
|
12
atom/common/api/lib/native-image.js
Normal file
12
atom/common/api/lib/native-image.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
var deprecate, nativeImage;
|
||||
|
||||
deprecate = require('electron').deprecate;
|
||||
|
||||
nativeImage = process.atomBinding('native_image');
|
||||
|
||||
|
||||
/* Deprecated. */
|
||||
|
||||
deprecate.rename(nativeImage, 'createFromDataUrl', 'createFromDataURL');
|
||||
|
||||
module.exports = nativeImage;
|
|
@ -1 +0,0 @@
|
|||
module.exports = process.atomBinding 'shell'
|
1
atom/common/api/lib/shell.js
Normal file
1
atom/common/api/lib/shell.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = process.atomBinding('shell');
|
|
@ -1,386 +0,0 @@
|
|||
asar = process.binding 'atom_common_asar'
|
||||
child_process = require 'child_process'
|
||||
path = require 'path'
|
||||
util = require 'util'
|
||||
|
||||
# Cache asar archive objects.
|
||||
cachedArchives = {}
|
||||
getOrCreateArchive = (p) ->
|
||||
archive = cachedArchives[p]
|
||||
return archive if archive?
|
||||
archive = asar.createArchive p
|
||||
return false unless archive
|
||||
cachedArchives[p] = archive
|
||||
|
||||
# Clean cache on quit.
|
||||
process.on 'exit', ->
|
||||
archive.destroy() for own p, archive of cachedArchives
|
||||
|
||||
# Separate asar package's path from full path.
|
||||
splitPath = (p) ->
|
||||
return [false] if process.noAsar # shortcut to disable asar.
|
||||
return [false] if typeof p isnt 'string'
|
||||
return [true, p, ''] if p.substr(-5) is '.asar'
|
||||
p = path.normalize p
|
||||
index = p.lastIndexOf ".asar#{path.sep}"
|
||||
return [false] if index is -1
|
||||
[true, p.substr(0, index + 5), p.substr(index + 6)]
|
||||
|
||||
# Convert asar archive's Stats object to fs's Stats object.
|
||||
nextInode = 0
|
||||
uid = if process.getuid? then process.getuid() else 0
|
||||
gid = if process.getgid? then process.getgid() else 0
|
||||
fakeTime = new Date()
|
||||
asarStatsToFsStats = (stats) ->
|
||||
{
|
||||
dev: 1,
|
||||
ino: ++nextInode,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
rdev: 0,
|
||||
atime: stats.atime || fakeTime,
|
||||
birthtime: stats.birthtime || fakeTime,
|
||||
mtime: stats.mtime || fakeTime,
|
||||
ctime: stats.ctime || fakeTime,
|
||||
size: stats.size,
|
||||
isFile: -> stats.isFile
|
||||
isDirectory: -> stats.isDirectory
|
||||
isSymbolicLink: -> stats.isLink
|
||||
isBlockDevice: -> false
|
||||
isCharacterDevice: -> false
|
||||
isFIFO: -> false
|
||||
isSocket: -> false
|
||||
}
|
||||
|
||||
# Create a ENOENT error.
|
||||
notFoundError = (asarPath, filePath, callback) ->
|
||||
error = new Error("ENOENT, #{filePath} not found in #{asarPath}")
|
||||
error.code = "ENOENT"
|
||||
error.errno = -2
|
||||
unless typeof callback is 'function'
|
||||
throw error
|
||||
process.nextTick -> callback error
|
||||
|
||||
# Create a ENOTDIR error.
|
||||
notDirError = (callback) ->
|
||||
error = new Error('ENOTDIR, not a directory')
|
||||
error.code = 'ENOTDIR'
|
||||
error.errno = -20
|
||||
unless typeof callback is 'function'
|
||||
throw error
|
||||
process.nextTick -> callback error
|
||||
|
||||
# Create invalid archive error.
|
||||
invalidArchiveError = (asarPath, callback) ->
|
||||
error = new Error("Invalid package #{asarPath}")
|
||||
unless typeof callback is 'function'
|
||||
throw error
|
||||
process.nextTick -> callback error
|
||||
|
||||
# Override APIs that rely on passing file path instead of content to C++.
|
||||
overrideAPISync = (module, name, arg = 0) ->
|
||||
old = module[name]
|
||||
module[name] = ->
|
||||
p = arguments[arg]
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return old.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
invalidArchiveError asarPath unless archive
|
||||
|
||||
newPath = archive.copyFileOut filePath
|
||||
notFoundError asarPath, filePath unless newPath
|
||||
|
||||
arguments[arg] = newPath
|
||||
old.apply this, arguments
|
||||
|
||||
overrideAPI = (module, name, arg = 0) ->
|
||||
old = module[name]
|
||||
module[name] = ->
|
||||
p = arguments[arg]
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return old.apply this, arguments unless isAsar
|
||||
|
||||
callback = arguments[arguments.length - 1]
|
||||
return overrideAPISync module, name, arg unless typeof callback is 'function'
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return invalidArchiveError asarPath, callback unless archive
|
||||
|
||||
newPath = archive.copyFileOut filePath
|
||||
return notFoundError asarPath, filePath, callback unless newPath
|
||||
|
||||
arguments[arg] = newPath
|
||||
old.apply this, arguments
|
||||
|
||||
# Override fs APIs.
|
||||
exports.wrapFsWithAsar = (fs) ->
|
||||
lstatSync = fs.lstatSync
|
||||
fs.lstatSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return lstatSync p unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
invalidArchiveError asarPath unless archive
|
||||
|
||||
stats = archive.stat filePath
|
||||
notFoundError asarPath, filePath unless stats
|
||||
|
||||
asarStatsToFsStats stats
|
||||
|
||||
lstat = fs.lstat
|
||||
fs.lstat = (p, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return lstat p, callback unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return invalidArchiveError asarPath, callback unless archive
|
||||
|
||||
stats = getOrCreateArchive(asarPath).stat filePath
|
||||
return notFoundError asarPath, filePath, callback unless stats
|
||||
|
||||
process.nextTick -> callback null, asarStatsToFsStats stats
|
||||
|
||||
statSync = fs.statSync
|
||||
fs.statSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return statSync p unless isAsar
|
||||
|
||||
# Do not distinguish links for now.
|
||||
fs.lstatSync p
|
||||
|
||||
stat = fs.stat
|
||||
fs.stat = (p, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return stat p, callback unless isAsar
|
||||
|
||||
# Do not distinguish links for now.
|
||||
process.nextTick -> fs.lstat p, callback
|
||||
|
||||
statSyncNoException = fs.statSyncNoException
|
||||
fs.statSyncNoException = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return statSyncNoException p unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return false unless archive
|
||||
stats = archive.stat filePath
|
||||
return false unless stats
|
||||
asarStatsToFsStats stats
|
||||
|
||||
realpathSync = fs.realpathSync
|
||||
fs.realpathSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return realpathSync.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
invalidArchiveError asarPath unless archive
|
||||
|
||||
real = archive.realpath filePath
|
||||
notFoundError asarPath, filePath if real is false
|
||||
|
||||
path.join realpathSync(asarPath), real
|
||||
|
||||
realpath = fs.realpath
|
||||
fs.realpath = (p, cache, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return realpath.apply this, arguments unless isAsar
|
||||
|
||||
if typeof cache is 'function'
|
||||
callback = cache
|
||||
cache = undefined
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return invalidArchiveError asarPath, callback unless archive
|
||||
|
||||
real = archive.realpath filePath
|
||||
if real is false
|
||||
return notFoundError asarPath, filePath, callback
|
||||
|
||||
realpath asarPath, (err, p) ->
|
||||
return callback err if err
|
||||
callback null, path.join(p, real)
|
||||
|
||||
exists = fs.exists
|
||||
fs.exists = (p, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return exists p, callback unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return invalidArchiveError asarPath, callback unless archive
|
||||
|
||||
process.nextTick -> callback archive.stat(filePath) isnt false
|
||||
|
||||
existsSync = fs.existsSync
|
||||
fs.existsSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return existsSync p unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return false unless archive
|
||||
|
||||
archive.stat(filePath) isnt false
|
||||
|
||||
open = fs.open
|
||||
readFile = fs.readFile
|
||||
fs.readFile = (p, options, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return readFile.apply this, arguments unless isAsar
|
||||
|
||||
if typeof options is 'function'
|
||||
callback = options
|
||||
options = undefined
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return invalidArchiveError asarPath, callback unless archive
|
||||
|
||||
info = archive.getFileInfo filePath
|
||||
return notFoundError asarPath, filePath, callback unless info
|
||||
|
||||
if info.size is 0
|
||||
return process.nextTick -> callback null, new Buffer(0)
|
||||
|
||||
if info.unpacked
|
||||
realPath = archive.copyFileOut filePath
|
||||
return fs.readFile realPath, options, callback
|
||||
|
||||
if not options
|
||||
options = encoding: null
|
||||
else if util.isString options
|
||||
options = encoding: options
|
||||
else if not util.isObject options
|
||||
throw new TypeError('Bad arguments')
|
||||
|
||||
encoding = options.encoding
|
||||
|
||||
buffer = new Buffer(info.size)
|
||||
fd = archive.getFd()
|
||||
return notFoundError asarPath, filePath, callback unless fd >= 0
|
||||
|
||||
fs.read fd, buffer, 0, info.size, info.offset, (error) ->
|
||||
callback error, if encoding then buffer.toString encoding else buffer
|
||||
|
||||
openSync = fs.openSync
|
||||
readFileSync = fs.readFileSync
|
||||
fs.readFileSync = (p, opts) ->
|
||||
options = opts # this allows v8 to optimize this function
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return readFileSync.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
invalidArchiveError asarPath unless archive
|
||||
|
||||
info = archive.getFileInfo filePath
|
||||
notFoundError asarPath, filePath unless info
|
||||
|
||||
if info.size is 0
|
||||
return if options then '' else new Buffer(0)
|
||||
|
||||
if info.unpacked
|
||||
realPath = archive.copyFileOut filePath
|
||||
return fs.readFileSync realPath, options
|
||||
|
||||
if not options
|
||||
options = encoding: null
|
||||
else if util.isString options
|
||||
options = encoding: options
|
||||
else if not util.isObject options
|
||||
throw new TypeError('Bad arguments')
|
||||
|
||||
encoding = options.encoding
|
||||
|
||||
buffer = new Buffer(info.size)
|
||||
fd = archive.getFd()
|
||||
notFoundError asarPath, filePath unless fd >= 0
|
||||
|
||||
fs.readSync fd, buffer, 0, info.size, info.offset
|
||||
if encoding then buffer.toString encoding else buffer
|
||||
|
||||
readdir = fs.readdir
|
||||
fs.readdir = (p, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return readdir.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return invalidArchiveError asarPath, callback unless archive
|
||||
|
||||
files = archive.readdir filePath
|
||||
return notFoundError asarPath, filePath, callback unless files
|
||||
|
||||
process.nextTick -> callback null, files
|
||||
|
||||
readdirSync = fs.readdirSync
|
||||
fs.readdirSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return readdirSync.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
invalidArchiveError asarPath unless archive
|
||||
|
||||
files = archive.readdir filePath
|
||||
notFoundError asarPath, filePath unless files
|
||||
|
||||
files
|
||||
|
||||
internalModuleReadFile = process.binding('fs').internalModuleReadFile
|
||||
process.binding('fs').internalModuleReadFile = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return internalModuleReadFile p unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return undefined unless archive
|
||||
|
||||
info = archive.getFileInfo filePath
|
||||
return undefined unless info
|
||||
return '' if info.size is 0
|
||||
|
||||
if info.unpacked
|
||||
realPath = archive.copyFileOut filePath
|
||||
return fs.readFileSync realPath, encoding: 'utf8'
|
||||
|
||||
buffer = new Buffer(info.size)
|
||||
fd = archive.getFd()
|
||||
return undefined unless fd >= 0
|
||||
|
||||
fs.readSync fd, buffer, 0, info.size, info.offset
|
||||
buffer.toString 'utf8'
|
||||
|
||||
internalModuleStat = process.binding('fs').internalModuleStat
|
||||
process.binding('fs').internalModuleStat = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return internalModuleStat p unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return -34 unless archive # -ENOENT
|
||||
|
||||
stats = archive.stat filePath
|
||||
return -34 unless stats # -ENOENT
|
||||
|
||||
if stats.isDirectory then return 1 else return 0
|
||||
|
||||
# Calling mkdir for directory inside asar archive should throw ENOTDIR
|
||||
# error, but on Windows it throws ENOENT.
|
||||
# This is to work around the recursive looping bug of mkdirp since it is
|
||||
# widely used.
|
||||
if process.platform is 'win32'
|
||||
mkdir = fs.mkdir
|
||||
fs.mkdir = (p, mode, callback) ->
|
||||
callback = mode if typeof mode is 'function'
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return notDirError callback if isAsar and filePath.length
|
||||
mkdir p, mode, callback
|
||||
|
||||
mkdirSync = fs.mkdirSync
|
||||
fs.mkdirSync = (p, mode) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
notDirError() if isAsar and filePath.length
|
||||
mkdirSync p, mode
|
||||
|
||||
overrideAPI fs, 'open'
|
||||
overrideAPI child_process, 'execFile'
|
||||
overrideAPISync process, 'dlopen', 1
|
||||
overrideAPISync require('module')._extensions, '.node', 1
|
||||
overrideAPISync fs, 'openSync'
|
||||
overrideAPISync child_process, 'execFileSync'
|
610
atom/common/lib/asar.js
Normal file
610
atom/common/lib/asar.js
Normal file
|
@ -0,0 +1,610 @@
|
|||
(function () {
|
||||
var asar, asarStatsToFsStats, cachedArchives, child_process, fakeTime, getOrCreateArchive, gid, invalidArchiveError, nextInode, notDirError, notFoundError, overrideAPI, overrideAPISync, path, splitPath, uid, util,
|
||||
hasProp = {}.hasOwnProperty;
|
||||
|
||||
asar = process.binding('atom_common_asar');
|
||||
|
||||
child_process = require('child_process');
|
||||
|
||||
path = require('path');
|
||||
|
||||
util = require('util');
|
||||
|
||||
|
||||
/* Cache asar archive objects. */
|
||||
|
||||
cachedArchives = {};
|
||||
|
||||
getOrCreateArchive = function(p) {
|
||||
var archive;
|
||||
archive = cachedArchives[p];
|
||||
if (archive != null) {
|
||||
return archive;
|
||||
}
|
||||
archive = asar.createArchive(p);
|
||||
if (!archive) {
|
||||
return false;
|
||||
}
|
||||
return cachedArchives[p] = archive;
|
||||
};
|
||||
|
||||
|
||||
/* Clean cache on quit. */
|
||||
|
||||
process.on('exit', function() {
|
||||
var archive, p, results;
|
||||
results = [];
|
||||
for (p in cachedArchives) {
|
||||
if (!hasProp.call(cachedArchives, p)) continue;
|
||||
archive = cachedArchives[p];
|
||||
results.push(archive.destroy());
|
||||
}
|
||||
return results;
|
||||
});
|
||||
|
||||
|
||||
/* Separate asar package's path from full path. */
|
||||
|
||||
splitPath = function(p) {
|
||||
|
||||
/* shortcut to disable asar. */
|
||||
var index;
|
||||
if (process.noAsar) {
|
||||
return [false];
|
||||
}
|
||||
if (typeof p !== 'string') {
|
||||
return [false];
|
||||
}
|
||||
if (p.substr(-5) === '.asar') {
|
||||
return [true, p, ''];
|
||||
}
|
||||
p = path.normalize(p);
|
||||
index = p.lastIndexOf(".asar" + path.sep);
|
||||
if (index === -1) {
|
||||
return [false];
|
||||
}
|
||||
return [true, p.substr(0, index + 5), p.substr(index + 6)];
|
||||
};
|
||||
|
||||
|
||||
/* Convert asar archive's Stats object to fs's Stats object. */
|
||||
|
||||
nextInode = 0;
|
||||
|
||||
uid = process.getuid != null ? process.getuid() : 0;
|
||||
|
||||
gid = process.getgid != null ? process.getgid() : 0;
|
||||
|
||||
fakeTime = new Date();
|
||||
|
||||
asarStatsToFsStats = function(stats) {
|
||||
return {
|
||||
dev: 1,
|
||||
ino: ++nextInode,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
rdev: 0,
|
||||
atime: stats.atime || fakeTime,
|
||||
birthtime: stats.birthtime || fakeTime,
|
||||
mtime: stats.mtime || fakeTime,
|
||||
ctime: stats.ctime || fakeTime,
|
||||
size: stats.size,
|
||||
isFile: function() {
|
||||
return stats.isFile;
|
||||
},
|
||||
isDirectory: function() {
|
||||
return stats.isDirectory;
|
||||
},
|
||||
isSymbolicLink: function() {
|
||||
return stats.isLink;
|
||||
},
|
||||
isBlockDevice: function() {
|
||||
return false;
|
||||
},
|
||||
isCharacterDevice: function() {
|
||||
return false;
|
||||
},
|
||||
isFIFO: function() {
|
||||
return false;
|
||||
},
|
||||
isSocket: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* Create a ENOENT error. */
|
||||
|
||||
notFoundError = function(asarPath, filePath, callback) {
|
||||
var error;
|
||||
error = new Error("ENOENT, " + filePath + " not found in " + asarPath);
|
||||
error.code = "ENOENT";
|
||||
error.errno = -2;
|
||||
if (typeof callback !== 'function') {
|
||||
throw error;
|
||||
}
|
||||
return process.nextTick(function() {
|
||||
return callback(error);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Create a ENOTDIR error. */
|
||||
|
||||
notDirError = function(callback) {
|
||||
var error;
|
||||
error = new Error('ENOTDIR, not a directory');
|
||||
error.code = 'ENOTDIR';
|
||||
error.errno = -20;
|
||||
if (typeof callback !== 'function') {
|
||||
throw error;
|
||||
}
|
||||
return process.nextTick(function() {
|
||||
return callback(error);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Create invalid archive error. */
|
||||
|
||||
invalidArchiveError = function(asarPath, callback) {
|
||||
var error;
|
||||
error = new Error("Invalid package " + asarPath);
|
||||
if (typeof callback !== 'function') {
|
||||
throw error;
|
||||
}
|
||||
return process.nextTick(function() {
|
||||
return callback(error);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* Override APIs that rely on passing file path instead of content to C++. */
|
||||
|
||||
overrideAPISync = function(module, name, arg) {
|
||||
var old;
|
||||
if (arg == null) {
|
||||
arg = 0;
|
||||
}
|
||||
old = module[name];
|
||||
return module[name] = function() {
|
||||
var archive, asarPath, filePath, isAsar, newPath, p, ref;
|
||||
p = arguments[arg];
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return old.apply(this, arguments);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath);
|
||||
}
|
||||
newPath = archive.copyFileOut(filePath);
|
||||
if (!newPath) {
|
||||
notFoundError(asarPath, filePath);
|
||||
}
|
||||
arguments[arg] = newPath;
|
||||
return old.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
overrideAPI = function(module, name, arg) {
|
||||
var old;
|
||||
if (arg == null) {
|
||||
arg = 0;
|
||||
}
|
||||
old = module[name];
|
||||
return module[name] = function() {
|
||||
var archive, asarPath, callback, filePath, isAsar, newPath, p, ref;
|
||||
p = arguments[arg];
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return old.apply(this, arguments);
|
||||
}
|
||||
callback = arguments[arguments.length - 1];
|
||||
if (typeof callback !== 'function') {
|
||||
return overrideAPISync(module, name, arg);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback);
|
||||
}
|
||||
newPath = archive.copyFileOut(filePath);
|
||||
if (!newPath) {
|
||||
return notFoundError(asarPath, filePath, callback);
|
||||
}
|
||||
arguments[arg] = newPath;
|
||||
return old.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* Override fs APIs. */
|
||||
|
||||
exports.wrapFsWithAsar = function(fs) {
|
||||
var exists, existsSync, internalModuleReadFile, internalModuleStat, lstat, lstatSync, mkdir, mkdirSync, open, openSync, readFile, readFileSync, readdir, readdirSync, realpath, realpathSync, stat, statSync, statSyncNoException;
|
||||
lstatSync = fs.lstatSync;
|
||||
fs.lstatSync = function(p) {
|
||||
var archive, asarPath, filePath, isAsar, ref, stats;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return lstatSync(p);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath);
|
||||
}
|
||||
stats = archive.stat(filePath);
|
||||
if (!stats) {
|
||||
notFoundError(asarPath, filePath);
|
||||
}
|
||||
return asarStatsToFsStats(stats);
|
||||
};
|
||||
lstat = fs.lstat;
|
||||
fs.lstat = function(p, callback) {
|
||||
var archive, asarPath, filePath, isAsar, ref, stats;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return lstat(p, callback);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback);
|
||||
}
|
||||
stats = getOrCreateArchive(asarPath).stat(filePath);
|
||||
if (!stats) {
|
||||
return notFoundError(asarPath, filePath, callback);
|
||||
}
|
||||
return process.nextTick(function() {
|
||||
return callback(null, asarStatsToFsStats(stats));
|
||||
});
|
||||
};
|
||||
statSync = fs.statSync;
|
||||
fs.statSync = function(p) {
|
||||
var asarPath, filePath, isAsar, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return statSync(p);
|
||||
}
|
||||
|
||||
/* Do not distinguish links for now. */
|
||||
return fs.lstatSync(p);
|
||||
};
|
||||
stat = fs.stat;
|
||||
fs.stat = function(p, callback) {
|
||||
var asarPath, filePath, isAsar, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return stat(p, callback);
|
||||
}
|
||||
|
||||
/* Do not distinguish links for now. */
|
||||
return process.nextTick(function() {
|
||||
return fs.lstat(p, callback);
|
||||
});
|
||||
};
|
||||
statSyncNoException = fs.statSyncNoException;
|
||||
fs.statSyncNoException = function(p) {
|
||||
var archive, asarPath, filePath, isAsar, ref, stats;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return statSyncNoException(p);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return false;
|
||||
}
|
||||
stats = archive.stat(filePath);
|
||||
if (!stats) {
|
||||
return false;
|
||||
}
|
||||
return asarStatsToFsStats(stats);
|
||||
};
|
||||
realpathSync = fs.realpathSync;
|
||||
fs.realpathSync = function(p) {
|
||||
var archive, asarPath, filePath, isAsar, real, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return realpathSync.apply(this, arguments);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath);
|
||||
}
|
||||
real = archive.realpath(filePath);
|
||||
if (real === false) {
|
||||
notFoundError(asarPath, filePath);
|
||||
}
|
||||
return path.join(realpathSync(asarPath), real);
|
||||
};
|
||||
realpath = fs.realpath;
|
||||
fs.realpath = function(p, cache, callback) {
|
||||
var archive, asarPath, filePath, isAsar, real, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return realpath.apply(this, arguments);
|
||||
}
|
||||
if (typeof cache === 'function') {
|
||||
callback = cache;
|
||||
cache = void 0;
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback);
|
||||
}
|
||||
real = archive.realpath(filePath);
|
||||
if (real === false) {
|
||||
return notFoundError(asarPath, filePath, callback);
|
||||
}
|
||||
return realpath(asarPath, function(err, p) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null, path.join(p, real));
|
||||
});
|
||||
};
|
||||
exists = fs.exists;
|
||||
fs.exists = function(p, callback) {
|
||||
var archive, asarPath, filePath, isAsar, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return exists(p, callback);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback);
|
||||
}
|
||||
return process.nextTick(function() {
|
||||
return callback(archive.stat(filePath) !== false);
|
||||
});
|
||||
};
|
||||
existsSync = fs.existsSync;
|
||||
fs.existsSync = function(p) {
|
||||
var archive, asarPath, filePath, isAsar, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return existsSync(p);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return false;
|
||||
}
|
||||
return archive.stat(filePath) !== false;
|
||||
};
|
||||
open = fs.open;
|
||||
readFile = fs.readFile;
|
||||
fs.readFile = function(p, options, callback) {
|
||||
var archive, asarPath, buffer, encoding, fd, filePath, info, isAsar, realPath, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return readFile.apply(this, arguments);
|
||||
}
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = void 0;
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback);
|
||||
}
|
||||
info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
return notFoundError(asarPath, filePath, callback);
|
||||
}
|
||||
if (info.size === 0) {
|
||||
return process.nextTick(function() {
|
||||
return callback(null, new Buffer(0));
|
||||
});
|
||||
}
|
||||
if (info.unpacked) {
|
||||
realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFile(realPath, options, callback);
|
||||
}
|
||||
if (!options) {
|
||||
options = {
|
||||
encoding: null
|
||||
};
|
||||
} else if (util.isString(options)) {
|
||||
options = {
|
||||
encoding: options
|
||||
};
|
||||
} else if (!util.isObject(options)) {
|
||||
throw new TypeError('Bad arguments');
|
||||
}
|
||||
encoding = options.encoding;
|
||||
buffer = new Buffer(info.size);
|
||||
fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
return notFoundError(asarPath, filePath, callback);
|
||||
}
|
||||
return fs.read(fd, buffer, 0, info.size, info.offset, function(error) {
|
||||
return callback(error, encoding ? buffer.toString(encoding) : buffer);
|
||||
});
|
||||
};
|
||||
openSync = fs.openSync;
|
||||
readFileSync = fs.readFileSync;
|
||||
fs.readFileSync = function(p, opts) {
|
||||
|
||||
/* this allows v8 to optimize this function */
|
||||
var archive, asarPath, buffer, encoding, fd, filePath, info, isAsar, options, realPath, ref;
|
||||
options = opts;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return readFileSync.apply(this, arguments);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath);
|
||||
}
|
||||
info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
notFoundError(asarPath, filePath);
|
||||
}
|
||||
if (info.size === 0) {
|
||||
if (options) {
|
||||
return '';
|
||||
} else {
|
||||
return new Buffer(0);
|
||||
}
|
||||
}
|
||||
if (info.unpacked) {
|
||||
realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFileSync(realPath, options);
|
||||
}
|
||||
if (!options) {
|
||||
options = {
|
||||
encoding: null
|
||||
};
|
||||
} else if (util.isString(options)) {
|
||||
options = {
|
||||
encoding: options
|
||||
};
|
||||
} else if (!util.isObject(options)) {
|
||||
throw new TypeError('Bad arguments');
|
||||
}
|
||||
encoding = options.encoding;
|
||||
buffer = new Buffer(info.size);
|
||||
fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
notFoundError(asarPath, filePath);
|
||||
}
|
||||
fs.readSync(fd, buffer, 0, info.size, info.offset);
|
||||
if (encoding) {
|
||||
return buffer.toString(encoding);
|
||||
} else {
|
||||
return buffer;
|
||||
}
|
||||
};
|
||||
readdir = fs.readdir;
|
||||
fs.readdir = function(p, callback) {
|
||||
var archive, asarPath, filePath, files, isAsar, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return readdir.apply(this, arguments);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return invalidArchiveError(asarPath, callback);
|
||||
}
|
||||
files = archive.readdir(filePath);
|
||||
if (!files) {
|
||||
return notFoundError(asarPath, filePath, callback);
|
||||
}
|
||||
return process.nextTick(function() {
|
||||
return callback(null, files);
|
||||
});
|
||||
};
|
||||
readdirSync = fs.readdirSync;
|
||||
fs.readdirSync = function(p) {
|
||||
var archive, asarPath, filePath, files, isAsar, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return readdirSync.apply(this, arguments);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
invalidArchiveError(asarPath);
|
||||
}
|
||||
files = archive.readdir(filePath);
|
||||
if (!files) {
|
||||
notFoundError(asarPath, filePath);
|
||||
}
|
||||
return files;
|
||||
};
|
||||
internalModuleReadFile = process.binding('fs').internalModuleReadFile;
|
||||
process.binding('fs').internalModuleReadFile = function(p) {
|
||||
var archive, asarPath, buffer, fd, filePath, info, isAsar, realPath, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return internalModuleReadFile(p);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
if (!archive) {
|
||||
return void 0;
|
||||
}
|
||||
info = archive.getFileInfo(filePath);
|
||||
if (!info) {
|
||||
return void 0;
|
||||
}
|
||||
if (info.size === 0) {
|
||||
return '';
|
||||
}
|
||||
if (info.unpacked) {
|
||||
realPath = archive.copyFileOut(filePath);
|
||||
return fs.readFileSync(realPath, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
}
|
||||
buffer = new Buffer(info.size);
|
||||
fd = archive.getFd();
|
||||
if (!(fd >= 0)) {
|
||||
return void 0;
|
||||
}
|
||||
fs.readSync(fd, buffer, 0, info.size, info.offset);
|
||||
return buffer.toString('utf8');
|
||||
};
|
||||
internalModuleStat = process.binding('fs').internalModuleStat;
|
||||
process.binding('fs').internalModuleStat = function(p) {
|
||||
var archive, asarPath, filePath, isAsar, ref, stats;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (!isAsar) {
|
||||
return internalModuleStat(p);
|
||||
}
|
||||
archive = getOrCreateArchive(asarPath);
|
||||
|
||||
/* -ENOENT */
|
||||
if (!archive) {
|
||||
return -34;
|
||||
}
|
||||
stats = archive.stat(filePath);
|
||||
|
||||
/* -ENOENT */
|
||||
if (!stats) {
|
||||
return -34;
|
||||
}
|
||||
if (stats.isDirectory) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Calling mkdir for directory inside asar archive should throw ENOTDIR
|
||||
error, but on Windows it throws ENOENT.
|
||||
This is to work around the recursive looping bug of mkdirp since it is
|
||||
widely used.
|
||||
*/
|
||||
if (process.platform === 'win32') {
|
||||
mkdir = fs.mkdir;
|
||||
fs.mkdir = function(p, mode, callback) {
|
||||
var asarPath, filePath, isAsar, ref;
|
||||
if (typeof mode === 'function') {
|
||||
callback = mode;
|
||||
}
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (isAsar && filePath.length) {
|
||||
return notDirError(callback);
|
||||
}
|
||||
return mkdir(p, mode, callback);
|
||||
};
|
||||
mkdirSync = fs.mkdirSync;
|
||||
fs.mkdirSync = function(p, mode) {
|
||||
var asarPath, filePath, isAsar, ref;
|
||||
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
|
||||
if (isAsar && filePath.length) {
|
||||
notDirError();
|
||||
}
|
||||
return mkdirSync(p, mode);
|
||||
};
|
||||
}
|
||||
overrideAPI(fs, 'open');
|
||||
overrideAPI(child_process, 'execFile');
|
||||
overrideAPISync(process, 'dlopen', 1);
|
||||
overrideAPISync(require('module')._extensions, '.node', 1);
|
||||
overrideAPISync(fs, 'openSync');
|
||||
return overrideAPISync(child_process, 'execFileSync');
|
||||
};
|
||||
})()
|
|
@ -1,22 +0,0 @@
|
|||
return (process, require, asarSource) ->
|
||||
{createArchive} = process.binding 'atom_common_asar'
|
||||
|
||||
# Make asar.coffee accessible via "require".
|
||||
process.binding('natives').ATOM_SHELL_ASAR = asarSource
|
||||
|
||||
# Monkey-patch the fs module.
|
||||
require('ATOM_SHELL_ASAR').wrapFsWithAsar require('fs')
|
||||
|
||||
# Make graceful-fs work with asar.
|
||||
source = process.binding 'natives'
|
||||
source['original-fs'] = source.fs
|
||||
source['fs'] = """
|
||||
var src = '(function (exports, require, module, __filename, __dirname) { ' +
|
||||
process.binding('natives')['original-fs'] +
|
||||
' });';
|
||||
var vm = require('vm');
|
||||
var fn = vm.runInThisContext(src, { filename: 'fs.js' });
|
||||
fn(exports, require, module);
|
||||
var asar = require('ATOM_SHELL_ASAR');
|
||||
asar.wrapFsWithAsar(exports);
|
||||
"""
|
17
atom/common/lib/asar_init.js
Normal file
17
atom/common/lib/asar_init.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
(function () {
|
||||
return function(process, require, asarSource) {
|
||||
var createArchive, source;
|
||||
createArchive = process.binding('atom_common_asar').createArchive;
|
||||
|
||||
/* Make asar.coffee accessible via "require". */
|
||||
process.binding('natives').ATOM_SHELL_ASAR = asarSource;
|
||||
|
||||
/* Monkey-patch the fs module. */
|
||||
require('ATOM_SHELL_ASAR').wrapFsWithAsar(require('fs'));
|
||||
|
||||
/* Make graceful-fs work with asar. */
|
||||
source = process.binding('natives');
|
||||
source['original-fs'] = source.fs;
|
||||
return source['fs'] = "var src = '(function (exports, require, module, __filename, __dirname) { ' +\n process.binding('natives')['original-fs'] +\n ' });';\nvar vm = require('vm');\nvar fn = vm.runInThisContext(src, { filename: 'fs.js' });\nfn(exports, require, module);\nvar asar = require('ATOM_SHELL_ASAR');\nasar.wrapFsWithAsar(exports);";
|
||||
};
|
||||
})()
|
|
@ -1,36 +0,0 @@
|
|||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
timers = require 'timers'
|
||||
Module = require 'module'
|
||||
|
||||
process.atomBinding = (name) ->
|
||||
try
|
||||
process.binding "atom_#{process.type}_#{name}"
|
||||
catch e
|
||||
process.binding "atom_common_#{name}" if /No such module/.test e.message
|
||||
|
||||
unless process.env.ELECTRON_HIDE_INTERNAL_MODULES
|
||||
# Add common/api/lib to module search paths.
|
||||
Module.globalPaths.push path.resolve(__dirname, '..', 'api', 'lib')
|
||||
|
||||
# setImmediate and process.nextTick makes use of uv_check and uv_prepare to
|
||||
# run the callbacks, however since we only run uv loop on requests, the
|
||||
# callbacks wouldn't be called until something else activated the uv loop,
|
||||
# which would delay the callbacks for arbitrary long time. So we should
|
||||
# initiatively activate the uv loop once setImmediate and process.nextTick is
|
||||
# called.
|
||||
wrapWithActivateUvLoop = (func) ->
|
||||
->
|
||||
process.activateUvLoop()
|
||||
func.apply this, arguments
|
||||
process.nextTick = wrapWithActivateUvLoop process.nextTick
|
||||
global.setImmediate = wrapWithActivateUvLoop timers.setImmediate
|
||||
global.clearImmediate = timers.clearImmediate
|
||||
|
||||
if process.type is 'browser'
|
||||
# setTimeout needs to update the polling timeout of the event loop, when
|
||||
# called under Chromium's event loop the node's event loop won't get a chance
|
||||
# to update the timeout, so we have to force the node's event loop to
|
||||
# recalculate the timeout in browser process.
|
||||
global.setTimeout = wrapWithActivateUvLoop timers.setTimeout
|
||||
global.setInterval = wrapWithActivateUvLoop timers.setInterval
|
62
atom/common/lib/init.js
Normal file
62
atom/common/lib/init.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
var Module, fs, path, timers, wrapWithActivateUvLoop;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
timers = require('timers');
|
||||
|
||||
Module = require('module');
|
||||
|
||||
process.atomBinding = function(name) {
|
||||
var e, error;
|
||||
try {
|
||||
return process.binding("atom_" + process.type + "_" + name);
|
||||
} catch (error) {
|
||||
e = error;
|
||||
if (/No such module/.test(e.message)) {
|
||||
return process.binding("atom_common_" + name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!process.env.ELECTRON_HIDE_INTERNAL_MODULES) {
|
||||
|
||||
/* Add common/api/lib to module search paths. */
|
||||
Module.globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib'));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
setImmediate and process.nextTick makes use of uv_check and uv_prepare to
|
||||
run the callbacks, however since we only run uv loop on requests, the
|
||||
callbacks wouldn't be called until something else activated the uv loop,
|
||||
which would delay the callbacks for arbitrary long time. So we should
|
||||
initiatively activate the uv loop once setImmediate and process.nextTick is
|
||||
called.
|
||||
*/
|
||||
|
||||
wrapWithActivateUvLoop = function(func) {
|
||||
return function() {
|
||||
process.activateUvLoop();
|
||||
return func.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
process.nextTick = wrapWithActivateUvLoop(process.nextTick);
|
||||
|
||||
global.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
|
||||
|
||||
global.clearImmediate = timers.clearImmediate;
|
||||
|
||||
if (process.type === 'browser') {
|
||||
|
||||
/*
|
||||
setTimeout needs to update the polling timeout of the event loop, when
|
||||
called under Chromium's event loop the node's event loop won't get a chance
|
||||
to update the timeout, so we have to force the node's event loop to
|
||||
recalculate the timeout in browser process.
|
||||
*/
|
||||
global.setTimeout = wrapWithActivateUvLoop(timers.setTimeout);
|
||||
global.setInterval = wrapWithActivateUvLoop(timers.setInterval);
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
path = require 'path'
|
||||
Module = require 'module'
|
||||
|
||||
# Clear Node's global search paths.
|
||||
Module.globalPaths.length = 0
|
||||
|
||||
# Clear current and parent(init.coffee)'s search paths.
|
||||
module.paths = []
|
||||
module.parent.paths = []
|
||||
|
||||
# Prevent Node from adding paths outside this app to search paths.
|
||||
Module._nodeModulePaths = (from) ->
|
||||
from = path.resolve from
|
||||
|
||||
# If "from" is outside the app then we do nothing.
|
||||
skipOutsidePaths = from.startsWith process.resourcesPath
|
||||
|
||||
# Following logoic is copied from module.js.
|
||||
splitRe = if process.platform is 'win32' then /[\/\\]/ else /\//
|
||||
paths = []
|
||||
|
||||
parts = from.split splitRe
|
||||
for part, tip in parts by -1
|
||||
continue if part is 'node_modules'
|
||||
dir = parts.slice(0, tip + 1).join path.sep
|
||||
break if skipOutsidePaths and not dir.startsWith process.resourcesPath
|
||||
paths.push path.join(dir, 'node_modules')
|
||||
|
||||
paths
|
45
atom/common/lib/reset-search-paths.js
Normal file
45
atom/common/lib/reset-search-paths.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
var Module, path;
|
||||
|
||||
path = require('path');
|
||||
|
||||
Module = require('module');
|
||||
|
||||
|
||||
/* Clear Node's global search paths. */
|
||||
|
||||
Module.globalPaths.length = 0;
|
||||
|
||||
|
||||
/* Clear current and parent(init.coffee)'s search paths. */
|
||||
|
||||
module.paths = [];
|
||||
|
||||
module.parent.paths = [];
|
||||
|
||||
|
||||
/* Prevent Node from adding paths outside this app to search paths. */
|
||||
|
||||
Module._nodeModulePaths = function(from) {
|
||||
var dir, i, part, parts, paths, skipOutsidePaths, splitRe, tip;
|
||||
from = path.resolve(from);
|
||||
|
||||
/* If "from" is outside the app then we do nothing. */
|
||||
skipOutsidePaths = from.startsWith(process.resourcesPath);
|
||||
|
||||
/* Following logoic is copied from module.js. */
|
||||
splitRe = process.platform === 'win32' ? /[\/\\]/ : /\//;
|
||||
paths = [];
|
||||
parts = from.split(splitRe);
|
||||
for (tip = i = parts.length - 1; i >= 0; tip = i += -1) {
|
||||
part = parts[tip];
|
||||
if (part === 'node_modules') {
|
||||
continue;
|
||||
}
|
||||
dir = parts.slice(0, tip + 1).join(path.sep);
|
||||
if (skipOutsidePaths && !dir.startsWith(process.resourcesPath)) {
|
||||
break;
|
||||
}
|
||||
paths.push(path.join(dir, 'node_modules'));
|
||||
}
|
||||
return paths;
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
{ipcRenderer, nativeImage} = require 'electron'
|
||||
|
||||
nextId = 0
|
||||
getNextId = -> ++nextId
|
||||
|
||||
# |options.type| can not be empty and has to include 'window' or 'screen'.
|
||||
isValid = (options) ->
|
||||
return options?.types? and Array.isArray options.types
|
||||
|
||||
exports.getSources = (options, callback) ->
|
||||
return callback new Error('Invalid options') unless isValid options
|
||||
|
||||
captureWindow = 'window' in options.types
|
||||
captureScreen = 'screen' in options.types
|
||||
options.thumbnailSize ?= width: 150, height: 150
|
||||
|
||||
id = getNextId()
|
||||
ipcRenderer.send 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id
|
||||
ipcRenderer.once "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{id}", (event, sources) ->
|
||||
callback null, ({id: source.id, name: source.name, thumbnail: nativeImage.createFromDataURL source.thumbnail} for source in sources)
|
50
atom/renderer/api/lib/desktop-capturer.js
Normal file
50
atom/renderer/api/lib/desktop-capturer.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
var getNextId, ipcRenderer, isValid, nativeImage, nextId, ref,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
ref = require('electron'), ipcRenderer = ref.ipcRenderer, nativeImage = ref.nativeImage;
|
||||
|
||||
nextId = 0;
|
||||
|
||||
getNextId = function() {
|
||||
return ++nextId;
|
||||
};
|
||||
|
||||
|
||||
/* |options.type| can not be empty and has to include 'window' or 'screen'. */
|
||||
|
||||
isValid = function(options) {
|
||||
return ((options != null ? options.types : void 0) != null) && Array.isArray(options.types);
|
||||
};
|
||||
|
||||
exports.getSources = function(options, callback) {
|
||||
var captureScreen, captureWindow, id;
|
||||
if (!isValid(options)) {
|
||||
return callback(new Error('Invalid options'));
|
||||
}
|
||||
captureWindow = indexOf.call(options.types, 'window') >= 0;
|
||||
captureScreen = indexOf.call(options.types, 'screen') >= 0;
|
||||
if (options.thumbnailSize == null) {
|
||||
options.thumbnailSize = {
|
||||
width: 150,
|
||||
height: 150
|
||||
};
|
||||
}
|
||||
id = getNextId();
|
||||
ipcRenderer.send('ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id);
|
||||
return ipcRenderer.once("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + id, function(event, sources) {
|
||||
var source;
|
||||
return callback(null, (function() {
|
||||
var i, len, results;
|
||||
results = [];
|
||||
for (i = 0, len = sources.length; i < len; i++) {
|
||||
source = sources[i];
|
||||
results.push({
|
||||
id: source.id,
|
||||
name: source.name,
|
||||
thumbnail: nativeImage.createFromDataURL(source.thumbnail)
|
||||
});
|
||||
}
|
||||
return results;
|
||||
})());
|
||||
});
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
common = require '../../../../common/api/lib/exports/electron'
|
||||
|
||||
# Import common modules.
|
||||
common.defineProperties exports
|
||||
|
||||
Object.defineProperties exports,
|
||||
# Renderer side modules, please sort with alphabet order.
|
||||
desktopCapturer:
|
||||
enumerable: true
|
||||
get: -> require '../desktop-capturer'
|
||||
ipcRenderer:
|
||||
enumerable: true
|
||||
get: -> require '../ipc-renderer'
|
||||
remote:
|
||||
enumerable: true
|
||||
get: -> require '../remote'
|
||||
screen:
|
||||
enumerable: true
|
||||
get: -> require '../screen'
|
||||
webFrame:
|
||||
enumerable: true
|
||||
get: -> require '../web-frame'
|
43
atom/renderer/api/lib/exports/electron.js
Normal file
43
atom/renderer/api/lib/exports/electron.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
var common;
|
||||
|
||||
common = require('../../../../common/api/lib/exports/electron');
|
||||
|
||||
|
||||
/* Import common modules. */
|
||||
|
||||
common.defineProperties(exports);
|
||||
|
||||
Object.defineProperties(exports, {
|
||||
|
||||
/* Renderer side modules, please sort with alphabet order. */
|
||||
desktopCapturer: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../desktop-capturer');
|
||||
}
|
||||
},
|
||||
ipcRenderer: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../ipc-renderer');
|
||||
}
|
||||
},
|
||||
remote: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../remote');
|
||||
}
|
||||
},
|
||||
screen: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../screen');
|
||||
}
|
||||
},
|
||||
webFrame: {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return require('../web-frame');
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
{EventEmitter} = require 'events'
|
||||
|
||||
binding = process.atomBinding 'ipc'
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
# Created by init.coffee.
|
||||
ipcRenderer = v8Util.getHiddenValue global, 'ipc'
|
||||
|
||||
ipcRenderer.send = (args...) ->
|
||||
binding.send 'ipc-message', [args...]
|
||||
|
||||
ipcRenderer.sendSync = (args...) ->
|
||||
JSON.parse binding.sendSync('ipc-message-sync', [args...])
|
||||
|
||||
ipcRenderer.sendToHost = (args...) ->
|
||||
binding.send 'ipc-message-host', [args...]
|
||||
|
||||
module.exports = ipcRenderer
|
33
atom/renderer/api/lib/ipc-renderer.js
Normal file
33
atom/renderer/api/lib/ipc-renderer.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
var EventEmitter, binding, ipcRenderer, v8Util,
|
||||
slice = [].slice;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
binding = process.atomBinding('ipc');
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
|
||||
/* Created by init.coffee. */
|
||||
|
||||
ipcRenderer = v8Util.getHiddenValue(global, 'ipc');
|
||||
|
||||
ipcRenderer.send = function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return binding.send('ipc-message', slice.call(args));
|
||||
};
|
||||
|
||||
ipcRenderer.sendSync = function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return JSON.parse(binding.sendSync('ipc-message-sync', slice.call(args)));
|
||||
};
|
||||
|
||||
ipcRenderer.sendToHost = function() {
|
||||
var args;
|
||||
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
||||
return binding.send('ipc-message-host', slice.call(args));
|
||||
};
|
||||
|
||||
module.exports = ipcRenderer;
|
|
@ -1,19 +0,0 @@
|
|||
{ipcRenderer, deprecate} = require 'electron'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
# This module is deprecated, we mirror everything from ipcRenderer.
|
||||
deprecate.warn 'ipc module', 'require("electron").ipcRenderer'
|
||||
|
||||
# Routes events of ipcRenderer.
|
||||
ipc = new EventEmitter
|
||||
ipcRenderer.emit = (channel, event, args...) ->
|
||||
ipc.emit channel, args...
|
||||
EventEmitter::emit.apply ipcRenderer, arguments
|
||||
|
||||
# Deprecated.
|
||||
for method of ipcRenderer when method.startsWith 'send'
|
||||
ipc[method] = ipcRenderer[method]
|
||||
deprecate.rename ipc, 'sendChannel', 'send'
|
||||
deprecate.rename ipc, 'sendChannelSync', 'sendSync'
|
||||
|
||||
module.exports = ipc
|
38
atom/renderer/api/lib/ipc.js
Normal file
38
atom/renderer/api/lib/ipc.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
var EventEmitter, deprecate, ipc, ipcRenderer, method, ref,
|
||||
slice = [].slice;
|
||||
|
||||
ref = require('electron'), ipcRenderer = ref.ipcRenderer, deprecate = ref.deprecate;
|
||||
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
|
||||
/* This module is deprecated, we mirror everything from ipcRenderer. */
|
||||
|
||||
deprecate.warn('ipc module', 'require("electron").ipcRenderer');
|
||||
|
||||
|
||||
/* Routes events of ipcRenderer. */
|
||||
|
||||
ipc = new EventEmitter;
|
||||
|
||||
ipcRenderer.emit = function() {
|
||||
var args, channel, event;
|
||||
channel = arguments[0], event = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
|
||||
ipc.emit.apply(ipc, [channel].concat(slice.call(args)));
|
||||
return EventEmitter.prototype.emit.apply(ipcRenderer, arguments);
|
||||
};
|
||||
|
||||
|
||||
/* Deprecated. */
|
||||
|
||||
for (method in ipcRenderer) {
|
||||
if (method.startsWith('send')) {
|
||||
ipc[method] = ipcRenderer[method];
|
||||
}
|
||||
}
|
||||
|
||||
deprecate.rename(ipc, 'sendChannel', 'send');
|
||||
|
||||
deprecate.rename(ipc, 'sendChannelSync', 'sendSync');
|
||||
|
||||
module.exports = ipc;
|
|
@ -1,199 +0,0 @@
|
|||
{ipcRenderer, CallbacksRegistry} = require 'electron'
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
callbacksRegistry = new CallbacksRegistry
|
||||
|
||||
# Check for circular reference.
|
||||
isCircular = (field, visited) ->
|
||||
if typeof field is 'object'
|
||||
if field in visited
|
||||
return true
|
||||
visited.push field
|
||||
return false
|
||||
|
||||
# Convert the arguments object into an array of meta data.
|
||||
wrapArgs = (args, visited=[]) ->
|
||||
valueToMeta = (value) ->
|
||||
if Array.isArray value
|
||||
type: 'array', value: wrapArgs(value, visited)
|
||||
else if Buffer.isBuffer value
|
||||
type: 'buffer', value: Array::slice.call(value, 0)
|
||||
else if value instanceof Date
|
||||
type: 'date', value: value.getTime()
|
||||
else if value?.constructor.name is 'Promise'
|
||||
type: 'promise', then: valueToMeta(value.then.bind(value))
|
||||
else if value? and typeof value is 'object' and v8Util.getHiddenValue value, 'atomId'
|
||||
type: 'remote-object', id: v8Util.getHiddenValue value, 'atomId'
|
||||
else if value? and typeof value is 'object'
|
||||
ret = type: 'object', name: value.constructor.name, members: []
|
||||
for prop, field of value
|
||||
ret.members.push
|
||||
name: prop
|
||||
value: valueToMeta(if isCircular(field, visited) then null else field)
|
||||
ret
|
||||
else if typeof value is 'function' and v8Util.getHiddenValue value, 'returnValue'
|
||||
type: 'function-with-return-value', value: valueToMeta(value())
|
||||
else if typeof value is 'function'
|
||||
type: 'function', id: callbacksRegistry.add(value), location: v8Util.getHiddenValue value, 'location'
|
||||
else
|
||||
type: 'value', value: value
|
||||
|
||||
Array::slice.call(args).map valueToMeta
|
||||
|
||||
# Convert meta data from browser into real value.
|
||||
metaToValue = (meta) ->
|
||||
switch meta.type
|
||||
when 'value' then meta.value
|
||||
when 'array' then (metaToValue(el) for el in meta.members)
|
||||
when 'buffer' then new Buffer(meta.value)
|
||||
when 'promise' then Promise.resolve(then: metaToValue(meta.then))
|
||||
when 'error' then metaToPlainObject meta
|
||||
when 'date' then new Date(meta.value)
|
||||
when 'exception'
|
||||
throw new Error("#{meta.message}\n#{meta.stack}")
|
||||
else
|
||||
if meta.type is 'function'
|
||||
# A shadow class to represent the remote function object.
|
||||
ret =
|
||||
class RemoteFunction
|
||||
constructor: ->
|
||||
if @constructor == RemoteFunction
|
||||
# Constructor call.
|
||||
obj = ipcRenderer.sendSync 'ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments)
|
||||
|
||||
# Returning object in constructor will replace constructed object
|
||||
# with the returned object.
|
||||
# http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
|
||||
return metaToValue obj
|
||||
else
|
||||
# Function call.
|
||||
obj = ipcRenderer.sendSync 'ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments)
|
||||
return metaToValue obj
|
||||
else
|
||||
ret = v8Util.createObjectWithName meta.name
|
||||
|
||||
# Polulate delegate members.
|
||||
for member in meta.members
|
||||
if member.type is 'function'
|
||||
ret[member.name] = createRemoteMemberFunction meta.id, member.name
|
||||
else
|
||||
Object.defineProperty ret, member.name, createRemoteMemberProperty(meta.id, member.name)
|
||||
|
||||
# Track delegate object's life time, and tell the browser to clean up
|
||||
# when the object is GCed.
|
||||
v8Util.setDestructor ret, ->
|
||||
ipcRenderer.send 'ATOM_BROWSER_DEREFERENCE', meta.id
|
||||
|
||||
# Remember object's id.
|
||||
v8Util.setHiddenValue ret, 'atomId', meta.id
|
||||
|
||||
ret
|
||||
|
||||
# Construct a plain object from the meta.
|
||||
metaToPlainObject = (meta) ->
|
||||
obj = switch meta.type
|
||||
when 'error' then new Error
|
||||
else {}
|
||||
obj[name] = value for {name, value} in meta.members
|
||||
obj
|
||||
|
||||
# Create a RemoteMemberFunction instance.
|
||||
# This function's content should not be inlined into metaToValue, otherwise V8
|
||||
# may consider it circular reference.
|
||||
createRemoteMemberFunction = (metaId, name) ->
|
||||
class RemoteMemberFunction
|
||||
constructor: ->
|
||||
if @constructor is RemoteMemberFunction
|
||||
# Constructor call.
|
||||
ret = ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', metaId, name, wrapArgs(arguments)
|
||||
return metaToValue ret
|
||||
else
|
||||
# Call member function.
|
||||
ret = ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_CALL', metaId, name, wrapArgs(arguments)
|
||||
return metaToValue ret
|
||||
|
||||
# Create configuration for defineProperty.
|
||||
# This function's content should not be inlined into metaToValue, otherwise V8
|
||||
# may consider it circular reference.
|
||||
createRemoteMemberProperty = (metaId, name) ->
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
set: (value) ->
|
||||
# Set member data.
|
||||
ipcRenderer.sendSync 'ATOM_BROWSER_MEMBER_SET', metaId, name, value
|
||||
value
|
||||
get: ->
|
||||
# Get member data.
|
||||
metaToValue ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_GET', metaId, name)
|
||||
|
||||
# Browser calls a callback in renderer.
|
||||
ipcRenderer.on 'ATOM_RENDERER_CALLBACK', (event, id, args) ->
|
||||
callbacksRegistry.apply id, metaToValue(args)
|
||||
|
||||
# A callback in browser is released.
|
||||
ipcRenderer.on 'ATOM_RENDERER_RELEASE_CALLBACK', (event, id) ->
|
||||
callbacksRegistry.remove id
|
||||
|
||||
# List all built-in modules in browser process.
|
||||
browserModules = require '../../../browser/api/lib/exports/electron'
|
||||
# And add a helper receiver for each one.
|
||||
for name of browserModules
|
||||
do (name) ->
|
||||
Object.defineProperty exports, name, get: -> exports.getBuiltin name
|
||||
|
||||
# Get remote module.
|
||||
# (Just like node's require, the modules are cached permanently, note that this
|
||||
# is safe leak since the object is not expected to get freed in browser)
|
||||
moduleCache = {}
|
||||
exports.require = (module) ->
|
||||
return moduleCache[module] if moduleCache[module]?
|
||||
|
||||
meta = ipcRenderer.sendSync 'ATOM_BROWSER_REQUIRE', module
|
||||
moduleCache[module] = metaToValue meta
|
||||
|
||||
# Optimize require('electron').
|
||||
moduleCache.electron = exports
|
||||
|
||||
# Alias to remote.require('electron').xxx.
|
||||
builtinCache = {}
|
||||
exports.getBuiltin = (module) ->
|
||||
return builtinCache[module] if builtinCache[module]?
|
||||
|
||||
meta = ipcRenderer.sendSync 'ATOM_BROWSER_GET_BUILTIN', module
|
||||
builtinCache[module] = metaToValue meta
|
||||
|
||||
# Get current BrowserWindow object.
|
||||
windowCache = null
|
||||
exports.getCurrentWindow = ->
|
||||
return windowCache if windowCache?
|
||||
meta = ipcRenderer.sendSync 'ATOM_BROWSER_CURRENT_WINDOW'
|
||||
windowCache = metaToValue meta
|
||||
|
||||
# Get current WebContents object.
|
||||
webContentsCache = null
|
||||
exports.getCurrentWebContents = ->
|
||||
return webContentsCache if webContentsCache?
|
||||
meta = ipcRenderer.sendSync 'ATOM_BROWSER_CURRENT_WEB_CONTENTS'
|
||||
webContentsCache = metaToValue meta
|
||||
|
||||
# Get a global object in browser.
|
||||
exports.getGlobal = (name) ->
|
||||
meta = ipcRenderer.sendSync 'ATOM_BROWSER_GLOBAL', name
|
||||
metaToValue meta
|
||||
|
||||
# Get the process object in browser.
|
||||
processCache = null
|
||||
exports.__defineGetter__ 'process', ->
|
||||
processCache = exports.getGlobal('process') unless processCache?
|
||||
processCache
|
||||
|
||||
# Create a funtion that will return the specifed value when called in browser.
|
||||
exports.createFunctionWithReturnValue = (returnValue) ->
|
||||
func = -> returnValue
|
||||
v8Util.setHiddenValue func, 'returnValue', true
|
||||
func
|
||||
|
||||
# Get the guest WebContents from guestInstanceId.
|
||||
exports.getGuestWebContents = (guestInstanceId) ->
|
||||
meta = ipcRenderer.sendSync 'ATOM_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId
|
||||
metaToValue meta
|
394
atom/renderer/api/lib/remote.js
Normal file
394
atom/renderer/api/lib/remote.js
Normal file
|
@ -0,0 +1,394 @@
|
|||
var CallbacksRegistry, browserModules, builtinCache, callbacksRegistry, createRemoteMemberFunction, createRemoteMemberProperty, fn, ipcRenderer, isCircular, metaToPlainObject, metaToValue, moduleCache, name, processCache, ref, v8Util, webContentsCache, windowCache, wrapArgs,
|
||||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
ref = require('electron'), ipcRenderer = ref.ipcRenderer, CallbacksRegistry = ref.CallbacksRegistry;
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
callbacksRegistry = new CallbacksRegistry;
|
||||
|
||||
|
||||
/* Check for circular reference. */
|
||||
|
||||
isCircular = function(field, visited) {
|
||||
if (typeof field === 'object') {
|
||||
if (indexOf.call(visited, field) >= 0) {
|
||||
return true;
|
||||
}
|
||||
visited.push(field);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/* Convert the arguments object into an array of meta data. */
|
||||
|
||||
wrapArgs = function(args, visited) {
|
||||
var valueToMeta;
|
||||
if (visited == null) {
|
||||
visited = [];
|
||||
}
|
||||
valueToMeta = function(value) {
|
||||
var field, prop, ret;
|
||||
if (Array.isArray(value)) {
|
||||
return {
|
||||
type: 'array',
|
||||
value: wrapArgs(value, visited)
|
||||
};
|
||||
} else if (Buffer.isBuffer(value)) {
|
||||
return {
|
||||
type: 'buffer',
|
||||
value: Array.prototype.slice.call(value, 0)
|
||||
};
|
||||
} else if (value instanceof Date) {
|
||||
return {
|
||||
type: 'date',
|
||||
value: value.getTime()
|
||||
};
|
||||
} else if ((value != null ? value.constructor.name : void 0) === 'Promise') {
|
||||
return {
|
||||
type: 'promise',
|
||||
then: valueToMeta(value.then.bind(value))
|
||||
};
|
||||
} else if ((value != null) && typeof value === 'object' && v8Util.getHiddenValue(value, 'atomId')) {
|
||||
return {
|
||||
type: 'remote-object',
|
||||
id: v8Util.getHiddenValue(value, 'atomId')
|
||||
};
|
||||
} else if ((value != null) && typeof value === 'object') {
|
||||
ret = {
|
||||
type: 'object',
|
||||
name: value.constructor.name,
|
||||
members: []
|
||||
};
|
||||
for (prop in value) {
|
||||
field = value[prop];
|
||||
ret.members.push({
|
||||
name: prop,
|
||||
value: valueToMeta(isCircular(field, visited) ? null : field)
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
} else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
|
||||
return {
|
||||
type: 'function-with-return-value',
|
||||
value: valueToMeta(value())
|
||||
};
|
||||
} else if (typeof value === 'function') {
|
||||
return {
|
||||
type: 'function',
|
||||
id: callbacksRegistry.add(value),
|
||||
location: v8Util.getHiddenValue(value, 'location')
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'value',
|
||||
value: value
|
||||
};
|
||||
}
|
||||
};
|
||||
return Array.prototype.slice.call(args).map(valueToMeta);
|
||||
};
|
||||
|
||||
|
||||
/* Convert meta data from browser into real value. */
|
||||
|
||||
metaToValue = function(meta) {
|
||||
var RemoteFunction, el, i, j, len, len1, member, ref1, ref2, results, ret;
|
||||
switch (meta.type) {
|
||||
case 'value':
|
||||
return meta.value;
|
||||
case 'array':
|
||||
ref1 = meta.members;
|
||||
results = [];
|
||||
for (i = 0, len = ref1.length; i < len; i++) {
|
||||
el = ref1[i];
|
||||
results.push(metaToValue(el));
|
||||
}
|
||||
return results;
|
||||
case 'buffer':
|
||||
return new Buffer(meta.value);
|
||||
case 'promise':
|
||||
return Promise.resolve({
|
||||
then: metaToValue(meta.then)
|
||||
});
|
||||
case 'error':
|
||||
return metaToPlainObject(meta);
|
||||
case 'date':
|
||||
return new Date(meta.value);
|
||||
case 'exception':
|
||||
throw new Error(meta.message + "\n" + meta.stack);
|
||||
break;
|
||||
default:
|
||||
if (meta.type === 'function') {
|
||||
|
||||
/* A shadow class to represent the remote function object. */
|
||||
ret = RemoteFunction = (function() {
|
||||
function RemoteFunction() {
|
||||
var obj;
|
||||
if (this.constructor === RemoteFunction) {
|
||||
|
||||
/* Constructor call. */
|
||||
obj = ipcRenderer.sendSync('ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments));
|
||||
|
||||
/*
|
||||
Returning object in constructor will replace constructed object
|
||||
with the returned object.
|
||||
http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
|
||||
*/
|
||||
return metaToValue(obj);
|
||||
} else {
|
||||
|
||||
/* Function call. */
|
||||
obj = ipcRenderer.sendSync('ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments));
|
||||
return metaToValue(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return RemoteFunction;
|
||||
|
||||
})();
|
||||
} else {
|
||||
ret = v8Util.createObjectWithName(meta.name);
|
||||
}
|
||||
|
||||
/* Polulate delegate members. */
|
||||
ref2 = meta.members;
|
||||
for (j = 0, len1 = ref2.length; j < len1; j++) {
|
||||
member = ref2[j];
|
||||
if (member.type === 'function') {
|
||||
ret[member.name] = createRemoteMemberFunction(meta.id, member.name);
|
||||
} else {
|
||||
Object.defineProperty(ret, member.name, createRemoteMemberProperty(meta.id, member.name));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Track delegate object's life time, and tell the browser to clean up
|
||||
when the object is GCed.
|
||||
*/
|
||||
v8Util.setDestructor(ret, function() {
|
||||
return ipcRenderer.send('ATOM_BROWSER_DEREFERENCE', meta.id);
|
||||
});
|
||||
|
||||
/* Remember object's id. */
|
||||
v8Util.setHiddenValue(ret, 'atomId', meta.id);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Construct a plain object from the meta. */
|
||||
|
||||
metaToPlainObject = function(meta) {
|
||||
var i, len, name, obj, ref1, ref2, value;
|
||||
obj = (function() {
|
||||
switch (meta.type) {
|
||||
case 'error':
|
||||
return new Error;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
})();
|
||||
ref1 = meta.members;
|
||||
for (i = 0, len = ref1.length; i < len; i++) {
|
||||
ref2 = ref1[i], name = ref2.name, value = ref2.value;
|
||||
obj[name] = value;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Create a RemoteMemberFunction instance.
|
||||
This function's content should not be inlined into metaToValue, otherwise V8
|
||||
may consider it circular reference.
|
||||
*/
|
||||
|
||||
createRemoteMemberFunction = function(metaId, name) {
|
||||
var RemoteMemberFunction;
|
||||
return RemoteMemberFunction = (function() {
|
||||
function RemoteMemberFunction() {
|
||||
var ret;
|
||||
if (this.constructor === RemoteMemberFunction) {
|
||||
|
||||
/* Constructor call. */
|
||||
ret = ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_CONSTRUCTOR', metaId, name, wrapArgs(arguments));
|
||||
return metaToValue(ret);
|
||||
} else {
|
||||
|
||||
/* Call member function. */
|
||||
ret = ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_CALL', metaId, name, wrapArgs(arguments));
|
||||
return metaToValue(ret);
|
||||
}
|
||||
}
|
||||
|
||||
return RemoteMemberFunction;
|
||||
|
||||
})();
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Create configuration for defineProperty.
|
||||
This function's content should not be inlined into metaToValue, otherwise V8
|
||||
may consider it circular reference.
|
||||
*/
|
||||
|
||||
createRemoteMemberProperty = function(metaId, name) {
|
||||
return {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
set: function(value) {
|
||||
|
||||
/* Set member data. */
|
||||
ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_SET', metaId, name, value);
|
||||
return value;
|
||||
},
|
||||
get: function() {
|
||||
|
||||
/* Get member data. */
|
||||
return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_GET', metaId, name));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/* Browser calls a callback in renderer. */
|
||||
|
||||
ipcRenderer.on('ATOM_RENDERER_CALLBACK', function(event, id, args) {
|
||||
return callbacksRegistry.apply(id, metaToValue(args));
|
||||
});
|
||||
|
||||
|
||||
/* A callback in browser is released. */
|
||||
|
||||
ipcRenderer.on('ATOM_RENDERER_RELEASE_CALLBACK', function(event, id) {
|
||||
return callbacksRegistry.remove(id);
|
||||
});
|
||||
|
||||
|
||||
/* List all built-in modules in browser process. */
|
||||
|
||||
browserModules = require('../../../browser/api/lib/exports/electron');
|
||||
|
||||
|
||||
/* And add a helper receiver for each one. */
|
||||
|
||||
fn = function(name) {
|
||||
return Object.defineProperty(exports, name, {
|
||||
get: function() {
|
||||
return exports.getBuiltin(name);
|
||||
}
|
||||
});
|
||||
};
|
||||
for (name in browserModules) {
|
||||
fn(name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get remote module.
|
||||
(Just like node's require, the modules are cached permanently, note that this
|
||||
is safe leak since the object is not expected to get freed in browser)
|
||||
*/
|
||||
|
||||
moduleCache = {};
|
||||
|
||||
exports.require = function(module) {
|
||||
var meta;
|
||||
if (moduleCache[module] != null) {
|
||||
return moduleCache[module];
|
||||
}
|
||||
meta = ipcRenderer.sendSync('ATOM_BROWSER_REQUIRE', module);
|
||||
return moduleCache[module] = metaToValue(meta);
|
||||
};
|
||||
|
||||
|
||||
/* Optimize require('electron'). */
|
||||
|
||||
moduleCache.electron = exports;
|
||||
|
||||
|
||||
/* Alias to remote.require('electron').xxx. */
|
||||
|
||||
builtinCache = {};
|
||||
|
||||
exports.getBuiltin = function(module) {
|
||||
var meta;
|
||||
if (builtinCache[module] != null) {
|
||||
return builtinCache[module];
|
||||
}
|
||||
meta = ipcRenderer.sendSync('ATOM_BROWSER_GET_BUILTIN', module);
|
||||
return builtinCache[module] = metaToValue(meta);
|
||||
};
|
||||
|
||||
|
||||
/* Get current BrowserWindow object. */
|
||||
|
||||
windowCache = null;
|
||||
|
||||
exports.getCurrentWindow = function() {
|
||||
var meta;
|
||||
if (windowCache != null) {
|
||||
return windowCache;
|
||||
}
|
||||
meta = ipcRenderer.sendSync('ATOM_BROWSER_CURRENT_WINDOW');
|
||||
return windowCache = metaToValue(meta);
|
||||
};
|
||||
|
||||
|
||||
/* Get current WebContents object. */
|
||||
|
||||
webContentsCache = null;
|
||||
|
||||
exports.getCurrentWebContents = function() {
|
||||
var meta;
|
||||
if (webContentsCache != null) {
|
||||
return webContentsCache;
|
||||
}
|
||||
meta = ipcRenderer.sendSync('ATOM_BROWSER_CURRENT_WEB_CONTENTS');
|
||||
return webContentsCache = metaToValue(meta);
|
||||
};
|
||||
|
||||
|
||||
/* Get a global object in browser. */
|
||||
|
||||
exports.getGlobal = function(name) {
|
||||
var meta;
|
||||
meta = ipcRenderer.sendSync('ATOM_BROWSER_GLOBAL', name);
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
|
||||
/* Get the process object in browser. */
|
||||
|
||||
processCache = null;
|
||||
|
||||
exports.__defineGetter__('process', function() {
|
||||
if (processCache == null) {
|
||||
processCache = exports.getGlobal('process');
|
||||
}
|
||||
return processCache;
|
||||
});
|
||||
|
||||
|
||||
/* Create a funtion that will return the specifed value when called in browser. */
|
||||
|
||||
exports.createFunctionWithReturnValue = function(returnValue) {
|
||||
var func;
|
||||
func = function() {
|
||||
return returnValue;
|
||||
};
|
||||
v8Util.setHiddenValue(func, 'returnValue', true);
|
||||
return func;
|
||||
};
|
||||
|
||||
|
||||
/* Get the guest WebContents from guestInstanceId. */
|
||||
|
||||
exports.getGuestWebContents = function(guestInstanceId) {
|
||||
var meta;
|
||||
meta = ipcRenderer.sendSync('ATOM_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId);
|
||||
return metaToValue(meta);
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
module.exports = require('electron').remote.screen
|
1
atom/renderer/api/lib/screen.js
Normal file
1
atom/renderer/api/lib/screen.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('electron').remote.screen;
|
|
@ -1,9 +0,0 @@
|
|||
{deprecate} = require 'electron'
|
||||
{webFrame} = process.atomBinding 'web_frame'
|
||||
|
||||
# Deprecated.
|
||||
deprecate.rename webFrame, 'registerUrlSchemeAsSecure', 'registerURLSchemeAsSecure'
|
||||
deprecate.rename webFrame, 'registerUrlSchemeAsBypassingCSP', 'registerURLSchemeAsBypassingCSP'
|
||||
deprecate.rename webFrame, 'registerUrlSchemeAsPrivileged', 'registerURLSchemeAsPrivileged'
|
||||
|
||||
module.exports = webFrame
|
16
atom/renderer/api/lib/web-frame.js
Normal file
16
atom/renderer/api/lib/web-frame.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
var deprecate, webFrame;
|
||||
|
||||
deprecate = require('electron').deprecate;
|
||||
|
||||
webFrame = process.atomBinding('web_frame').webFrame;
|
||||
|
||||
|
||||
/* Deprecated. */
|
||||
|
||||
deprecate.rename(webFrame, 'registerUrlSchemeAsSecure', 'registerURLSchemeAsSecure');
|
||||
|
||||
deprecate.rename(webFrame, 'registerUrlSchemeAsBypassingCSP', 'registerURLSchemeAsBypassingCSP');
|
||||
|
||||
deprecate.rename(webFrame, 'registerUrlSchemeAsPrivileged', 'registerURLSchemeAsPrivileged');
|
||||
|
||||
module.exports = webFrame;
|
|
@ -1,10 +0,0 @@
|
|||
url = require 'url'
|
||||
|
||||
chrome = window.chrome = window.chrome || {}
|
||||
chrome.extension =
|
||||
getURL: (path) ->
|
||||
url.format
|
||||
protocol: location.protocol
|
||||
slashes: true
|
||||
hostname: location.hostname
|
||||
pathname: path
|
16
atom/renderer/lib/chrome-api.js
Normal file
16
atom/renderer/lib/chrome-api.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
var chrome, url;
|
||||
|
||||
url = require('url');
|
||||
|
||||
chrome = window.chrome = window.chrome || {};
|
||||
|
||||
chrome.extension = {
|
||||
getURL: function(path) {
|
||||
return url.format({
|
||||
protocol: location.protocol,
|
||||
slashes: true,
|
||||
hostname: location.hostname,
|
||||
pathname: path
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,109 +0,0 @@
|
|||
events = require 'events'
|
||||
path = require 'path'
|
||||
url = require 'url'
|
||||
Module = require 'module'
|
||||
|
||||
# We modified the original process.argv to let node.js load the
|
||||
# atom-renderer.js, we need to restore it here.
|
||||
process.argv.splice 1, 1
|
||||
|
||||
# Clear search paths.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths')
|
||||
|
||||
# Import common settings.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')
|
||||
|
||||
globalPaths = Module.globalPaths
|
||||
unless process.env.ELECTRON_HIDE_INTERNAL_MODULES
|
||||
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib')
|
||||
|
||||
# Expose public APIs.
|
||||
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib', 'exports')
|
||||
|
||||
# The global variable will be used by ipc for event dispatching
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
v8Util.setHiddenValue global, 'ipc', new events.EventEmitter
|
||||
|
||||
# Process command line arguments.
|
||||
nodeIntegration = 'false'
|
||||
for arg in process.argv
|
||||
if arg.indexOf('--guest-instance-id=') == 0
|
||||
# This is a guest web view.
|
||||
process.guestInstanceId = parseInt arg.substr(arg.indexOf('=') + 1)
|
||||
else if arg.indexOf('--opener-id=') == 0
|
||||
# This is a guest BrowserWindow.
|
||||
process.openerId = parseInt arg.substr(arg.indexOf('=') + 1)
|
||||
else if arg.indexOf('--node-integration=') == 0
|
||||
nodeIntegration = arg.substr arg.indexOf('=') + 1
|
||||
else if arg.indexOf('--preload=') == 0
|
||||
preloadScript = arg.substr arg.indexOf('=') + 1
|
||||
|
||||
if location.protocol is 'chrome-devtools:'
|
||||
# Override some inspector APIs.
|
||||
require './inspector'
|
||||
nodeIntegration = 'true'
|
||||
else if location.protocol is 'chrome-extension:'
|
||||
# Add implementations of chrome API.
|
||||
require './chrome-api'
|
||||
nodeIntegration = 'true'
|
||||
else
|
||||
# Override default web functions.
|
||||
require './override'
|
||||
# Load webview tag implementation.
|
||||
unless process.guestInstanceId?
|
||||
require './web-view/web-view'
|
||||
require './web-view/web-view-attributes'
|
||||
|
||||
if nodeIntegration in ['true', 'all', 'except-iframe', 'manual-enable-iframe']
|
||||
# Export node bindings to global.
|
||||
global.require = require
|
||||
global.module = module
|
||||
|
||||
# Set the __filename to the path of html file if it is file: protocol.
|
||||
if window.location.protocol is 'file:'
|
||||
pathname =
|
||||
if process.platform is 'win32' and window.location.pathname[0] is '/'
|
||||
window.location.pathname.substr 1
|
||||
else
|
||||
window.location.pathname
|
||||
global.__filename = path.normalize decodeURIComponent(pathname)
|
||||
global.__dirname = path.dirname global.__filename
|
||||
|
||||
# Set module's filename so relative require can work as expected.
|
||||
module.filename = global.__filename
|
||||
|
||||
# Also search for module under the html file.
|
||||
module.paths = module.paths.concat Module._nodeModulePaths(global.__dirname)
|
||||
else
|
||||
global.__filename = __filename
|
||||
global.__dirname = __dirname
|
||||
|
||||
# Redirect window.onerror to uncaughtException.
|
||||
window.onerror = (message, filename, lineno, colno, error) ->
|
||||
if global.process.listeners('uncaughtException').length > 0
|
||||
global.process.emit 'uncaughtException', error
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
# Emit the 'exit' event when page is unloading.
|
||||
window.addEventListener 'unload', ->
|
||||
process.emit 'exit'
|
||||
else
|
||||
# Delete Node's symbols after the Environment has been loaded.
|
||||
process.once 'loaded', ->
|
||||
delete global.process
|
||||
delete global.setImmediate
|
||||
delete global.clearImmediate
|
||||
delete global.global
|
||||
|
||||
# Load the script specfied by the "preload" attribute.
|
||||
if preloadScript
|
||||
try
|
||||
require preloadScript
|
||||
catch error
|
||||
if error.code is 'MODULE_NOT_FOUND'
|
||||
console.error "Unable to load preload script #{preloadScript}"
|
||||
else
|
||||
console.error(error)
|
||||
console.error(error.stack)
|
154
atom/renderer/lib/init.js
Normal file
154
atom/renderer/lib/init.js
Normal file
|
@ -0,0 +1,154 @@
|
|||
var Module, arg, error, error1, events, globalPaths, i, len, nodeIntegration, path, pathname, preloadScript, ref, url, v8Util;
|
||||
|
||||
events = require('events');
|
||||
|
||||
path = require('path');
|
||||
|
||||
url = require('url');
|
||||
|
||||
Module = require('module');
|
||||
|
||||
|
||||
/*
|
||||
We modified the original process.argv to let node.js load the
|
||||
atom-renderer.js, we need to restore it here.
|
||||
*/
|
||||
|
||||
process.argv.splice(1, 1);
|
||||
|
||||
|
||||
/* Clear search paths. */
|
||||
|
||||
require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths'));
|
||||
|
||||
|
||||
/* Import common settings. */
|
||||
|
||||
require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'init'));
|
||||
|
||||
globalPaths = Module.globalPaths;
|
||||
|
||||
if (!process.env.ELECTRON_HIDE_INTERNAL_MODULES) {
|
||||
globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib'));
|
||||
}
|
||||
|
||||
|
||||
/* Expose public APIs. */
|
||||
|
||||
globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib', 'exports'));
|
||||
|
||||
|
||||
/* The global variable will be used by ipc for event dispatching */
|
||||
|
||||
v8Util = process.atomBinding('v8_util');
|
||||
|
||||
v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter);
|
||||
|
||||
|
||||
/* Process command line arguments. */
|
||||
|
||||
nodeIntegration = 'false';
|
||||
|
||||
ref = process.argv;
|
||||
for (i = 0, len = ref.length; i < len; i++) {
|
||||
arg = ref[i];
|
||||
if (arg.indexOf('--guest-instance-id=') === 0) {
|
||||
|
||||
/* This is a guest web view. */
|
||||
process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1));
|
||||
} else if (arg.indexOf('--opener-id=') === 0) {
|
||||
|
||||
/* This is a guest BrowserWindow. */
|
||||
process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1));
|
||||
} else if (arg.indexOf('--node-integration=') === 0) {
|
||||
nodeIntegration = arg.substr(arg.indexOf('=') + 1);
|
||||
} else if (arg.indexOf('--preload=') === 0) {
|
||||
preloadScript = arg.substr(arg.indexOf('=') + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (location.protocol === 'chrome-devtools:') {
|
||||
|
||||
/* Override some inspector APIs. */
|
||||
require('./inspector');
|
||||
nodeIntegration = 'true';
|
||||
} else if (location.protocol === 'chrome-extension:') {
|
||||
|
||||
/* Add implementations of chrome API. */
|
||||
require('./chrome-api');
|
||||
nodeIntegration = 'true';
|
||||
} else {
|
||||
|
||||
/* Override default web functions. */
|
||||
require('./override');
|
||||
|
||||
/* Load webview tag implementation. */
|
||||
if (process.guestInstanceId == null) {
|
||||
require('./web-view/web-view');
|
||||
require('./web-view/web-view-attributes');
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeIntegration === 'true' || nodeIntegration === 'all' || nodeIntegration === 'except-iframe' || nodeIntegration === 'manual-enable-iframe') {
|
||||
|
||||
/* Export node bindings to global. */
|
||||
global.require = require;
|
||||
global.module = module;
|
||||
|
||||
/* Set the __filename to the path of html file if it is file: protocol. */
|
||||
if (window.location.protocol === 'file:') {
|
||||
pathname = process.platform === 'win32' && window.location.pathname[0] === '/' ? window.location.pathname.substr(1) : window.location.pathname;
|
||||
global.__filename = path.normalize(decodeURIComponent(pathname));
|
||||
global.__dirname = path.dirname(global.__filename);
|
||||
|
||||
/* Set module's filename so relative require can work as expected. */
|
||||
module.filename = global.__filename;
|
||||
|
||||
/* Also search for module under the html file. */
|
||||
module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname));
|
||||
} else {
|
||||
global.__filename = __filename;
|
||||
global.__dirname = __dirname;
|
||||
}
|
||||
|
||||
/* Redirect window.onerror to uncaughtException. */
|
||||
window.onerror = function(message, filename, lineno, colno, error) {
|
||||
if (global.process.listeners('uncaughtException').length > 0) {
|
||||
global.process.emit('uncaughtException', error);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/* Emit the 'exit' event when page is unloading. */
|
||||
window.addEventListener('unload', function() {
|
||||
return process.emit('exit');
|
||||
});
|
||||
} else {
|
||||
|
||||
/* Delete Node's symbols after the Environment has been loaded. */
|
||||
process.once('loaded', function() {
|
||||
delete global.process;
|
||||
delete global.setImmediate;
|
||||
delete global.clearImmediate;
|
||||
return delete global.global;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* Load the script specfied by the "preload" attribute. */
|
||||
|
||||
if (preloadScript) {
|
||||
try {
|
||||
require(preloadScript);
|
||||
} catch (error1) {
|
||||
error = error1;
|
||||
if (error.code === 'MODULE_NOT_FOUND') {
|
||||
console.error("Unable to load preload script " + preloadScript);
|
||||
} else {
|
||||
console.error(error);
|
||||
console.error(error.stack);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
window.onload = ->
|
||||
# Use menu API to show context menu.
|
||||
InspectorFrontendHost.showContextMenuAtPoint = createMenu
|
||||
|
||||
# Use dialog API to override file chooser dialog.
|
||||
WebInspector.createFileSelectorElement = createFileSelectorElement
|
||||
|
||||
convertToMenuTemplate = (items) ->
|
||||
template = []
|
||||
for item in items
|
||||
do (item) ->
|
||||
transformed =
|
||||
if item.type is 'subMenu'
|
||||
type: 'submenu'
|
||||
label: item.label
|
||||
enabled: item.enabled
|
||||
submenu: convertToMenuTemplate item.subItems
|
||||
else if item.type is 'separator'
|
||||
type: 'separator'
|
||||
else if item.type is 'checkbox'
|
||||
type: 'checkbox'
|
||||
label: item.label
|
||||
enabled: item.enabled
|
||||
checked: item.checked
|
||||
else
|
||||
type: 'normal'
|
||||
label: item.label
|
||||
enabled: item.enabled
|
||||
if item.id?
|
||||
transformed.click = ->
|
||||
DevToolsAPI.contextMenuItemSelected item.id
|
||||
DevToolsAPI.contextMenuCleared()
|
||||
template.push transformed
|
||||
template
|
||||
|
||||
createMenu = (x, y, items, document) ->
|
||||
{remote} = require 'electron'
|
||||
{Menu} = remote
|
||||
|
||||
menu = Menu.buildFromTemplate convertToMenuTemplate(items)
|
||||
# The menu is expected to show asynchronously.
|
||||
setTimeout -> menu.popup remote.getCurrentWindow()
|
||||
|
||||
showFileChooserDialog = (callback) ->
|
||||
{remote} = require 'electron'
|
||||
{dialog} = remote
|
||||
files = dialog.showOpenDialog {}
|
||||
callback pathToHtml5FileObject files[0] if files?
|
||||
|
||||
pathToHtml5FileObject = (path) ->
|
||||
fs = require 'fs'
|
||||
blob = new Blob([fs.readFileSync(path)])
|
||||
blob.name = path
|
||||
blob
|
||||
|
||||
createFileSelectorElement = (callback) ->
|
||||
fileSelectorElement = document.createElement 'span'
|
||||
fileSelectorElement.style.display = 'none'
|
||||
fileSelectorElement.click = showFileChooserDialog.bind this, callback
|
||||
return fileSelectorElement
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue