Convert all source files to JavaScript
This commit is contained in:
parent
403870a27e
commit
1f9691ae13
144 changed files with 11211 additions and 7301 deletions
|
@ -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
|
113
atom/renderer/lib/web-view/guest-view-internal.js
Normal file
113
atom/renderer/lib/web-view/guest-view-internal.js
Normal 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);
|
||||
}
|
||||
};
|
|
@ -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)
|
410
atom/renderer/lib/web-view/web-view-attributes.js
Normal file
410
atom/renderer/lib/web-view/web-view-attributes.js
Normal 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;
|
||||
};
|
|
@ -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.'
|
29
atom/renderer/lib/web-view/web-view-constants.js
Normal file
29
atom/renderer/lib/web-view/web-view-constants.js
Normal 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.'
|
||||
};
|
|
@ -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
|
435
atom/renderer/lib/web-view/web-view.js
Normal file
435
atom/renderer/lib/web-view/web-view.js
Normal 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;
|
Loading…
Add table
Add a link
Reference in a new issue