electron/atom/renderer/lib/web-view/web-view.coffee

529 lines
21 KiB
CoffeeScript
Raw Normal View History

2014-10-22 14:55:13 +00:00
v8Util = process.atomBinding 'v8_util'
guestViewInternal = require './guest-view-internal'
webViewConstants = require './web-view-constants'
2014-10-24 10:24:12 +00:00
webFrame = require 'web-frame'
2014-10-24 11:57:29 +00:00
remote = require 'remote'
2014-10-22 14:55:13 +00:00
# Attributes.
2014-10-22 14:55:13 +00:00
AUTO_SIZE_ATTRIBUTES = [
webViewConstants.ATTRIBUTE_AUTOSIZE,
webViewConstants.ATTRIBUTE_MAXHEIGHT,
webViewConstants.ATTRIBUTE_MAXWIDTH,
webViewConstants.ATTRIBUTE_MINHEIGHT,
webViewConstants.ATTRIBUTE_MINWIDTH,
2014-10-22 14:55:13 +00:00
]
# ID generator.
nextId = 0
getNextId = -> ++nextId
2014-10-22 14:55:13 +00:00
# Represents the internal state of the WebView node.
class WebView
constructor: (@webviewNode) ->
2014-10-22 15:37:27 +00:00
v8Util.setHiddenValue @webviewNode, 'internal', this
2014-10-22 14:55:13 +00:00
@attached = false
@pendingGuestCreation = false
@elementAttached = false
@beforeFirstNavigation = true
@contentWindow = null
@validPartitionId = true
# Used to save some state upon deferred attachment.
# If <object> bindings is not available, we defer attachment.
# This state contains whether or not the attachment request was for
# newwindow.
@deferredAttachState = null
# on* Event handlers.
@on = {}
@browserPluginNode = @createBrowserPluginNode()
shadowRoot = @webviewNode.createShadowRoot()
@setupWebViewAttributes()
2014-10-22 14:55:13 +00:00
@setupWebViewSrcAttributeMutationObserver()
@setupFocusPropagation()
@setupWebviewNodeProperties()
@viewInstanceId = getNextId()
2014-12-08 16:05:34 +00:00
# UPSTREAM: new WebViewEvents(this, this.viewInstanceId);
guestViewInternal.registerEvents this, @viewInstanceId
2014-10-22 14:55:13 +00:00
shadowRoot.appendChild @browserPluginNode
createBrowserPluginNode: ->
# We create BrowserPlugin as a custom element in order to observe changes
# to attributes synchronously.
browserPluginNode = new WebView.BrowserPlugin()
v8Util.setHiddenValue browserPluginNode, 'internal', this
browserPluginNode
getGuestInstanceId: ->
@guestInstanceId
# 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
2014-12-08 16:05:34 +00:00
# FIXME
guestViewInternal.destroyGuest @guestInstanceId
2014-10-22 14:55:13 +00:00
@guestInstanceId = undefined
@beforeFirstNavigation = true
@validPartitionId = true
@attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true
2014-10-22 14:55:13 +00:00
@contentWindow = null
@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()
# Validation helper function for executeScript() and insertCSS().
validateExecuteCodeCall: ->
throw new Error(webViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT) unless @guestInstanceId
2014-10-22 14:55:13 +00:00
setupAutoSizeProperties: ->
for attributeName in AUTO_SIZE_ATTRIBUTES
@attributes[attributeName].setValue @webviewNode.getAttribute(attributeName)
2014-10-22 14:55:13 +00:00
Object.defineProperty @webviewNode, attributeName,
get: => @attributes[attributeName].getValue()
2014-10-22 14:55:13 +00:00
set: (value) => @webviewNode.setAttribute attributeName, value
enumerable: true
setupWebviewNodeProperties: ->
@setupAutoSizeProperties()
Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY,
get: => @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue()
2014-10-22 14:55:13 +00:00
set: (value) =>
@webviewNode.setAttribute webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, value
2014-10-22 14:55:13 +00:00
enumerable: true
# We cannot use {writable: true} property descriptor because we want a
# dynamic getter value.
Object.defineProperty @webviewNode, 'contentWindow',
get: =>
return @contentWindow if @contentWindow?
window.console.error webViewConstants.ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE
2014-10-22 14:55:13 +00:00
# No setter.
enumerable: true
Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_PARTITION,
get: => @attributes[webViewConstants.ATTRIBUTE_PARTITION].getValue()
2014-10-22 14:55:13 +00:00
set: (value) =>
result = @attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue value
2014-10-22 14:55:13 +00:00
throw result.error if result.error?
@webviewNode.setAttribute webViewConstants.ATTRIBUTE_PARTITION, value
2014-10-22 14:55:13 +00:00
enumerable: true
@attributes[webViewConstants.ATTRIBUTE_SRC].setValue @webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC)
Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_SRC,
get: => @attributes[webViewConstants.ATTRIBUTE_SRC].getValue()
set: (value) => @webviewNode.setAttribute webViewConstants.ATTRIBUTE_SRC, value
2014-10-22 14:55:13 +00:00
enumerable: true
@attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].setValue @webviewNode.getAttribute(webViewConstants.ATTRIBUTE_HTTPREFERRER)
Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_HTTPREFERRER,
get: => @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
set: (value) => @webviewNode.setAttribute webViewConstants.ATTRIBUTE_HTTPREFERRER, value
enumerable: true
2014-10-22 14:55:13 +00:00
# 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.
setupWebViewSrcAttributeMutationObserver: ->
@srcAndPartitionObserver = new MutationObserver (mutations) =>
for mutation in mutations
oldValue = mutation.oldValue
newValue = @webviewNode.getAttribute mutation.attributeName
return if oldValue isnt newValue
@handleWebviewAttributeMutation mutation.attributeName, oldValue, newValue
params =
attributes: true,
attributeOldValue: true,
attributeFilter: [
webViewConstants.ATTRIBUTE_SRC
webViewConstants.ATTRIBUTE_PARTITION
webViewConstants.ATTRIBUTE_HTTPREFERRER
]
2014-10-22 14:55:13 +00:00
@srcAndPartitionObserver.observe @webviewNode, params
# 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: (name, oldValue, newValue) ->
if name in AUTO_SIZE_ATTRIBUTES
@attributes[name].setValue newValue
2014-10-22 14:55:13 +00:00
return unless @guestInstanceId
# Convert autosize attribute to boolean.
autosize = @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_AUTOSIZE
guestViewInternal.setAutoSize @guestInstanceId,
enableAutoSize: autosize,
min:
width: parseInt @attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0
height: parseInt @attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0
max:
width: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0
height: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0
else if name is webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY
2014-10-22 14:55:13 +00:00
# We treat null attribute (attribute removed) and the empty string as
# one case.
oldValue ?= ''
newValue ?= ''
return if oldValue is newValue
@attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].setValue(newValue != '')
2014-10-22 14:55:13 +00:00
return unless @guestInstanceId
guestViewInternal.setAllowTransparency @guestInstanceId, @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue()
else if name is webViewConstants.ATTRIBUTE_HTTPREFERRER
oldValue ?= ''
newValue ?= ''
if newValue == '' and oldValue != ''
@webviewNode.setAttribute webViewConstants.ATTRIBUTE_HTTPREFERRER, oldValue
@attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].setValue newValue
result = {}
# If the httpreferrer changes treat it as though the src changes and reload
# the page with the new httpreferrer.
@parseSrcAttribute result
throw result.error if result.error?
else if name is webViewConstants.ATTRIBUTE_SRC
2014-10-22 14:55:13 +00:00
# We treat null attribute (attribute removed) and the empty string as
# one case.
oldValue ?= ''
newValue ?= ''
# Once we have navigated, we don't allow clearing the src attribute.
# Once <webview> enters a navigated state, it cannot be return back to a
# placeholder state.
if 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.
@ignoreNextSrcAttributeChange = true
@webviewNode.setAttribute webViewConstants.ATTRIBUTE_SRC, oldValue
2014-10-22 14:55:13 +00:00
@src = newValue
if @ignoreNextSrcAttributeChange
# Don't allow the src mutation observer to see this change.
@srcAndPartitionObserver.takeRecords()
@ignoreNextSrcAttributeChange = false
return
result = {}
@parseSrcAttribute result
throw result.error if result.error?
else if name is webViewConstants.ATTRIBUTE_PARTITION
2014-10-22 14:55:13 +00:00
# Note that throwing error here won't synchronously propagate.
@attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue newValue
2014-10-22 14:55:13 +00:00
handleBrowserPluginAttributeMutation: (name, oldValue, newValue) ->
if name is webViewConstants.ATTRIBUTE_INTERNALINSTANCEID and !oldValue and !!newValue
@browserPluginNode.removeAttribute webViewConstants.ATTRIBUTE_INTERNALINSTANCEID
2014-12-08 16:05:34 +00:00
@internalInstanceId = parseInt newValue
2014-10-22 14:55:13 +00:00
if !!@guestInstanceId and @guestInstanceId != 0
isNewWindow = if @deferredAttachState then @deferredAttachState.isNewWindow else false
params = @buildAttachParams isNewWindow
2014-12-08 16:05:34 +00:00
guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, params, (w) => @contentWindow = w
2014-10-22 14:55:13 +00:00
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.
if node.hasAttribute(webViewConstants.ATTRIBUTE_MAXWIDTH) and
node[webViewConstants.ATTRIBUTE_MAXWIDTH]
maxWidth = node[webViewConstants.ATTRIBUTE_MAXWIDTH]
2014-10-22 14:55:13 +00:00
else
maxWidth = width
if node.hasAttribute(webViewConstants.ATTRIBUTE_MINWIDTH) and
node[webViewConstants.ATTRIBUTE_MINWIDTH]
minWidth = node[webViewConstants.ATTRIBUTE_MINWIDTH]
2014-10-22 14:55:13 +00:00
else
minWidth = width
minWidth = maxWidth if minWidth > maxWidth
if node.hasAttribute(webViewConstants.ATTRIBUTE_MAXHEIGHT) and
node[webViewConstants.ATTRIBUTE_MAXHEIGHT]
maxHeight = node[webViewConstants.ATTRIBUTE_MAXHEIGHT]
2014-10-22 14:55:13 +00:00
else
maxHeight = height
if node.hasAttribute(webViewConstants.ATTRIBUTE_MINHEIGHT) and
node[webViewConstants.ATTRIBUTE_MINHEIGHT]
minHeight = node[webViewConstants.ATTRIBUTE_MINHEIGHT]
2014-10-22 14:55:13 +00:00
else
minHeight = height
minHeight = maxHeight if minHeight > maxHeight
if not @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_AUTOSIZE or
2014-10-22 14:55:13 +00:00
(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
# Returns if <object> is in the render tree.
isPluginInRenderTree: ->
2014-12-08 16:05:34 +00:00
!!@internalInstanceId && @internalInstanceId != 0
2014-10-22 14:55:13 +00:00
hasNavigated: ->
not @beforeFirstNavigation
parseSrcAttribute: (result) ->
unless @attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId
result.error = webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
2014-10-22 14:55:13 +00:00
return
@attributes[webViewConstants.ATTRIBUTE_SRC].setValue @webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC)
2014-10-22 14:55:13 +00:00
return unless @attributes[webViewConstants.ATTRIBUTE_SRC].getValue()
2014-10-22 14:55:13 +00:00
unless @guestInstanceId?
if @beforeFirstNavigation
@beforeFirstNavigation = false
@createGuest()
return
# Navigate to |this.src|.
httpreferrer = @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
urlOptions = if httpreferrer then {httpreferrer} else {}
remote.getGuestWebContents(@guestInstanceId).loadUrl @attributes[webViewConstants.ATTRIBUTE_SRC].getValue(), urlOptions
2014-10-22 14:55:13 +00:00
parseAttributes: ->
return unless @elementAttached
hasNavigated = @hasNavigated()
attributeValue = @webviewNode.getAttribute webViewConstants.ATTRIBUTE_PARTITION
result = @attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue attributeValue
2014-10-22 14:55:13 +00:00
@parseSrcAttribute result
createGuest: ->
return if @pendingGuestCreation
2014-10-22 14:55:13 +00:00
storagePartitionId =
@webviewNode.getAttribute(webViewConstants.ATTRIBUTE_PARTITION) or
@webviewNode[webViewConstants.ATTRIBUTE_PARTITION]
params =
storagePartitionId: storagePartitionId
nodeIntegration: @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_NODEINTEGRATION
plugins: @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_PLUGINS
if @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_PRELOAD
preload = @webviewNode.getAttribute webViewConstants.ATTRIBUTE_PRELOAD
# Get the full path.
a = document.createElement 'a'
a.href = preload
params.preload = a.href
# Only support file: or asar: protocol.
protocol = params.preload.substr 0, 5
unless protocol in ['file:', 'asar:']
delete params.preload
console.error webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE
guestViewInternal.createGuest 'webview', params, (guestInstanceId) =>
@pendingGuestCreation = false
unless @elementAttached
guestViewInternal.destroyGuest guestInstanceId
return
@attachWindow guestInstanceId, false
2014-10-22 14:55:13 +00:00
@pendingGuestCreation = true
dispatchEvent: (webViewEvent) ->
2014-10-22 14:55:13 +00:00
@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: (@baseUrlForDataUrl, @currentEntryIndex, @entryCount, @processId, url, isTopLevel) ->
oldValue = @webviewNode.getAttribute webViewConstants.ATTRIBUTE_SRC
2014-10-22 14:55:13 +00:00
newValue = url
if isTopLevel and (oldValue != newValue)
# Touching the src attribute triggers a navigation. To avoid
# triggering a page reload on every guest-initiated navigation,
# we use the flag ignoreNextSrcAttributeChange here.
this.ignoreNextSrcAttributeChange = true
this.webviewNode.setAttribute webViewConstants.ATTRIBUTE_SRC, newValue
2014-10-22 14:55:13 +00:00
onAttach: (storagePartitionId) ->
@webviewNode.setAttribute webViewConstants.ATTRIBUTE_PARTITION, storagePartitionId
@attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue storagePartitionId
2014-10-22 14:55:13 +00:00
buildAttachParams: (isNewWindow) ->
allowtransparency: @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue() || false
autosize: @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_AUTOSIZE
2014-10-22 14:55:13 +00:00
instanceId: @viewInstanceId
maxheight: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0
maxwidth: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0
minheight: parseInt @attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0
minwidth: parseInt @attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0
2014-10-22 14:55:13 +00:00
# We don't need to navigate new window from here.
src: if isNewWindow then undefined else @attributes[webViewConstants.ATTRIBUTE_SRC].getValue()
2014-10-22 14:55:13 +00:00
# If we have a partition from the opener, that will also be already
# set via this.onAttach().
storagePartitionId: @attributes[webViewConstants.ATTRIBUTE_PARTITION].getValue()
2014-10-22 14:55:13 +00:00
userAgentOverride: @userAgentOverride
httpreferrer: @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
2014-10-22 14:55:13 +00:00
attachWindow: (guestInstanceId, isNewWindow) ->
@guestInstanceId = guestInstanceId
params = @buildAttachParams isNewWindow
unless @isPluginInRenderTree()
@deferredAttachState = isNewWindow: isNewWindow
2014-10-22 14:55:13 +00:00
return true
@deferredAttachState = null
2014-12-08 16:05:34 +00:00
guestViewInternal.attachGuest @internalInstanceId, @guestInstanceId, params, (w) => @contentWindow = w
2014-10-22 14:55:13 +00:00
# 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.width = '100%'
@style.height = '100%'
proto.attributeChangedCallback = (name, oldValue, newValue) ->
2014-10-22 15:37:27 +00:00
internal = v8Util.getHiddenValue this, 'internal'
2014-10-22 14:55:13 +00:00
return unless internal
internal.handleBrowserPluginAttributeMutation name, oldValue, newValue
proto.attachedCallback = ->
# Load the plugin immediately.
unused = this.nonExistentAttribute
2014-10-24 10:24:12 +00:00
WebView.BrowserPlugin = webFrame.registerEmbedderCustomElement 'browserplugin',
2014-10-22 14:55:13 +00:00
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 WebView(this)
proto.attributeChangedCallback = (name, oldValue, newValue) ->
2014-10-22 15:37:27 +00:00
internal = v8Util.getHiddenValue this, 'internal'
2014-10-22 14:55:13 +00:00
return unless internal
internal.handleWebviewAttributeMutation name, oldValue, newValue
proto.detachedCallback = ->
2014-10-22 15:37:27 +00:00
internal = v8Util.getHiddenValue this, 'internal'
2014-10-22 14:55:13 +00:00
return unless internal
internal.elementAttached = false
internal.reset()
proto.attachedCallback = ->
2014-10-22 15:37:27 +00:00
internal = v8Util.getHiddenValue this, 'internal'
2014-10-22 14:55:13 +00:00
return unless internal
unless internal.elementAttached
internal.elementAttached = true
internal.parseAttributes()
2014-10-24 12:49:51 +00:00
# Public-facing API methods.
methods = [
"getUrl"
"getTitle"
"isLoading"
"isWaitingForResponse"
"stop"
"reload"
2014-11-23 05:26:09 +00:00
"reloadIgnoringCache"
2014-10-24 12:49:51 +00:00
"canGoBack"
"canGoForward"
"canGoToOffset"
"goBack"
"goForward"
"goToIndex"
"goToOffset"
"isCrashed"
2014-10-24 13:04:50 +00:00
"setUserAgent"
2014-10-24 12:49:51 +00:00
"executeJavaScript"
2014-11-14 08:34:14 +00:00
"insertCSS"
"openDevTools"
"closeDevTools"
"isDevToolsOpened"
2014-10-24 12:49:51 +00:00
"send"
2014-11-14 08:34:14 +00:00
"getId"
2014-10-24 12:49:51 +00:00
]
# Forward proto.foo* method calls to WebView.foo*.
createHandler = (m) ->
(args...) ->
internal = v8Util.getHiddenValue this, 'internal'
remote.getGuestWebContents(internal.guestInstanceId)[m] args...
proto[m] = createHandler m for m in methods
2014-10-24 10:44:15 +00:00
window.WebView = webFrame.registerEmbedderCustomElement 'webview',
2014-10-22 14:55:13 +00:00
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 = WebView