Optimized the handling of webview attribute mutation

Imported from:
86dff6fc51%5E%21/
This commit is contained in:
Cheng Zhao 2014-12-08 21:06:23 -08:00
parent 4348143fd9
commit ac51207860
2 changed files with 110 additions and 129 deletions

View file

@ -1,4 +1,5 @@
WebView = require './web-view' WebView = require './web-view'
guestViewInternal = require './guest-view-internal'
webViewConstants = require './web-view-constants' webViewConstants = require './web-view-constants'
# Attribute objects. # Attribute objects.
@ -7,7 +8,9 @@ class WebViewAttribute
constructor: (name, webViewImpl) -> constructor: (name, webViewImpl) ->
@name = name @name = name
@webViewImpl = webViewImpl @webViewImpl = webViewImpl
@ignoreNextMutation = false @ignoreMutation = false
@defineProperty()
# Retrieves and returns the attribute's value. # Retrieves and returns the attribute's value.
getValue: -> @webViewImpl.webviewNode.getAttribute(@name) || '' getValue: -> @webViewImpl.webviewNode.getAttribute(@name) || ''
@ -15,8 +18,14 @@ class WebViewAttribute
# Sets the attribute's value. # Sets the attribute's value.
setValue: (value) -> @webViewImpl.webviewNode.setAttribute(@name, value || '') setValue: (value) -> @webViewImpl.webviewNode.setAttribute(@name, value || '')
# Changes the attribute's value without triggering its mutation handler.
setValueIgnoreMutation: (value) ->
@ignoreMutation = true
@webViewImpl.webviewNode.setAttribute(@name, value || '')
@ignoreMutation = false
# Defines this attribute as a property on the webview node. # Defines this attribute as a property on the webview node.
define: -> defineProperty: ->
Object.defineProperty @webViewImpl.webviewNode, @name, Object.defineProperty @webViewImpl.webviewNode, @name,
get: => @getValue() get: => @getValue()
set: (value) => @setValue value set: (value) => @setValue value
@ -30,8 +39,7 @@ class BooleanAttribute extends WebViewAttribute
constructor: (name, webViewImpl) -> constructor: (name, webViewImpl) ->
super name, webViewImpl super name, webViewImpl
getValue: -> getValue: -> @webViewImpl.webviewNode.hasAttribute @name
@webViewImpl.webviewNode.hasAttribute @name
setValue: (value) -> setValue: (value) ->
unless value unless value
@ -39,8 +47,42 @@ class BooleanAttribute extends WebViewAttribute
else else
@webViewImpl.webviewNode.setAttribute @name, '' @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, @webViewImpl.attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].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.setAutoSize @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. # Attribute representing the state of the storage partition.
class Partition extends WebViewAttribute class PartitionAttribute extends WebViewAttribute
constructor: (webViewImpl) -> constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_PARTITION, webViewImpl super webViewConstants.ATTRIBUTE_PARTITION, webViewImpl
@validPartitionId = true @validPartitionId = true
@ -51,35 +93,71 @@ class Partition extends WebViewAttribute
# The partition cannot change if the webview has already navigated. # The partition cannot change if the webview has already navigated.
unless @webViewImpl.beforeFirstNavigation unless @webViewImpl.beforeFirstNavigation
window.console.error webViewConstants.ERROR_MSG_ALREADY_NAVIGATED window.console.error webViewConstants.ERROR_MSG_ALREADY_NAVIGATED
@ignoreNextMutation = true @setValueIgnoreMutation oldValue
@webViewImpl.webviewNode.setAttribute @name, oldValue
return return
if newValue is 'persist:' if newValue is 'persist:'
@validPartitionId = false @validPartitionId = false
window.console.error webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE 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()
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
@webViewImpl.parseSrcAttribute()
# 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
# Attribute specifies HTTP referrer.
class HttpReferrerAttribute extends WebViewAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl
handleMutation: (oldValue, newValue) ->
@webViewImpl.parseSrcAttribute()
# Sets up all of the webview attributes. # Sets up all of the webview attributes.
WebView::setupWebViewAttributes = -> WebView::setupWebViewAttributes = ->
@attributes = {} @attributes = {}
# Initialize the attributes with special behavior (and custom attribute @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] = new AllowTransparencyAttribute(this)
# objects). @attributes[webViewConstants.ATTRIBUTE_AUTOSIZE] = new AutosizeAttribute(this)
@attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] = @attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this)
new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, this) @attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this)
@attributes[webViewConstants.ATTRIBUTE_AUTOSIZE] = @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
new BooleanAttribute(webViewConstants.ATTRIBUTE_AUTOSIZE, this)
@attributes[webViewConstants.ATTRIBUTE_PARTITION] = new Partition(this)
# Initialize the remaining attributes, which have default behavior. autosizeAttributes = [
defaultAttributes = [
webViewConstants.ATTRIBUTE_MAXHEIGHT webViewConstants.ATTRIBUTE_MAXHEIGHT
webViewConstants.ATTRIBUTE_MAXWIDTH webViewConstants.ATTRIBUTE_MAXWIDTH
webViewConstants.ATTRIBUTE_MINHEIGHT webViewConstants.ATTRIBUTE_MINHEIGHT
webViewConstants.ATTRIBUTE_MINWIDTH webViewConstants.ATTRIBUTE_MINWIDTH
webViewConstants.ATTRIBUTE_SRC
webViewConstants.ATTRIBUTE_HTTPREFERRER
] ]
for attribute in autosizeAttributes
for attribute in defaultAttributes @attributes[attribute] = new AutosizeDimensionAttribute(attribute, this)
@attributes[attribute] = new WebViewAttribute(attribute, this)

View file

@ -4,15 +4,6 @@ webViewConstants = require './web-view-constants'
webFrame = require 'web-frame' webFrame = require 'web-frame'
remote = require 'remote' remote = require 'remote'
# Attributes.
AUTO_SIZE_ATTRIBUTES = [
webViewConstants.ATTRIBUTE_AUTOSIZE,
webViewConstants.ATTRIBUTE_MAXHEIGHT,
webViewConstants.ATTRIBUTE_MAXWIDTH,
webViewConstants.ATTRIBUTE_MINHEIGHT,
webViewConstants.ATTRIBUTE_MINWIDTH,
]
# ID generator. # ID generator.
nextId = 0 nextId = 0
getNextId = -> ++nextId getNextId = -> ++nextId
@ -34,13 +25,11 @@ class WebView
@browserPluginNode = @createBrowserPluginNode() @browserPluginNode = @createBrowserPluginNode()
shadowRoot = @webviewNode.createShadowRoot() shadowRoot = @webviewNode.createShadowRoot()
@setupWebViewAttributes() @setupWebViewAttributes()
@setupWebViewSrcAttributeMutationObserver()
@setupFocusPropagation() @setupFocusPropagation()
@setupWebviewNodeProperties() @setupWebviewNodeProperties()
@viewInstanceId = getNextId() @viewInstanceId = getNextId()
# UPSTREAM: new WebViewEvents(this, this.viewInstanceId);
guestViewInternal.registerEvents this, @viewInstanceId guestViewInternal.registerEvents this, @viewInstanceId
shadowRoot.appendChild @browserPluginNode shadowRoot.appendChild @browserPluginNode
@ -95,9 +84,6 @@ class WebView
throw new Error(webViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT) unless @guestInstanceId throw new Error(webViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT) unless @guestInstanceId
setupWebviewNodeProperties: -> setupWebviewNodeProperties: ->
for attributeName of @attributes
@attributes[attributeName].define()
# We cannot use {writable: true} property descriptor because we want a # We cannot use {writable: true} property descriptor because we want a
# dynamic getter value. # dynamic getter value.
Object.defineProperty @webviewNode, 'contentWindow', Object.defineProperty @webviewNode, 'contentWindow',
@ -107,94 +93,17 @@ class WebView
# No setter. # No setter.
enumerable: true enumerable: true
# 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 = @attributes[mutation.attributeName].getValue()
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
]
@srcAndPartitionObserver.observe @webviewNode, params
# This observer monitors mutations to attributes of the <webview> and # This observer monitors mutations to attributes of the <webview> and
# updates the BrowserPlugin properties accordingly. In turn, updating # updates the BrowserPlugin properties accordingly. In turn, updating
# a BrowserPlugin property will update the corresponding BrowserPlugin # a BrowserPlugin property will update the corresponding BrowserPlugin
# attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more # attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
# details. # details.
handleWebviewAttributeMutation: (attributeName, oldValue, newValue) -> handleWebviewAttributeMutation: (attributeName, oldValue, newValue) ->
# Certain changes (such as internally-initiated changes) to attributes should if not @attributes[attributeName] or @attributes[attributeName].ignoreMutation
# not be handled normally.
if @attributes[attributeName]?.ignoreNextMutation
@attributes[attributeName].ignoreNextMutation = false
return return
if attributeName in AUTO_SIZE_ATTRIBUTES # Let the changed attribute handle its own mutation;
return unless @guestInstanceId @attributes[attributeName].handleMutation oldValue, newValue
guestViewInternal.setAutoSize @guestInstanceId,
enableAutoSize: @attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue(),
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 attributeName is webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY
# We treat null attribute (attribute removed) and the empty string as
# one case.
oldValue ?= ''
newValue ?= ''
return if oldValue is newValue and not @guestInstanceId
guestViewInternal.setAllowTransparency @guestInstanceId, @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue()
else if attributeName is webViewConstants.ATTRIBUTE_HTTPREFERRER
oldValue ?= ''
newValue ?= ''
if newValue == '' and oldValue != ''
@webviewNode.setAttribute webViewConstants.ATTRIBUTE_HTTPREFERRER, oldValue
@attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].setValue newValue
# If the httpreferrer changes treat it as though the src changes and reload
# the page with the new httpreferrer.
@parseSrcAttribute()
else if attributeName is webViewConstants.ATTRIBUTE_SRC
# 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 return 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
return
if @ignoreNextSrcAttributeChange
# Don't allow the src mutation observer to see this change.
@srcAndPartitionObserver.takeRecords()
@ignoreNextSrcAttributeChange = false
return
@parseSrcAttribute()
else if attributeName is webViewConstants.ATTRIBUTE_PARTITION
@attributes[webViewConstants.ATTRIBUTE_PARTITION].handleMutation oldValue, newValue
handleBrowserPluginAttributeMutation: (attributeName, oldValue, newValue) -> handleBrowserPluginAttributeMutation: (attributeName, oldValue, newValue) ->
if attributeName is webViewConstants.ATTRIBUTE_INTERNALINSTANCEID and !oldValue and !!newValue if attributeName is webViewConstants.ATTRIBUTE_INTERNALINSTANCEID and !oldValue and !!newValue
@ -330,25 +239,19 @@ class WebView
if isTopLevel and (oldValue != newValue) if isTopLevel and (oldValue != newValue)
# Touching the src attribute triggers a navigation. To avoid # Touching the src attribute triggers a navigation. To avoid
# triggering a page reload on every guest-initiated navigation, # triggering a page reload on every guest-initiated navigation,
# we use the flag ignoreNextSrcAttributeChange here. # we do not handle this mutation
this.ignoreNextSrcAttributeChange = true @attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation newValue
this.webviewNode.setAttribute webViewConstants.ATTRIBUTE_SRC, newValue
onAttach: (storagePartitionId) -> onAttach: (storagePartitionId) ->
@attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue storagePartitionId @attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue storagePartitionId
buildAttachParams: -> buildAttachParams: ->
instanceId: @viewInstanceId params =
userAgentOverride: @userAgentOverride instanceId: @viewInstanceId
# Attributes: userAgentOverride: @userAgentOverride
allowtransparency: @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue() for attributeName, attribute of @attributes
autosize: @attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue() params[attributeName] = attribute.getValue()
maxheight: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0 params
maxwidth: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0
minheight: parseInt @attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0
minwidth: parseInt @attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0
src: @attributes[webViewConstants.ATTRIBUTE_SRC].getValue()
httpreferrer: @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
attachWindow: (guestInstanceId) -> attachWindow: (guestInstanceId) ->
@guestInstanceId = guestInstanceId @guestInstanceId = guestInstanceId