Convert all source files to JavaScript

This commit is contained in:
Kevin Sawicki 2016-01-11 18:40:23 -08:00
parent 403870a27e
commit 1f9691ae13
144 changed files with 11211 additions and 7301 deletions

View file

@ -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

View 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
});
}
};

View file

@ -1,111 +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
View 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);
}
}
}

View file

@ -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

View file

@ -0,0 +1,85 @@
var convertToMenuTemplate, createFileSelectorElement, createMenu, pathToHtml5FileObject, showFileChooserDialog;
window.onload = function() {
/* Use menu API to show context menu. */
InspectorFrontendHost.showContextMenuAtPoint = createMenu;
/* Use dialog API to override file chooser dialog. */
return WebInspector.createFileSelectorElement = createFileSelectorElement;
};
convertToMenuTemplate = function(items) {
var fn, i, item, len, template;
template = [];
fn = function(item) {
var transformed;
transformed = item.type === 'subMenu' ? {
type: 'submenu',
label: item.label,
enabled: item.enabled,
submenu: convertToMenuTemplate(item.subItems)
} : item.type === 'separator' ? {
type: 'separator'
} : item.type === 'checkbox' ? {
type: 'checkbox',
label: item.label,
enabled: item.enabled,
checked: item.checked
} : {
type: 'normal',
label: item.label,
enabled: item.enabled
};
if (item.id != null) {
transformed.click = function() {
DevToolsAPI.contextMenuItemSelected(item.id);
return DevToolsAPI.contextMenuCleared();
};
}
return template.push(transformed);
};
for (i = 0, len = items.length; i < len; i++) {
item = items[i];
fn(item);
}
return template;
};
createMenu = function(x, y, items, document) {
var Menu, menu, remote;
remote = require('electron').remote;
Menu = remote.Menu;
menu = Menu.buildFromTemplate(convertToMenuTemplate(items));
/* The menu is expected to show asynchronously. */
return setTimeout(function() {
return menu.popup(remote.getCurrentWindow());
});
};
showFileChooserDialog = function(callback) {
var dialog, files, remote;
remote = require('electron').remote;
dialog = remote.dialog;
files = dialog.showOpenDialog({});
if (files != null) {
return callback(pathToHtml5FileObject(files[0]));
}
};
pathToHtml5FileObject = function(path) {
var blob, fs;
fs = require('fs');
blob = new Blob([fs.readFileSync(path)]);
blob.name = path;
return blob;
};
createFileSelectorElement = function(callback) {
var fileSelectorElement;
fileSelectorElement = document.createElement('span');
fileSelectorElement.style.display = 'none';
fileSelectorElement.click = showFileChooserDialog.bind(this, callback);
return fileSelectorElement;
};

View file

@ -1,129 +0,0 @@
{ipcRenderer, remote} = require 'electron'
### Helper function to resolve relative url. ###
a = window.top.document.createElement 'a'
resolveURL = (url) ->
a.href = url
a.href
### Window object returned by "window.open". ###
class BrowserWindowProxy
@proxies: {}
@getOrCreate: (guestId) ->
@proxies[guestId] ?= new BrowserWindowProxy(guestId)
@remove: (guestId) ->
delete @proxies[guestId]
constructor: (@guestId) ->
@closed = false
ipcRenderer.once "ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_#{@guestId}", =>
BrowserWindowProxy.remove(@guestId)
@closed = true
close: ->
ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', @guestId
focus: ->
ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', @guestId, 'focus'
blur: ->
ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', @guestId, 'blur'
postMessage: (message, targetOrigin='*') ->
ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', @guestId, message, targetOrigin, location.origin
eval: (args...) ->
ipcRenderer.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', @guestId, 'executeJavaScript', args...
unless process.guestInstanceId?
### Override default window.close. ###
window.close = ->
remote.getCurrentWindow().close()
### Make the browser window or guest view emit "new-window" event. ###
window.open = (url, frameName='', features='') ->
options = {}
ints = [ 'x', 'y', 'width', 'height', 'min-width', 'max-width', 'min-height', 'max-height', 'zoom-factor' ]
### Make sure to get rid of excessive whitespace in the property name ###
for feature in features.split /,\s*/
[name, value] = feature.split /\s*=/
options[name] =
if value is 'yes' or value is '1'
true
else if value is 'no' or value is '0'
false
else
value
options.x ?= options.left if options.left
options.y ?= options.top if options.top
options.title ?= frameName
options.width ?= 800
options.height ?= 600
### Resolve relative urls. ###
url = resolveURL url
(options[name] = parseInt(options[name], 10) if options[name]?) for name in ints
guestId = ipcRenderer.sendSync 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, frameName, options
if guestId
BrowserWindowProxy.getOrCreate(guestId)
else
null
### Use the dialog API to implement alert(). ###
window.alert = (message, title='') ->
buttons = ['OK']
message = message.toString()
remote.dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons}
### Alert should always return undefined. ###
return
### And the confirm(). ###
window.confirm = (message, title='') ->
buttons = ['OK', 'Cancel']
cancelId = 1
not remote.dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons, cancelId}
### But we do not support prompt(). ###
window.prompt = ->
throw new Error('prompt() is and will not be supported.')
if process.openerId?
window.opener = BrowserWindowProxy.getOrCreate process.openerId
ipcRenderer.on 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', (event, sourceId, message, sourceOrigin) ->
### Manually dispatch event instead of using postMessage because we also need to ###
### set event.source. ###
event = document.createEvent 'Event'
event.initEvent 'message', false, false
event.data = message
event.origin = sourceOrigin
event.source = BrowserWindowProxy.getOrCreate(sourceId)
window.dispatchEvent event
### Forward history operations to browser. ###
sendHistoryOperation = (args...) ->
ipcRenderer.send 'ATOM_SHELL_NAVIGATION_CONTROLLER', args...
getHistoryOperation = (args...) ->
ipcRenderer.sendSync 'ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', args...
window.history.back = -> sendHistoryOperation 'goBack'
window.history.forward = -> sendHistoryOperation 'goForward'
window.history.go = (offset) -> sendHistoryOperation 'goToOffset', offset
Object.defineProperty window.history, 'length',
get: ->
getHistoryOperation 'length'
### Make document.hidden and document.visibilityState return the correct value. ###
Object.defineProperty document, 'hidden',
get: ->
currentWindow = remote.getCurrentWindow()
currentWindow.isMinimized() || !currentWindow.isVisible()
Object.defineProperty document, 'visibilityState',
get: ->
if document.hidden then "hidden" else "visible"

View file

@ -0,0 +1,249 @@
var BrowserWindowProxy, a, getHistoryOperation, ipcRenderer, ref, remote, resolveURL, sendHistoryOperation,
slice = [].slice;
ref = require('electron'), ipcRenderer = ref.ipcRenderer, remote = ref.remote;
/* Helper function to resolve relative url. */
a = window.top.document.createElement('a');
resolveURL = function(url) {
a.href = url;
return a.href;
};
/* Window object returned by "window.open". */
BrowserWindowProxy = (function() {
BrowserWindowProxy.proxies = {};
BrowserWindowProxy.getOrCreate = function(guestId) {
var base;
return (base = this.proxies)[guestId] != null ? base[guestId] : base[guestId] = new BrowserWindowProxy(guestId);
};
BrowserWindowProxy.remove = function(guestId) {
return delete this.proxies[guestId];
};
function BrowserWindowProxy(guestId1) {
this.guestId = guestId1;
this.closed = false;
ipcRenderer.once("ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_" + this.guestId, (function(_this) {
return function() {
BrowserWindowProxy.remove(_this.guestId);
return _this.closed = true;
};
})(this));
}
BrowserWindowProxy.prototype.close = function() {
return ipcRenderer.send('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', this.guestId);
};
BrowserWindowProxy.prototype.focus = function() {
return ipcRenderer.send('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'focus');
};
BrowserWindowProxy.prototype.blur = function() {
return ipcRenderer.send('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'blur');
};
BrowserWindowProxy.prototype.postMessage = function(message, targetOrigin) {
if (targetOrigin == null) {
targetOrigin = '*';
}
return ipcRenderer.send('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, targetOrigin, location.origin);
};
BrowserWindowProxy.prototype["eval"] = function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return ipcRenderer.send.apply(ipcRenderer, ['ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'executeJavaScript'].concat(slice.call(args)));
};
return BrowserWindowProxy;
})();
if (process.guestInstanceId == null) {
/* Override default window.close. */
window.close = function() {
return remote.getCurrentWindow().close();
};
}
/* Make the browser window or guest view emit "new-window" event. */
window.open = function(url, frameName, features) {
var feature, guestId, i, ints, j, len, len1, name, options, ref1, ref2, value;
if (frameName == null) {
frameName = '';
}
if (features == null) {
features = '';
}
options = {};
ints = ['x', 'y', 'width', 'height', 'min-width', 'max-width', 'min-height', 'max-height', 'zoom-factor'];
/* Make sure to get rid of excessive whitespace in the property name */
ref1 = features.split(/,\s*/);
for (i = 0, len = ref1.length; i < len; i++) {
feature = ref1[i];
ref2 = feature.split(/\s*=/), name = ref2[0], value = ref2[1];
options[name] = value === 'yes' || value === '1' ? true : value === 'no' || value === '0' ? false : value;
}
if (options.left) {
if (options.x == null) {
options.x = options.left;
}
}
if (options.top) {
if (options.y == null) {
options.y = options.top;
}
}
if (options.title == null) {
options.title = frameName;
}
if (options.width == null) {
options.width = 800;
}
if (options.height == null) {
options.height = 600;
}
/* Resolve relative urls. */
url = resolveURL(url);
for (j = 0, len1 = ints.length; j < len1; j++) {
name = ints[j];
if (options[name] != null) {
options[name] = parseInt(options[name], 10);
}
}
guestId = ipcRenderer.sendSync('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, frameName, options);
if (guestId) {
return BrowserWindowProxy.getOrCreate(guestId);
} else {
return null;
}
};
/* Use the dialog API to implement alert(). */
window.alert = function(message, title) {
var buttons;
if (title == null) {
title = '';
}
buttons = ['OK'];
message = message.toString();
remote.dialog.showMessageBox(remote.getCurrentWindow(), {
message: message,
title: title,
buttons: buttons
});
/* Alert should always return undefined. */
};
/* And the confirm(). */
window.confirm = function(message, title) {
var buttons, cancelId;
if (title == null) {
title = '';
}
buttons = ['OK', 'Cancel'];
cancelId = 1;
return !remote.dialog.showMessageBox(remote.getCurrentWindow(), {
message: message,
title: title,
buttons: buttons,
cancelId: cancelId
});
};
/* But we do not support prompt(). */
window.prompt = function() {
throw new Error('prompt() is and will not be supported.');
};
if (process.openerId != null) {
window.opener = BrowserWindowProxy.getOrCreate(process.openerId);
}
ipcRenderer.on('ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', function(event, sourceId, message, sourceOrigin) {
/* Manually dispatch event instead of using postMessage because we also need to */
/* set event.source. */
event = document.createEvent('Event');
event.initEvent('message', false, false);
event.data = message;
event.origin = sourceOrigin;
event.source = BrowserWindowProxy.getOrCreate(sourceId);
return window.dispatchEvent(event);
});
/* Forward history operations to browser. */
sendHistoryOperation = function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return ipcRenderer.send.apply(ipcRenderer, ['ATOM_SHELL_NAVIGATION_CONTROLLER'].concat(slice.call(args)));
};
getHistoryOperation = function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return ipcRenderer.sendSync.apply(ipcRenderer, ['ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER'].concat(slice.call(args)));
};
window.history.back = function() {
return sendHistoryOperation('goBack');
};
window.history.forward = function() {
return sendHistoryOperation('goForward');
};
window.history.go = function(offset) {
return sendHistoryOperation('goToOffset', offset);
};
Object.defineProperty(window.history, 'length', {
get: function() {
return getHistoryOperation('length');
}
});
/* Make document.hidden and document.visibilityState return the correct value. */
Object.defineProperty(document, 'hidden', {
get: function() {
var currentWindow;
currentWindow = remote.getCurrentWindow();
return currentWindow.isMinimized() || !currentWindow.isVisible();
}
});
Object.defineProperty(document, 'visibilityState', {
get: function() {
if (document.hidden) {
return "hidden";
} else {
return "visible";
}
}
});

View file

@ -1,89 +0,0 @@
{ipcRenderer, webFrame} = require 'electron'
requestId = 0
WEB_VIEW_EVENTS =
'load-commit': ['url', 'isMainFrame']
'did-finish-load': []
'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL']
'did-frame-finish-load': ['isMainFrame']
'did-start-loading': []
'did-stop-loading': []
'did-get-response-details': ['status', 'newURL', 'originalURL',
'httpResponseCode', 'requestMethod', 'referrer',
'headers']
'did-get-redirect-request': ['oldURL', 'newURL', 'isMainFrame']
'dom-ready': []
'console-message': ['level', 'message', 'line', 'sourceId']
'devtools-opened': []
'devtools-closed': []
'devtools-focused': []
'new-window': ['url', 'frameName', 'disposition', 'options']
'will-navigate': ['url']
'did-navigate': ['url']
'did-navigate-in-page': ['url']
'close': []
'crashed': []
'gpu-crashed': []
'plugin-crashed': ['name', 'version']
'media-started-playing': []
'media-paused': []
'did-change-theme-color': ['themeColor']
'destroyed': []
'page-title-updated': ['title', 'explicitSet']
'page-favicon-updated': ['favicons']
'enter-html-full-screen': []
'leave-html-full-screen': []
'found-in-page': ['result']
DEPRECATED_EVENTS =
'page-title-updated': 'page-title-set'
dispatchEvent = (webView, eventName, eventKey, args...) ->
if DEPRECATED_EVENTS[eventName]?
dispatchEvent webView, DEPRECATED_EVENTS[eventName], eventKey, args...
domEvent = new Event(eventName)
for f, i in WEB_VIEW_EVENTS[eventKey]
domEvent[f] = args[i]
webView.dispatchEvent domEvent
webView.onLoadCommit domEvent if eventName is 'load-commit'
module.exports =
registerEvents: (webView, viewInstanceId) ->
ipcRenderer.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{viewInstanceId}", (event, eventName, args...) ->
dispatchEvent webView, eventName, eventName, args...
ipcRenderer.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{viewInstanceId}", (event, channel, args...) ->
domEvent = new Event('ipc-message')
domEvent.channel = channel
domEvent.args = [args...]
webView.dispatchEvent domEvent
ipcRenderer.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{viewInstanceId}", (event, args...) ->
domEvent = new Event('size-changed')
for f, i in ['oldWidth', 'oldHeight', 'newWidth', 'newHeight']
domEvent[f] = args[i]
webView.onSizeChanged domEvent
deregisterEvents: (viewInstanceId) ->
ipcRenderer.removeAllListeners "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{viewInstanceId}"
ipcRenderer.removeAllListeners "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{viewInstanceId}"
ipcRenderer.removeAllListeners "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{viewInstanceId}"
createGuest: (params, callback) ->
requestId++
ipcRenderer.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId
ipcRenderer.once "ATOM_SHELL_RESPONSE_#{requestId}", callback
attachGuest: (elementInstanceId, guestInstanceId, params) ->
ipcRenderer.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', elementInstanceId, guestInstanceId, params
webFrame.attachGuest elementInstanceId
destroyGuest: (guestInstanceId) ->
ipcRenderer.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId
setSize: (guestInstanceId, params) ->
ipcRenderer.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', guestInstanceId, params
setAllowTransparency: (guestInstanceId, allowtransparency) ->
ipcRenderer.send 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', guestInstanceId, allowtransparency

View file

@ -0,0 +1,113 @@
var DEPRECATED_EVENTS, WEB_VIEW_EVENTS, dispatchEvent, ipcRenderer, ref, requestId, webFrame,
slice = [].slice;
ref = require('electron'), ipcRenderer = ref.ipcRenderer, webFrame = ref.webFrame;
requestId = 0;
WEB_VIEW_EVENTS = {
'load-commit': ['url', 'isMainFrame'],
'did-finish-load': [],
'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL'],
'did-frame-finish-load': ['isMainFrame'],
'did-start-loading': [],
'did-stop-loading': [],
'did-get-response-details': ['status', 'newURL', 'originalURL', 'httpResponseCode', 'requestMethod', 'referrer', 'headers'],
'did-get-redirect-request': ['oldURL', 'newURL', 'isMainFrame'],
'dom-ready': [],
'console-message': ['level', 'message', 'line', 'sourceId'],
'devtools-opened': [],
'devtools-closed': [],
'devtools-focused': [],
'new-window': ['url', 'frameName', 'disposition', 'options'],
'will-navigate': ['url'],
'did-navigate': ['url'],
'did-navigate-in-page': ['url'],
'close': [],
'crashed': [],
'gpu-crashed': [],
'plugin-crashed': ['name', 'version'],
'media-started-playing': [],
'media-paused': [],
'did-change-theme-color': ['themeColor'],
'destroyed': [],
'page-title-updated': ['title', 'explicitSet'],
'page-favicon-updated': ['favicons'],
'enter-html-full-screen': [],
'leave-html-full-screen': [],
'found-in-page': ['result']
};
DEPRECATED_EVENTS = {
'page-title-updated': 'page-title-set'
};
dispatchEvent = function() {
var args, domEvent, eventKey, eventName, f, i, j, len, ref1, webView;
webView = arguments[0], eventName = arguments[1], eventKey = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
if (DEPRECATED_EVENTS[eventName] != null) {
dispatchEvent.apply(null, [webView, DEPRECATED_EVENTS[eventName], eventKey].concat(slice.call(args)));
}
domEvent = new Event(eventName);
ref1 = WEB_VIEW_EVENTS[eventKey];
for (i = j = 0, len = ref1.length; j < len; i = ++j) {
f = ref1[i];
domEvent[f] = args[i];
}
webView.dispatchEvent(domEvent);
if (eventName === 'load-commit') {
return webView.onLoadCommit(domEvent);
}
};
module.exports = {
registerEvents: function(webView, viewInstanceId) {
ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + viewInstanceId, function() {
var args, event, eventName;
event = arguments[0], eventName = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
return dispatchEvent.apply(null, [webView, eventName, eventName].concat(slice.call(args)));
});
ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + viewInstanceId, function() {
var args, channel, domEvent, event;
event = arguments[0], channel = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
domEvent = new Event('ipc-message');
domEvent.channel = channel;
domEvent.args = slice.call(args);
return webView.dispatchEvent(domEvent);
});
return ipcRenderer.on("ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + viewInstanceId, function() {
var args, domEvent, event, f, i, j, len, ref1;
event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
domEvent = new Event('size-changed');
ref1 = ['oldWidth', 'oldHeight', 'newWidth', 'newHeight'];
for (i = j = 0, len = ref1.length; j < len; i = ++j) {
f = ref1[i];
domEvent[f] = args[i];
}
return webView.onSizeChanged(domEvent);
});
},
deregisterEvents: function(viewInstanceId) {
ipcRenderer.removeAllListeners("ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + viewInstanceId);
ipcRenderer.removeAllListeners("ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + viewInstanceId);
return ipcRenderer.removeAllListeners("ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + viewInstanceId);
},
createGuest: function(params, callback) {
requestId++;
ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId);
return ipcRenderer.once("ATOM_SHELL_RESPONSE_" + requestId, callback);
},
attachGuest: function(elementInstanceId, guestInstanceId, params) {
ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', elementInstanceId, guestInstanceId, params);
return webFrame.attachGuest(elementInstanceId);
},
destroyGuest: function(guestInstanceId) {
return ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId);
},
setSize: function(guestInstanceId, params) {
return ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', guestInstanceId, params);
},
setAllowTransparency: function(guestInstanceId, allowtransparency) {
return ipcRenderer.send('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', guestInstanceId, allowtransparency);
}
};

View file

@ -1,240 +0,0 @@
WebViewImpl = require './web-view'
guestViewInternal = require './guest-view-internal'
webViewConstants = require './web-view-constants'
{remote} = require 'electron'
### Helper function to resolve url set in attribute. ###
a = document.createElement 'a'
resolveURL = (url) ->
a.href = url
a.href
###
Attribute objects.
Default implementation of a WebView attribute.
###
class WebViewAttribute
constructor: (name, webViewImpl) ->
@name = name
@webViewImpl = webViewImpl
@ignoreMutation = false
@defineProperty()
### Retrieves and returns the attribute's value. ###
getValue: -> @webViewImpl.webviewNode.getAttribute(@name) || ''
### Sets the attribute's value. ###
setValue: (value) -> @webViewImpl.webviewNode.setAttribute(@name, value || '')
### Changes the attribute's value without triggering its mutation handler. ###
setValueIgnoreMutation: (value) ->
@ignoreMutation = true
@setValue value
@ignoreMutation = false
### Defines this attribute as a property on the webview node. ###
defineProperty: ->
Object.defineProperty @webViewImpl.webviewNode, @name,
get: => @getValue()
set: (value) => @setValue value
enumerable: true
### Called when the attribute's value changes. ###
handleMutation: ->
### An attribute that is treated as a Boolean. ###
class BooleanAttribute extends WebViewAttribute
constructor: (name, webViewImpl) ->
super name, webViewImpl
getValue: -> @webViewImpl.webviewNode.hasAttribute @name
setValue: (value) ->
unless value
@webViewImpl.webviewNode.removeAttribute @name
else
@webViewImpl.webviewNode.setAttribute @name, ''
### Attribute that specifies whether transparency is allowed in the webview. ###
class AllowTransparencyAttribute extends BooleanAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, webViewImpl
handleMutation: (oldValue, newValue) ->
return unless @webViewImpl.guestInstanceId
guestViewInternal.setAllowTransparency @webViewImpl.guestInstanceId, @getValue()
### Attribute used to define the demension limits of autosizing. ###
class AutosizeDimensionAttribute extends WebViewAttribute
constructor: (name, webViewImpl) ->
super name, webViewImpl
getValue: -> parseInt(@webViewImpl.webviewNode.getAttribute(@name)) || 0
handleMutation: (oldValue, newValue) ->
return unless @webViewImpl.guestInstanceId
guestViewInternal.setSize @webViewImpl.guestInstanceId,
enableAutoSize: @webViewImpl.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue()
min:
width: parseInt @webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0
height: parseInt @webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0
max:
width: parseInt @webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0
height: parseInt @webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0
### Attribute that specifies whether the webview should be autosized. ###
class AutosizeAttribute extends BooleanAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl
handleMutation: AutosizeDimensionAttribute::handleMutation
### Attribute representing the state of the storage partition. ###
class PartitionAttribute extends WebViewAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_PARTITION, webViewImpl
@validPartitionId = true
handleMutation: (oldValue, newValue) ->
newValue = newValue || ''
### The partition cannot change if the webview has already navigated. ###
unless @webViewImpl.beforeFirstNavigation
window.console.error webViewConstants.ERROR_MSG_ALREADY_NAVIGATED
@setValueIgnoreMutation oldValue
return
if newValue is 'persist:'
@validPartitionId = false
window.console.error webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
### Attribute that handles the location and navigation of the webview. ###
class SrcAttribute extends WebViewAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_SRC, webViewImpl
@setupMutationObserver()
getValue: ->
if @webViewImpl.webviewNode.hasAttribute @name
resolveURL @webViewImpl.webviewNode.getAttribute(@name)
else
''
setValueIgnoreMutation: (value) ->
WebViewAttribute::setValueIgnoreMutation.call(this, value)
###
takeRecords() is needed to clear queued up src mutations. Without it, it
is possible for this change to get picked up asyncronously by src's
mutation observer |observer|, and then get handled even though we do not
want to handle this mutation.
###
@observer.takeRecords()
handleMutation: (oldValue, newValue) ->
###
Once we have navigated, we don't allow clearing the src attribute.
Once <webview> enters a navigated state, it cannot return to a
placeholder state.
###
if not newValue and oldValue
###
src attribute changes normally initiate a navigation. We suppress
the next src attribute handler call to avoid reloading the page
on every guest-initiated navigation.
###
@setValueIgnoreMutation oldValue
return
@parse()
###
The purpose of this mutation observer is to catch assignment to the src
attribute without any changes to its value. This is useful in the case
where the webview guest has crashed and navigating to the same address
spawns off a new process.
###
setupMutationObserver: ->
@observer = new MutationObserver (mutations) =>
for mutation in mutations
oldValue = mutation.oldValue
newValue = @getValue()
return if oldValue isnt newValue
@handleMutation oldValue, newValue
params =
attributes: true,
attributeOldValue: true,
attributeFilter: [@name]
@observer.observe @webViewImpl.webviewNode, params
parse: ->
if not @webViewImpl.elementAttached or
not @webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId or
not @.getValue()
return
unless @webViewImpl.guestInstanceId?
if @webViewImpl.beforeFirstNavigation
@webViewImpl.beforeFirstNavigation = false
@webViewImpl.createGuest()
return
### Navigate to |this.src|. ###
opts = {}
httpreferrer = @webViewImpl.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
if httpreferrer then opts.httpReferrer = httpreferrer
useragent = @webViewImpl.attributes[webViewConstants.ATTRIBUTE_USERAGENT].getValue()
if useragent then opts.userAgent = useragent
guestContents = remote.getGuestWebContents(@webViewImpl.guestInstanceId)
guestContents.loadURL @getValue(), opts
### Attribute specifies HTTP referrer. ###
class HttpReferrerAttribute extends WebViewAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl
### Attribute specifies user agent ###
class UserAgentAttribute extends WebViewAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_USERAGENT, webViewImpl
### Attribute that set preload script. ###
class PreloadAttribute extends WebViewAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_PRELOAD, webViewImpl
getValue: ->
return '' unless @webViewImpl.webviewNode.hasAttribute @name
preload = resolveURL @webViewImpl.webviewNode.getAttribute(@name)
protocol = preload.substr 0, 5
unless protocol is 'file:'
console.error webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE
preload = ''
preload
### Sets up all of the webview attributes. ###
WebViewImpl::setupWebViewAttributes = ->
@attributes = {}
@attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] = new AllowTransparencyAttribute(this)
@attributes[webViewConstants.ATTRIBUTE_AUTOSIZE] = new AutosizeAttribute(this)
@attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this)
@attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this)
@attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
@attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this)
@attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this)
@attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this)
@attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this)
@attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
@attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
autosizeAttributes = [
webViewConstants.ATTRIBUTE_MAXHEIGHT
webViewConstants.ATTRIBUTE_MAXWIDTH
webViewConstants.ATTRIBUTE_MINHEIGHT
webViewConstants.ATTRIBUTE_MINWIDTH
]
for attribute in autosizeAttributes
@attributes[attribute] = new AutosizeDimensionAttribute(attribute, this)

View file

@ -0,0 +1,410 @@
var AllowTransparencyAttribute, AutosizeAttribute, AutosizeDimensionAttribute, BooleanAttribute, HttpReferrerAttribute, PartitionAttribute, PreloadAttribute, SrcAttribute, UserAgentAttribute, WebViewAttribute, WebViewImpl, a, guestViewInternal, remote, resolveURL, webViewConstants,
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;
WebViewImpl = require('./web-view');
guestViewInternal = require('./guest-view-internal');
webViewConstants = require('./web-view-constants');
remote = require('electron').remote;
/* Helper function to resolve url set in attribute. */
a = document.createElement('a');
resolveURL = function(url) {
a.href = url;
return a.href;
};
/*
Attribute objects.
Default implementation of a WebView attribute.
*/
WebViewAttribute = (function() {
function WebViewAttribute(name, webViewImpl) {
this.name = name;
this.webViewImpl = webViewImpl;
this.ignoreMutation = false;
this.defineProperty();
}
/* Retrieves and returns the attribute's value. */
WebViewAttribute.prototype.getValue = function() {
return this.webViewImpl.webviewNode.getAttribute(this.name) || '';
};
/* Sets the attribute's value. */
WebViewAttribute.prototype.setValue = function(value) {
return this.webViewImpl.webviewNode.setAttribute(this.name, value || '');
};
/* Changes the attribute's value without triggering its mutation handler. */
WebViewAttribute.prototype.setValueIgnoreMutation = function(value) {
this.ignoreMutation = true;
this.setValue(value);
return this.ignoreMutation = false;
};
/* Defines this attribute as a property on the webview node. */
WebViewAttribute.prototype.defineProperty = function() {
return Object.defineProperty(this.webViewImpl.webviewNode, this.name, {
get: (function(_this) {
return function() {
return _this.getValue();
};
})(this),
set: (function(_this) {
return function(value) {
return _this.setValue(value);
};
})(this),
enumerable: true
});
};
/* Called when the attribute's value changes. */
WebViewAttribute.prototype.handleMutation = function() {};
return WebViewAttribute;
})();
/* An attribute that is treated as a Boolean. */
BooleanAttribute = (function(superClass) {
extend(BooleanAttribute, superClass);
function BooleanAttribute(name, webViewImpl) {
BooleanAttribute.__super__.constructor.call(this, name, webViewImpl);
}
BooleanAttribute.prototype.getValue = function() {
return this.webViewImpl.webviewNode.hasAttribute(this.name);
};
BooleanAttribute.prototype.setValue = function(value) {
if (!value) {
return this.webViewImpl.webviewNode.removeAttribute(this.name);
} else {
return this.webViewImpl.webviewNode.setAttribute(this.name, '');
}
};
return BooleanAttribute;
})(WebViewAttribute);
/* Attribute that specifies whether transparency is allowed in the webview. */
AllowTransparencyAttribute = (function(superClass) {
extend(AllowTransparencyAttribute, superClass);
function AllowTransparencyAttribute(webViewImpl) {
AllowTransparencyAttribute.__super__.constructor.call(this, webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, webViewImpl);
}
AllowTransparencyAttribute.prototype.handleMutation = function(oldValue, newValue) {
if (!this.webViewImpl.guestInstanceId) {
return;
}
return guestViewInternal.setAllowTransparency(this.webViewImpl.guestInstanceId, this.getValue());
};
return AllowTransparencyAttribute;
})(BooleanAttribute);
/* Attribute used to define the demension limits of autosizing. */
AutosizeDimensionAttribute = (function(superClass) {
extend(AutosizeDimensionAttribute, superClass);
function AutosizeDimensionAttribute(name, webViewImpl) {
AutosizeDimensionAttribute.__super__.constructor.call(this, name, webViewImpl);
}
AutosizeDimensionAttribute.prototype.getValue = function() {
return parseInt(this.webViewImpl.webviewNode.getAttribute(this.name)) || 0;
};
AutosizeDimensionAttribute.prototype.handleMutation = function(oldValue, newValue) {
if (!this.webViewImpl.guestInstanceId) {
return;
}
return guestViewInternal.setSize(this.webViewImpl.guestInstanceId, {
enableAutoSize: this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue(),
min: {
width: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0),
height: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0)
},
max: {
width: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0),
height: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0)
}
});
};
return AutosizeDimensionAttribute;
})(WebViewAttribute);
/* Attribute that specifies whether the webview should be autosized. */
AutosizeAttribute = (function(superClass) {
extend(AutosizeAttribute, superClass);
function AutosizeAttribute(webViewImpl) {
AutosizeAttribute.__super__.constructor.call(this, webViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl);
}
AutosizeAttribute.prototype.handleMutation = AutosizeDimensionAttribute.prototype.handleMutation;
return AutosizeAttribute;
})(BooleanAttribute);
/* Attribute representing the state of the storage partition. */
PartitionAttribute = (function(superClass) {
extend(PartitionAttribute, superClass);
function PartitionAttribute(webViewImpl) {
PartitionAttribute.__super__.constructor.call(this, webViewConstants.ATTRIBUTE_PARTITION, webViewImpl);
this.validPartitionId = true;
}
PartitionAttribute.prototype.handleMutation = function(oldValue, newValue) {
newValue = newValue || '';
/* The partition cannot change if the webview has already navigated. */
if (!this.webViewImpl.beforeFirstNavigation) {
window.console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED);
this.setValueIgnoreMutation(oldValue);
return;
}
if (newValue === 'persist:') {
this.validPartitionId = false;
return window.console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE);
}
};
return PartitionAttribute;
})(WebViewAttribute);
/* Attribute that handles the location and navigation of the webview. */
SrcAttribute = (function(superClass) {
extend(SrcAttribute, superClass);
function SrcAttribute(webViewImpl) {
SrcAttribute.__super__.constructor.call(this, webViewConstants.ATTRIBUTE_SRC, webViewImpl);
this.setupMutationObserver();
}
SrcAttribute.prototype.getValue = function() {
if (this.webViewImpl.webviewNode.hasAttribute(this.name)) {
return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name));
} else {
return '';
}
};
SrcAttribute.prototype.setValueIgnoreMutation = function(value) {
WebViewAttribute.prototype.setValueIgnoreMutation.call(this, value);
/*
takeRecords() is needed to clear queued up src mutations. Without it, it
is possible for this change to get picked up asyncronously by src's
mutation observer |observer|, and then get handled even though we do not
want to handle this mutation.
*/
return this.observer.takeRecords();
};
SrcAttribute.prototype.handleMutation = function(oldValue, newValue) {
/*
Once we have navigated, we don't allow clearing the src attribute.
Once <webview> enters a navigated state, it cannot return to a
placeholder state.
*/
if (!newValue && oldValue) {
/*
src attribute changes normally initiate a navigation. We suppress
the next src attribute handler call to avoid reloading the page
on every guest-initiated navigation.
*/
this.setValueIgnoreMutation(oldValue);
return;
}
return this.parse();
};
/*
The purpose of this mutation observer is to catch assignment to the src
attribute without any changes to its value. This is useful in the case
where the webview guest has crashed and navigating to the same address
spawns off a new process.
*/
SrcAttribute.prototype.setupMutationObserver = function() {
var params;
this.observer = new MutationObserver((function(_this) {
return function(mutations) {
var i, len, mutation, newValue, oldValue;
for (i = 0, len = mutations.length; i < len; i++) {
mutation = mutations[i];
oldValue = mutation.oldValue;
newValue = _this.getValue();
if (oldValue !== newValue) {
return;
}
_this.handleMutation(oldValue, newValue);
}
};
})(this));
params = {
attributes: true,
attributeOldValue: true,
attributeFilter: [this.name]
};
return this.observer.observe(this.webViewImpl.webviewNode, params);
};
SrcAttribute.prototype.parse = function() {
var guestContents, httpreferrer, opts, useragent;
if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) {
return;
}
if (this.webViewImpl.guestInstanceId == null) {
if (this.webViewImpl.beforeFirstNavigation) {
this.webViewImpl.beforeFirstNavigation = false;
this.webViewImpl.createGuest();
}
return;
}
/* Navigate to |this.src|. */
opts = {};
httpreferrer = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue();
if (httpreferrer) {
opts.httpReferrer = httpreferrer;
}
useragent = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_USERAGENT].getValue();
if (useragent) {
opts.userAgent = useragent;
}
guestContents = remote.getGuestWebContents(this.webViewImpl.guestInstanceId);
return guestContents.loadURL(this.getValue(), opts);
};
return SrcAttribute;
})(WebViewAttribute);
/* Attribute specifies HTTP referrer. */
HttpReferrerAttribute = (function(superClass) {
extend(HttpReferrerAttribute, superClass);
function HttpReferrerAttribute(webViewImpl) {
HttpReferrerAttribute.__super__.constructor.call(this, webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl);
}
return HttpReferrerAttribute;
})(WebViewAttribute);
/* Attribute specifies user agent */
UserAgentAttribute = (function(superClass) {
extend(UserAgentAttribute, superClass);
function UserAgentAttribute(webViewImpl) {
UserAgentAttribute.__super__.constructor.call(this, webViewConstants.ATTRIBUTE_USERAGENT, webViewImpl);
}
return UserAgentAttribute;
})(WebViewAttribute);
/* Attribute that set preload script. */
PreloadAttribute = (function(superClass) {
extend(PreloadAttribute, superClass);
function PreloadAttribute(webViewImpl) {
PreloadAttribute.__super__.constructor.call(this, webViewConstants.ATTRIBUTE_PRELOAD, webViewImpl);
}
PreloadAttribute.prototype.getValue = function() {
var preload, protocol;
if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) {
return '';
}
preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name));
protocol = preload.substr(0, 5);
if (protocol !== 'file:') {
console.error(webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE);
preload = '';
}
return preload;
};
return PreloadAttribute;
})(WebViewAttribute);
/* Sets up all of the webview attributes. */
WebViewImpl.prototype.setupWebViewAttributes = function() {
var attribute, autosizeAttributes, i, len, results;
this.attributes = {};
this.attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] = new AllowTransparencyAttribute(this);
this.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE] = new AutosizeAttribute(this);
this.attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this);
this.attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this);
this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this);
this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this);
this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this);
this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this);
this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this);
this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this);
this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this);
autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH];
results = [];
for (i = 0, len = autosizeAttributes.length; i < len; i++) {
attribute = autosizeAttributes[i];
results.push(this.attributes[attribute] = new AutosizeDimensionAttribute(attribute, this));
}
return results;
};

View file

@ -1,28 +0,0 @@
module.exports =
### Attributes. ###
ATTRIBUTE_ALLOWTRANSPARENCY: 'allowtransparency'
ATTRIBUTE_AUTOSIZE: 'autosize'
ATTRIBUTE_MAXHEIGHT: 'maxheight'
ATTRIBUTE_MAXWIDTH: 'maxwidth'
ATTRIBUTE_MINHEIGHT: 'minheight'
ATTRIBUTE_MINWIDTH: 'minwidth'
ATTRIBUTE_NAME: 'name'
ATTRIBUTE_PARTITION: 'partition'
ATTRIBUTE_SRC: 'src'
ATTRIBUTE_HTTPREFERRER: 'httpreferrer'
ATTRIBUTE_NODEINTEGRATION: 'nodeintegration'
ATTRIBUTE_PLUGINS: 'plugins'
ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity'
ATTRIBUTE_ALLOWPOPUPS: 'allowpopups'
ATTRIBUTE_PRELOAD: 'preload'
ATTRIBUTE_USERAGENT: 'useragent'
### Internal attribute. ###
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid'
### Error messages. ###
ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.'
ERROR_MSG_CANNOT_INJECT_SCRIPT: '<webview>: ' +
'Script cannot be injected into content until the page has loaded.'
ERROR_MSG_INVALID_PARTITION_ATTRIBUTE: 'Invalid partition attribute.'
ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE: 'Only "file:" protocol is supported in "preload" attribute.'

View file

@ -0,0 +1,29 @@
module.exports = {
/* Attributes. */
ATTRIBUTE_ALLOWTRANSPARENCY: 'allowtransparency',
ATTRIBUTE_AUTOSIZE: 'autosize',
ATTRIBUTE_MAXHEIGHT: 'maxheight',
ATTRIBUTE_MAXWIDTH: 'maxwidth',
ATTRIBUTE_MINHEIGHT: 'minheight',
ATTRIBUTE_MINWIDTH: 'minwidth',
ATTRIBUTE_NAME: 'name',
ATTRIBUTE_PARTITION: 'partition',
ATTRIBUTE_SRC: 'src',
ATTRIBUTE_HTTPREFERRER: 'httpreferrer',
ATTRIBUTE_NODEINTEGRATION: 'nodeintegration',
ATTRIBUTE_PLUGINS: 'plugins',
ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity',
ATTRIBUTE_ALLOWPOPUPS: 'allowpopups',
ATTRIBUTE_PRELOAD: 'preload',
ATTRIBUTE_USERAGENT: 'useragent',
/* Internal attribute. */
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',
/* Error messages. */
ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.',
ERROR_MSG_CANNOT_INJECT_SCRIPT: '<webview>: ' + 'Script cannot be injected into content until the page has loaded.',
ERROR_MSG_INVALID_PARTITION_ATTRIBUTE: 'Invalid partition attribute.',
ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE: 'Only "file:" protocol is supported in "preload" attribute.'
};

View file

@ -1,356 +0,0 @@
{deprecate, webFrame, remote, ipcRenderer} = require 'electron'
v8Util = process.atomBinding 'v8_util'
guestViewInternal = require './guest-view-internal'
webViewConstants = require './web-view-constants'
### ID generator. ###
nextId = 0
getNextId = -> ++nextId
### Represents the internal state of the WebView node. ###
class WebViewImpl
constructor: (@webviewNode) ->
v8Util.setHiddenValue @webviewNode, 'internal', this
@attached = false
@elementAttached = false
@beforeFirstNavigation = true
### on* Event handlers. ###
@on = {}
@browserPluginNode = @createBrowserPluginNode()
shadowRoot = @webviewNode.createShadowRoot()
@setupWebViewAttributes()
@setupFocusPropagation()
@viewInstanceId = getNextId()
shadowRoot.appendChild @browserPluginNode
createBrowserPluginNode: ->
###
We create BrowserPlugin as a custom element in order to observe changes
to attributes synchronously.
###
browserPluginNode = new WebViewImpl.BrowserPlugin()
v8Util.setHiddenValue browserPluginNode, 'internal', this
browserPluginNode
### Resets some state upon reattaching <webview> element to the DOM. ###
reset: ->
###
If guestInstanceId is defined then the <webview> has navigated and has
already picked up a partition ID. Thus, we need to reset the initialization
state. However, it may be the case that beforeFirstNavigation is false BUT
guestInstanceId has yet to be initialized. This means that we have not
heard back from createGuest yet. We will not reset the flag in this case so
that we don't end up allocating a second guest.
###
if @guestInstanceId
guestViewInternal.destroyGuest @guestInstanceId
@webContents = null
@guestInstanceId = undefined
@beforeFirstNavigation = true
@attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true
@internalInstanceId = 0
### Sets the <webview>.request property. ###
setRequestPropertyOnWebViewNode: (request) ->
Object.defineProperty @webviewNode, 'request', value: request, enumerable: true
setupFocusPropagation: ->
unless @webviewNode.hasAttribute 'tabIndex'
###
<webview> needs a tabIndex in order to be focusable.
TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
to allow <webview> to be focusable.
See http://crbug.com/231664.
###
@webviewNode.setAttribute 'tabIndex', -1
@webviewNode.addEventListener 'focus', (e) =>
### Focus the BrowserPlugin when the <webview> takes focus. ###
@browserPluginNode.focus()
@webviewNode.addEventListener 'blur', (e) =>
### Blur the BrowserPlugin when the <webview> loses focus. ###
@browserPluginNode.blur()
###
This observer monitors mutations to attributes of the <webview> and
updates the BrowserPlugin properties accordingly. In turn, updating
a BrowserPlugin property will update the corresponding BrowserPlugin
attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
details.
###
handleWebviewAttributeMutation: (attributeName, oldValue, newValue) ->
if not @attributes[attributeName] or @attributes[attributeName].ignoreMutation
return
### Let the changed attribute handle its own mutation; ###
@attributes[attributeName].handleMutation oldValue, newValue
handleBrowserPluginAttributeMutation: (attributeName, oldValue, newValue) ->
if attributeName is webViewConstants.ATTRIBUTE_INTERNALINSTANCEID and !oldValue and !!newValue
@browserPluginNode.removeAttribute webViewConstants.ATTRIBUTE_INTERNALINSTANCEID
@internalInstanceId = parseInt newValue
### Track when the element resizes using the element resize callback. ###
webFrame.registerElementResizeCallback @internalInstanceId, @onElementResize.bind(this)
return unless @guestInstanceId
guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, @buildParams()
onSizeChanged: (webViewEvent) ->
newWidth = webViewEvent.newWidth
newHeight = webViewEvent.newHeight
node = @webviewNode
width = node.offsetWidth
height = node.offsetHeight
### Check the current bounds to make sure we do not resize <webview> ###
### outside of current constraints. ###
maxWidth = @attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() | width
maxHeight = @attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() | width
minWidth = @attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() | width
minHeight = @attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() | width
minWidth = Math.min minWidth, maxWidth
minHeight = Math.min minHeight, maxHeight
if not @attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue() or
(newWidth >= minWidth and
newWidth <= maxWidth and
newHeight >= minHeight and
newHeight <= maxHeight)
node.style.width = newWidth + 'px'
node.style.height = newHeight + 'px'
###
Only fire the DOM event if the size of the <webview> has actually
changed.
###
@dispatchEvent webViewEvent
onElementResize: (newSize) ->
### Dispatch the 'resize' event. ###
resizeEvent = new Event('resize', bubbles: true)
resizeEvent.newWidth = newSize.width
resizeEvent.newHeight = newSize.height
@dispatchEvent resizeEvent
if @guestInstanceId
guestViewInternal.setSize @guestInstanceId, normal: newSize
createGuest: ->
guestViewInternal.createGuest @buildParams(), (event, guestInstanceId) =>
@attachWindow guestInstanceId
dispatchEvent: (webViewEvent) ->
@webviewNode.dispatchEvent webViewEvent
### Adds an 'on<event>' property on the webview, which can be used to set/unset ###
### an event handler. ###
setupEventProperty: (eventName) ->
propertyName = 'on' + eventName.toLowerCase()
Object.defineProperty @webviewNode, propertyName,
get: => @on[propertyName]
set: (value) =>
if @on[propertyName]
@webviewNode.removeEventListener eventName, @on[propertyName]
@on[propertyName] = value
if value
@webviewNode.addEventListener eventName, value
enumerable: true
### Updates state upon loadcommit. ###
onLoadCommit: (webViewEvent) ->
oldValue = @webviewNode.getAttribute webViewConstants.ATTRIBUTE_SRC
newValue = webViewEvent.url
if webViewEvent.isMainFrame and (oldValue != newValue)
###
Touching the src attribute triggers a navigation. To avoid
triggering a page reload on every guest-initiated navigation,
we do not handle this mutation.
###
@attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation newValue
onAttach: (storagePartitionId) ->
@attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue storagePartitionId
buildParams: ->
params =
instanceId: @viewInstanceId
userAgentOverride: @userAgentOverride
for own attributeName, attribute of @attributes
params[attributeName] = attribute.getValue()
###
When the WebView is not participating in layout (display:none)
then getBoundingClientRect() would report a width and height of 0.
However, in the case where the WebView has a fixed size we can
use that value to initially size the guest so as to avoid a relayout of
the on display:block.
###
css = window.getComputedStyle @webviewNode, null
elementRect = @webviewNode.getBoundingClientRect()
params.elementWidth = parseInt(elementRect.width) ||
parseInt(css.getPropertyValue('width'))
params.elementHeight = parseInt(elementRect.height) ||
parseInt(css.getPropertyValue('height'))
params
attachWindow: (guestInstanceId) ->
@guestInstanceId = guestInstanceId
@webContents = remote.getGuestWebContents @guestInstanceId
return true unless @internalInstanceId
guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, @buildParams()
### Registers browser plugin <object> custom element. ###
registerBrowserPluginElement = ->
proto = Object.create HTMLObjectElement.prototype
proto.createdCallback = ->
@setAttribute 'type', 'application/browser-plugin'
@setAttribute 'id', 'browser-plugin-' + getNextId()
### The <object> node fills in the <webview> container. ###
@style.display = 'block'
@style.width = '100%'
@style.height = '100%'
proto.attributeChangedCallback = (name, oldValue, newValue) ->
internal = v8Util.getHiddenValue this, 'internal'
return unless internal
internal.handleBrowserPluginAttributeMutation name, oldValue, newValue
proto.attachedCallback = ->
### Load the plugin immediately. ###
unused = this.nonExistentAttribute
WebViewImpl.BrowserPlugin = webFrame.registerEmbedderCustomElement 'browserplugin',
extends: 'object', prototype: proto
delete proto.createdCallback
delete proto.attachedCallback
delete proto.detachedCallback
delete proto.attributeChangedCallback
### Registers <webview> custom element. ###
registerWebViewElement = ->
proto = Object.create HTMLObjectElement.prototype
proto.createdCallback = ->
new WebViewImpl(this)
proto.attributeChangedCallback = (name, oldValue, newValue) ->
internal = v8Util.getHiddenValue this, 'internal'
return unless internal
internal.handleWebviewAttributeMutation name, oldValue, newValue
proto.detachedCallback = ->
internal = v8Util.getHiddenValue this, 'internal'
return unless internal
guestViewInternal.deregisterEvents internal.viewInstanceId
internal.elementAttached = false
internal.reset()
proto.attachedCallback = ->
internal = v8Util.getHiddenValue this, 'internal'
return unless internal
unless internal.elementAttached
guestViewInternal.registerEvents internal, internal.viewInstanceId
internal.elementAttached = true
internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse()
### Public-facing API methods. ###
methods = [
'getURL'
'getTitle'
'isLoading'
'isWaitingForResponse'
'stop'
'reload'
'reloadIgnoringCache'
'canGoBack'
'canGoForward'
'canGoToOffset'
'clearHistory'
'goBack'
'goForward'
'goToIndex'
'goToOffset'
'isCrashed'
'setUserAgent'
'getUserAgent'
'openDevTools'
'closeDevTools'
'isDevToolsOpened'
'isDevToolsFocused'
'inspectElement'
'setAudioMuted'
'isAudioMuted'
'undo'
'redo'
'cut'
'copy'
'paste'
'pasteAndMatchStyle'
'delete'
'selectAll'
'unselect'
'replace'
'replaceMisspelling'
'findInPage'
'stopFindInPage'
'getId'
'downloadURL'
'inspectServiceWorker'
'print'
'printToPDF'
]
nonblockMethods = [
'send',
'sendInputEvent',
'executeJavaScript',
'insertCSS'
]
### Forward proto.foo* method calls to WebViewImpl.foo*. ###
createBlockHandler = (m) ->
(args...) ->
internal = v8Util.getHiddenValue this, 'internal'
internal.webContents[m] args...
proto[m] = createBlockHandler m for m in methods
createNonBlockHandler = (m) ->
(args...) ->
internal = v8Util.getHiddenValue this, 'internal'
ipcRenderer.send('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', internal.guestInstanceId, m, args...)
proto[m] = createNonBlockHandler m for m in nonblockMethods
### Deprecated. ###
deprecate.rename proto, 'getUrl', 'getURL'
window.WebView = webFrame.registerEmbedderCustomElement 'webview',
prototype: proto
### Delete the callbacks so developers cannot call them and produce unexpected ###
### behavior. ###
delete proto.createdCallback
delete proto.attachedCallback
delete proto.detachedCallback
delete proto.attributeChangedCallback
useCapture = true
listener = (event) ->
return if document.readyState == 'loading'
registerBrowserPluginElement()
registerWebViewElement()
window.removeEventListener event.type, listener, useCapture
window.addEventListener 'readystatechange', listener, true
module.exports = WebViewImpl

View file

@ -0,0 +1,435 @@
var WebViewImpl, deprecate, getNextId, guestViewInternal, ipcRenderer, listener, nextId, ref, registerBrowserPluginElement, registerWebViewElement, remote, useCapture, v8Util, webFrame, webViewConstants,
hasProp = {}.hasOwnProperty,
slice = [].slice;
ref = require('electron'), deprecate = ref.deprecate, webFrame = ref.webFrame, remote = ref.remote, ipcRenderer = ref.ipcRenderer;
v8Util = process.atomBinding('v8_util');
guestViewInternal = require('./guest-view-internal');
webViewConstants = require('./web-view-constants');
/* ID generator. */
nextId = 0;
getNextId = function() {
return ++nextId;
};
/* Represents the internal state of the WebView node. */
WebViewImpl = (function() {
function WebViewImpl(webviewNode) {
var shadowRoot;
this.webviewNode = webviewNode;
v8Util.setHiddenValue(this.webviewNode, 'internal', this);
this.attached = false;
this.elementAttached = false;
this.beforeFirstNavigation = true;
/* on* Event handlers. */
this.on = {};
this.browserPluginNode = this.createBrowserPluginNode();
shadowRoot = this.webviewNode.createShadowRoot();
this.setupWebViewAttributes();
this.setupFocusPropagation();
this.viewInstanceId = getNextId();
shadowRoot.appendChild(this.browserPluginNode);
}
WebViewImpl.prototype.createBrowserPluginNode = function() {
/*
We create BrowserPlugin as a custom element in order to observe changes
to attributes synchronously.
*/
var browserPluginNode;
browserPluginNode = new WebViewImpl.BrowserPlugin();
v8Util.setHiddenValue(browserPluginNode, 'internal', this);
return browserPluginNode;
};
/* Resets some state upon reattaching <webview> element to the DOM. */
WebViewImpl.prototype.reset = function() {
/*
If guestInstanceId is defined then the <webview> has navigated and has
already picked up a partition ID. Thus, we need to reset the initialization
state. However, it may be the case that beforeFirstNavigation is false BUT
guestInstanceId has yet to be initialized. This means that we have not
heard back from createGuest yet. We will not reset the flag in this case so
that we don't end up allocating a second guest.
*/
if (this.guestInstanceId) {
guestViewInternal.destroyGuest(this.guestInstanceId);
this.webContents = null;
this.guestInstanceId = void 0;
this.beforeFirstNavigation = true;
this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true;
}
return this.internalInstanceId = 0;
};
/* Sets the <webview>.request property. */
WebViewImpl.prototype.setRequestPropertyOnWebViewNode = function(request) {
return Object.defineProperty(this.webviewNode, 'request', {
value: request,
enumerable: true
});
};
WebViewImpl.prototype.setupFocusPropagation = function() {
if (!this.webviewNode.hasAttribute('tabIndex')) {
/*
<webview> needs a tabIndex in order to be focusable.
TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
to allow <webview> to be focusable.
See http://crbug.com/231664.
*/
this.webviewNode.setAttribute('tabIndex', -1);
}
this.webviewNode.addEventListener('focus', (function(_this) {
return function(e) {
/* Focus the BrowserPlugin when the <webview> takes focus. */
return _this.browserPluginNode.focus();
};
})(this));
return this.webviewNode.addEventListener('blur', (function(_this) {
return function(e) {
/* Blur the BrowserPlugin when the <webview> loses focus. */
return _this.browserPluginNode.blur();
};
})(this));
};
/*
This observer monitors mutations to attributes of the <webview> and
updates the BrowserPlugin properties accordingly. In turn, updating
a BrowserPlugin property will update the corresponding BrowserPlugin
attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
details.
*/
WebViewImpl.prototype.handleWebviewAttributeMutation = function(attributeName, oldValue, newValue) {
if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) {
return;
}
/* Let the changed attribute handle its own mutation; */
return this.attributes[attributeName].handleMutation(oldValue, newValue);
};
WebViewImpl.prototype.handleBrowserPluginAttributeMutation = function(attributeName, oldValue, newValue) {
if (attributeName === webViewConstants.ATTRIBUTE_INTERNALINSTANCEID && !oldValue && !!newValue) {
this.browserPluginNode.removeAttribute(webViewConstants.ATTRIBUTE_INTERNALINSTANCEID);
this.internalInstanceId = parseInt(newValue);
/* Track when the element resizes using the element resize callback. */
webFrame.registerElementResizeCallback(this.internalInstanceId, this.onElementResize.bind(this));
if (!this.guestInstanceId) {
return;
}
return guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams());
}
};
WebViewImpl.prototype.onSizeChanged = function(webViewEvent) {
var height, maxHeight, maxWidth, minHeight, minWidth, newHeight, newWidth, node, width;
newWidth = webViewEvent.newWidth;
newHeight = webViewEvent.newHeight;
node = this.webviewNode;
width = node.offsetWidth;
height = node.offsetHeight;
/* Check the current bounds to make sure we do not resize <webview> */
/* outside of current constraints. */
maxWidth = this.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() | width;
maxHeight = this.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() | width;
minWidth = this.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() | width;
minHeight = this.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() | width;
minWidth = Math.min(minWidth, maxWidth);
minHeight = Math.min(minHeight, maxHeight);
if (!this.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue() || (newWidth >= minWidth && newWidth <= maxWidth && newHeight >= minHeight && newHeight <= maxHeight)) {
node.style.width = newWidth + 'px';
node.style.height = newHeight + 'px';
/*
Only fire the DOM event if the size of the <webview> has actually
changed.
*/
return this.dispatchEvent(webViewEvent);
}
};
WebViewImpl.prototype.onElementResize = function(newSize) {
/* Dispatch the 'resize' event. */
var resizeEvent;
resizeEvent = new Event('resize', {
bubbles: true
});
resizeEvent.newWidth = newSize.width;
resizeEvent.newHeight = newSize.height;
this.dispatchEvent(resizeEvent);
if (this.guestInstanceId) {
return guestViewInternal.setSize(this.guestInstanceId, {
normal: newSize
});
}
};
WebViewImpl.prototype.createGuest = function() {
return guestViewInternal.createGuest(this.buildParams(), (function(_this) {
return function(event, guestInstanceId) {
return _this.attachWindow(guestInstanceId);
};
})(this));
};
WebViewImpl.prototype.dispatchEvent = function(webViewEvent) {
return this.webviewNode.dispatchEvent(webViewEvent);
};
/* Adds an 'on<event>' property on the webview, which can be used to set/unset */
/* an event handler. */
WebViewImpl.prototype.setupEventProperty = function(eventName) {
var propertyName;
propertyName = 'on' + eventName.toLowerCase();
return Object.defineProperty(this.webviewNode, propertyName, {
get: (function(_this) {
return function() {
return _this.on[propertyName];
};
})(this),
set: (function(_this) {
return function(value) {
if (_this.on[propertyName]) {
_this.webviewNode.removeEventListener(eventName, _this.on[propertyName]);
}
_this.on[propertyName] = value;
if (value) {
return _this.webviewNode.addEventListener(eventName, value);
}
};
})(this),
enumerable: true
});
};
/* Updates state upon loadcommit. */
WebViewImpl.prototype.onLoadCommit = function(webViewEvent) {
var newValue, oldValue;
oldValue = this.webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC);
newValue = webViewEvent.url;
if (webViewEvent.isMainFrame && (oldValue !== newValue)) {
/*
Touching the src attribute triggers a navigation. To avoid
triggering a page reload on every guest-initiated navigation,
we do not handle this mutation.
*/
return this.attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue);
}
};
WebViewImpl.prototype.onAttach = function(storagePartitionId) {
return this.attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue(storagePartitionId);
};
WebViewImpl.prototype.buildParams = function() {
var attribute, attributeName, css, elementRect, params, ref1;
params = {
instanceId: this.viewInstanceId,
userAgentOverride: this.userAgentOverride
};
ref1 = this.attributes;
for (attributeName in ref1) {
if (!hasProp.call(ref1, attributeName)) continue;
attribute = ref1[attributeName];
params[attributeName] = attribute.getValue();
}
/*
When the WebView is not participating in layout (display:none)
then getBoundingClientRect() would report a width and height of 0.
However, in the case where the WebView has a fixed size we can
use that value to initially size the guest so as to avoid a relayout of
the on display:block.
*/
css = window.getComputedStyle(this.webviewNode, null);
elementRect = this.webviewNode.getBoundingClientRect();
params.elementWidth = parseInt(elementRect.width) || parseInt(css.getPropertyValue('width'));
params.elementHeight = parseInt(elementRect.height) || parseInt(css.getPropertyValue('height'));
return params;
};
WebViewImpl.prototype.attachWindow = function(guestInstanceId) {
this.guestInstanceId = guestInstanceId;
this.webContents = remote.getGuestWebContents(this.guestInstanceId);
if (!this.internalInstanceId) {
return true;
}
return guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams());
};
return WebViewImpl;
})();
/* Registers browser plugin <object> custom element. */
registerBrowserPluginElement = function() {
var proto;
proto = Object.create(HTMLObjectElement.prototype);
proto.createdCallback = function() {
this.setAttribute('type', 'application/browser-plugin');
this.setAttribute('id', 'browser-plugin-' + getNextId());
/* The <object> node fills in the <webview> container. */
this.style.display = 'block';
this.style.width = '100%';
return this.style.height = '100%';
};
proto.attributeChangedCallback = function(name, oldValue, newValue) {
var internal;
internal = v8Util.getHiddenValue(this, 'internal');
if (!internal) {
return;
}
return internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue);
};
proto.attachedCallback = function() {
/* Load the plugin immediately. */
var unused;
return unused = this.nonExistentAttribute;
};
WebViewImpl.BrowserPlugin = webFrame.registerEmbedderCustomElement('browserplugin', {
"extends": 'object',
prototype: proto
});
delete proto.createdCallback;
delete proto.attachedCallback;
delete proto.detachedCallback;
return delete proto.attributeChangedCallback;
};
/* Registers <webview> custom element. */
registerWebViewElement = function() {
var createBlockHandler, createNonBlockHandler, i, j, len, len1, m, methods, nonblockMethods, proto;
proto = Object.create(HTMLObjectElement.prototype);
proto.createdCallback = function() {
return new WebViewImpl(this);
};
proto.attributeChangedCallback = function(name, oldValue, newValue) {
var internal;
internal = v8Util.getHiddenValue(this, 'internal');
if (!internal) {
return;
}
return internal.handleWebviewAttributeMutation(name, oldValue, newValue);
};
proto.detachedCallback = function() {
var internal;
internal = v8Util.getHiddenValue(this, 'internal');
if (!internal) {
return;
}
guestViewInternal.deregisterEvents(internal.viewInstanceId);
internal.elementAttached = false;
return internal.reset();
};
proto.attachedCallback = function() {
var internal;
internal = v8Util.getHiddenValue(this, 'internal');
if (!internal) {
return;
}
if (!internal.elementAttached) {
guestViewInternal.registerEvents(internal, internal.viewInstanceId);
internal.elementAttached = true;
return internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse();
}
};
/* Public-facing API methods. */
methods = ['getURL', 'getTitle', 'isLoading', 'isWaitingForResponse', 'stop', 'reload', 'reloadIgnoringCache', 'canGoBack', 'canGoForward', 'canGoToOffset', 'clearHistory', 'goBack', 'goForward', 'goToIndex', 'goToOffset', 'isCrashed', 'setUserAgent', 'getUserAgent', 'openDevTools', 'closeDevTools', 'isDevToolsOpened', 'isDevToolsFocused', 'inspectElement', 'setAudioMuted', 'isAudioMuted', 'undo', 'redo', 'cut', 'copy', 'paste', 'pasteAndMatchStyle', 'delete', 'selectAll', 'unselect', 'replace', 'replaceMisspelling', 'findInPage', 'stopFindInPage', 'getId', 'downloadURL', 'inspectServiceWorker', 'print', 'printToPDF'];
nonblockMethods = ['send', 'sendInputEvent', 'executeJavaScript', 'insertCSS'];
/* Forward proto.foo* method calls to WebViewImpl.foo*. */
createBlockHandler = function(m) {
return function() {
var args, internal, ref1;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
internal = v8Util.getHiddenValue(this, 'internal');
return (ref1 = internal.webContents)[m].apply(ref1, args);
};
};
for (i = 0, len = methods.length; i < len; i++) {
m = methods[i];
proto[m] = createBlockHandler(m);
}
createNonBlockHandler = function(m) {
return function() {
var args, internal;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
internal = v8Util.getHiddenValue(this, 'internal');
return ipcRenderer.send.apply(ipcRenderer, ['ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', internal.guestInstanceId, m].concat(slice.call(args)));
};
};
for (j = 0, len1 = nonblockMethods.length; j < len1; j++) {
m = nonblockMethods[j];
proto[m] = createNonBlockHandler(m);
}
/* Deprecated. */
deprecate.rename(proto, 'getUrl', 'getURL');
window.WebView = webFrame.registerEmbedderCustomElement('webview', {
prototype: proto
});
/* Delete the callbacks so developers cannot call them and produce unexpected */
/* behavior. */
delete proto.createdCallback;
delete proto.attachedCallback;
delete proto.detachedCallback;
return delete proto.attributeChangedCallback;
};
useCapture = true;
listener = function(event) {
if (document.readyState === 'loading') {
return;
}
registerBrowserPluginElement();
registerWebViewElement();
return window.removeEventListener(event.type, listener, useCapture);
};
window.addEventListener('readystatechange', listener, true);
module.exports = WebViewImpl;