Webview attributes overhaul

Imported from:
884a5b43cb
This commit is contained in:
Cheng Zhao 2014-12-08 16:14:12 -08:00
parent e0f1433c12
commit d7eae69587
5 changed files with 145 additions and 91 deletions

View file

@ -49,6 +49,7 @@
'atom/renderer/lib/override.coffee', 'atom/renderer/lib/override.coffee',
'atom/renderer/lib/web-view/guest-view-internal.coffee', 'atom/renderer/lib/web-view/guest-view-internal.coffee',
'atom/renderer/lib/web-view/web-view.coffee', 'atom/renderer/lib/web-view/web-view.coffee',
'atom/renderer/lib/web-view/web-view-attributes.coffee',
'atom/renderer/lib/web-view/web-view-constants.coffee', 'atom/renderer/lib/web-view/web-view-constants.coffee',
'atom/renderer/api/lib/ipc.coffee', 'atom/renderer/api/lib/ipc.coffee',
'atom/renderer/api/lib/remote.coffee', 'atom/renderer/api/lib/remote.coffee',

View file

@ -36,17 +36,19 @@ for arg in process.argv
if location.protocol is 'chrome-devtools:' if location.protocol is 'chrome-devtools:'
# Override some inspector APIs. # Override some inspector APIs.
require path.join(__dirname, 'inspector') require './inspector'
nodeIntegration = 'true' nodeIntegration = 'true'
else if location.protocol is 'chrome-extension:' else if location.protocol is 'chrome-extension:'
# Add implementations of chrome API. # Add implementations of chrome API.
require path.join(__dirname, 'chrome-api') require './chrome-api'
nodeIntegration = 'true' nodeIntegration = 'true'
else else
# Override default web functions. # Override default web functions.
require path.join(__dirname, 'override') require './override'
# Load webview tag implementation. # Load webview tag implementation.
require path.join(__dirname, 'web-view/web-view') unless process.guestInstanceId? unless process.guestInstanceId?
require './web-view/web-view'
require './web-view/web-view-attributes'
if nodeIntegration in ['true', 'all', 'except-iframe', 'manual-enable-iframe'] if nodeIntegration in ['true', 'all', 'except-iframe', 'manual-enable-iframe']
# Export node bindings to global. # Export node bindings to global.

View file

@ -0,0 +1,71 @@
WebView = require './web-view'
webViewConstants = require './web-view-constants'
# Attribute objects.
# Default implementation of a WebView attribute.
class WebViewAttribute
constructor: (name, webViewImpl) ->
@name = name
@value = ''
@webViewImpl = webViewImpl
getValue: -> @value || ''
setValue: (value) -> @value = value
# Attribute representing the state of the storage partition.
class Partition extends WebViewAttribute
constructor: (webViewImpl) ->
super webViewConstants.ATTRIBUTE_PARTITION, webViewImpl
@validPartitionId = true
@persistStorage = false
@storagePartitionId = ''
@webViewImpl = webViewImpl
getValue: ->
return '' unless @validPartitionId
(if @persistStorage then 'persist:' else '') + @storagePartitionId
setValue: (value) ->
result = {}
hasNavigated = !@webViewImpl.beforeFirstNavigation
if hasNavigated
result.error = webViewConstants.ERROR_MSG_ALREADY_NAVIGATED
return result
value = '' unless value
LEN = 'persist:'.length
if value.substr(0, LEN) == 'persist:'
value = value.substr LEN
unless value
@validPartitionId = false
result.error = webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
return result
@persistStorage = true
else
@persistStorage = false
@storagePartitionId = value
result
# Sets up all of the webview attributes.
WebView::setupWebViewAttributes = ->
@attributes = {}
# Initialize the attributes with special behavior (and custom attribute
# objects).
@attributes[webViewConstants.ATTRIBUTE_PARTITION] = new Partition(this)
# Initialize the remaining attributes, which have default behavior.
defaultAttributes = [
webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY
webViewConstants.ATTRIBUTE_AUTOSIZE
webViewConstants.ATTRIBUTE_MAXHEIGHT
webViewConstants.ATTRIBUTE_MAXWIDTH
webViewConstants.ATTRIBUTE_MINHEIGHT
webViewConstants.ATTRIBUTE_MINWIDTH
webViewConstants.ATTRIBUTE_SRC
webViewConstants.ATTRIBUTE_HTTPREFERRER
]
for attribute in defaultAttributes
@attributes[attribute] = new WebViewAttribute(attribute, this)

View file

@ -6,11 +6,17 @@ module.exports =
ATTRIBUTE_MAXWIDTH: 'maxwidth' ATTRIBUTE_MAXWIDTH: 'maxwidth'
ATTRIBUTE_MINHEIGHT: 'minheight' ATTRIBUTE_MINHEIGHT: 'minheight'
ATTRIBUTE_MINWIDTH: 'minwidth' ATTRIBUTE_MINWIDTH: 'minwidth'
ATTRIBUTE_NAME: 'name'
ATTRIBUTE_PARTITION: 'partition' ATTRIBUTE_PARTITION: 'partition'
ATTRIBUTE_SRC: 'src'
ATTRIBUTE_HTTPREFERRER: 'httpreferrer'
ATTRIBUTE_NODEINTEGRATION: 'nodeintegration' ATTRIBUTE_NODEINTEGRATION: 'nodeintegration'
ATTRIBUTE_PLUGINS: 'plugins' ATTRIBUTE_PLUGINS: 'plugins'
ATTRIBUTE_PRELOAD: 'preload' ATTRIBUTE_PRELOAD: 'preload'
# Internal attribute.
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid'
# Error messages. # Error messages.
ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.' ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.'
ERROR_MSG_CANNOT_INJECT_SCRIPT: '<webview>: ' + ERROR_MSG_CANNOT_INJECT_SCRIPT: '<webview>: ' +

View file

@ -4,6 +4,7 @@ webViewConstants = require './web-view-constants'
webFrame = require 'web-frame' webFrame = require 'web-frame'
remote = require 'remote' remote = require 'remote'
# Attributes.
AUTO_SIZE_ATTRIBUTES = [ AUTO_SIZE_ATTRIBUTES = [
webViewConstants.ATTRIBUTE_AUTOSIZE, webViewConstants.ATTRIBUTE_AUTOSIZE,
webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXHEIGHT,
@ -16,38 +17,6 @@ AUTO_SIZE_ATTRIBUTES = [
nextId = 0 nextId = 0
getNextId = -> ++nextId getNextId = -> ++nextId
# Represents the state of the storage partition.
class Partition
constructor: ->
@validPartitionId = true
@persistStorage = false
@storagePartitionId = ''
toAttribute: ->
return '' unless @validPartitionId
(if @persistStorage then 'persist:' else '') + @storagePartitionId
fromAttribute: (value, hasNavigated) ->
result = {}
if hasNavigated
result.error = webViewConstants.ERROR_MSG_ALREADY_NAVIGATED
return result
value ?= ''
LEN = 'persist:'.length
if value.substr(0, LEN) == 'persist:'
value = value.substr LEN
unless value
@validPartitionId = false
result.error = webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
return result
@persistStorage = true
else
@persistStorage = false
@storagePartitionId = value
result
# Represents the internal state of the WebView node. # Represents the internal state of the WebView node.
class WebView class WebView
constructor: (@webviewNode) -> constructor: (@webviewNode) ->
@ -70,8 +39,7 @@ class WebView
@browserPluginNode = @createBrowserPluginNode() @browserPluginNode = @createBrowserPluginNode()
shadowRoot = @webviewNode.createShadowRoot() shadowRoot = @webviewNode.createShadowRoot()
@partition = new Partition() @setupWebViewAttributes()
@setupWebViewSrcAttributeMutationObserver() @setupWebViewSrcAttributeMutationObserver()
@setupFocusPropagation() @setupFocusPropagation()
@setupWebviewNodeProperties() @setupWebviewNodeProperties()
@ -107,7 +75,7 @@ class WebView
@guestInstanceId = undefined @guestInstanceId = undefined
@beforeFirstNavigation = true @beforeFirstNavigation = true
@validPartitionId = true @validPartitionId = true
@partition.validPartitionId = true @attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true
@contentWindow = null @contentWindow = null
@internalInstanceId = 0 @internalInstanceId = 0
@ -135,9 +103,9 @@ class WebView
setupAutoSizeProperties: -> setupAutoSizeProperties: ->
for attributeName in AUTO_SIZE_ATTRIBUTES for attributeName in AUTO_SIZE_ATTRIBUTES
this[attributeName] = @webviewNode.getAttribute attributeName @attributes[attributeName].setValue @webviewNode.getAttribute(attributeName)
Object.defineProperty @webviewNode, attributeName, Object.defineProperty @webviewNode, attributeName,
get: => this[attributeName] get: => @attributes[attributeName].getValue()
set: (value) => @webviewNode.setAttribute attributeName, value set: (value) => @webviewNode.setAttribute attributeName, value
enumerable: true enumerable: true
@ -145,7 +113,7 @@ class WebView
@setupAutoSizeProperties() @setupAutoSizeProperties()
Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY,
get: => @allowtransparency get: => @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue()
set: (value) => set: (value) =>
@webviewNode.setAttribute webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, value @webviewNode.setAttribute webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, value
enumerable: true enumerable: true
@ -159,25 +127,24 @@ class WebView
# No setter. # No setter.
enumerable: true enumerable: true
Object.defineProperty @webviewNode, 'partition', Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_PARTITION,
get: => @partition.toAttribute() get: => @attributes[webViewConstants.ATTRIBUTE_PARTITION].getValue()
set: (value) => set: (value) =>
result = @partition.fromAttribute value, @hasNavigated() result = @attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue value
throw result.error if result.error? throw result.error if result.error?
@webviewNode.setAttribute 'partition', value @webviewNode.setAttribute webViewConstants.ATTRIBUTE_PARTITION, value
enumerable: true enumerable: true
@src = @webviewNode.getAttribute 'src' @attributes[webViewConstants.ATTRIBUTE_SRC].setValue @webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC)
Object.defineProperty @webviewNode, 'src', Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_SRC,
get: => @src get: => @attributes[webViewConstants.ATTRIBUTE_SRC].getValue()
set: (value) => @webviewNode.setAttribute 'src', value set: (value) => @webviewNode.setAttribute webViewConstants.ATTRIBUTE_SRC, value
# No setter.
enumerable: true enumerable: true
@httpreferrer = @webviewNode.getAttribute 'httpreferrer' @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].setValue @webviewNode.getAttribute(webViewConstants.ATTRIBUTE_HTTPREFERRER)
Object.defineProperty @webviewNode, 'httpreferrer', Object.defineProperty @webviewNode, webViewConstants.ATTRIBUTE_HTTPREFERRER,
get: => @httpreferrer get: => @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
set: (value) => @webviewNode.setAttribute 'httpreferrer', value set: (value) => @webviewNode.setAttribute webViewConstants.ATTRIBUTE_HTTPREFERRER, value
enumerable: true enumerable: true
# The purpose of this mutation observer is to catch assignment to the src # The purpose of this mutation observer is to catch assignment to the src
@ -194,7 +161,11 @@ class WebView
params = params =
attributes: true, attributes: true,
attributeOldValue: true, attributeOldValue: true,
attributeFilter: ['src', 'partition', 'httpreferrer'] attributeFilter: [
webViewConstants.ATTRIBUTE_SRC
webViewConstants.ATTRIBUTE_PARTITION
webViewConstants.ATTRIBUTE_HTTPREFERRER
]
@srcAndPartitionObserver.observe @webviewNode, params @srcAndPartitionObserver.observe @webviewNode, params
# This observer monitors mutations to attributes of the <webview> and # This observer monitors mutations to attributes of the <webview> and
@ -204,18 +175,18 @@ class WebView
# details. # details.
handleWebviewAttributeMutation: (name, oldValue, newValue) -> handleWebviewAttributeMutation: (name, oldValue, newValue) ->
if name in AUTO_SIZE_ATTRIBUTES if name in AUTO_SIZE_ATTRIBUTES
this[name] = newValue @attributes[name].setValue newValue
return unless @guestInstanceId return unless @guestInstanceId
# Convert autosize attribute to boolean. # Convert autosize attribute to boolean.
autosize = @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_AUTOSIZE autosize = @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_AUTOSIZE
guestViewInternal.setAutoSize @guestInstanceId, guestViewInternal.setAutoSize @guestInstanceId,
enableAutoSize: autosize, enableAutoSize: autosize,
min: min:
width: parseInt @minwidth || 0 width: parseInt @attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0
height: parseInt @minheight || 0 height: parseInt @attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0
max: max:
width: parseInt @maxwidth || 0 width: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0
height: parseInt @maxheight || 0 height: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0
else if name is webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY else if name is webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY
# We treat null attribute (attribute removed) and the empty string as # We treat null attribute (attribute removed) and the empty string as
# one case. # one case.
@ -223,19 +194,19 @@ class WebView
newValue ?= '' newValue ?= ''
return if oldValue is newValue return if oldValue is newValue
@allowtransparency = newValue != '' @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].setValue(newValue != '')
return unless @guestInstanceId return unless @guestInstanceId
guestViewInternal.setAllowTransparency @guestInstanceId, @allowtransparency guestViewInternal.setAllowTransparency @guestInstanceId, @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue()
else if name is 'httpreferrer' else if name is webViewConstants.ATTRIBUTE_HTTPREFERRER
oldValue ?= '' oldValue ?= ''
newValue ?= '' newValue ?= ''
if newValue == '' and oldValue != '' if newValue == '' and oldValue != ''
@webviewNode.setAttribute 'httpreferrer', oldValue @webviewNode.setAttribute webViewConstants.ATTRIBUTE_HTTPREFERRER, oldValue
@httpreferrer = newValue @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].setValue newValue
result = {} result = {}
# If the httpreferrer changes treat it as though the src changes and reload # If the httpreferrer changes treat it as though the src changes and reload
@ -243,7 +214,7 @@ class WebView
@parseSrcAttribute result @parseSrcAttribute result
throw result.error if result.error? throw result.error if result.error?
else if name is 'src' else if name is webViewConstants.ATTRIBUTE_SRC
# We treat null attribute (attribute removed) and the empty string as # We treat null attribute (attribute removed) and the empty string as
# one case. # one case.
oldValue ?= '' oldValue ?= ''
@ -256,7 +227,7 @@ class WebView
# the next src attribute handler call to avoid reloading the page # the next src attribute handler call to avoid reloading the page
# on every guest-initiated navigation. # on every guest-initiated navigation.
@ignoreNextSrcAttributeChange = true @ignoreNextSrcAttributeChange = true
@webviewNode.setAttribute 'src', oldValue @webviewNode.setAttribute webViewConstants.ATTRIBUTE_SRC, oldValue
@src = newValue @src = newValue
if @ignoreNextSrcAttributeChange if @ignoreNextSrcAttributeChange
# Don't allow the src mutation observer to see this change. # Don't allow the src mutation observer to see this change.
@ -267,13 +238,13 @@ class WebView
@parseSrcAttribute result @parseSrcAttribute result
throw result.error if result.error? throw result.error if result.error?
else if name is 'partition' else if name is webViewConstants.ATTRIBUTE_PARTITION
# Note that throwing error here won't synchronously propagate. # Note that throwing error here won't synchronously propagate.
@partition.fromAttribute newValue, @hasNavigated() @attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue newValue
handleBrowserPluginAttributeMutation: (name, oldValue, newValue) -> handleBrowserPluginAttributeMutation: (name, oldValue, newValue) ->
if name is 'internalinstanceid' and !oldValue and !!newValue if name is webViewConstants.ATTRIBUTE_INTERNALINSTANCEID and !oldValue and !!newValue
@browserPluginNode.removeAttribute 'internalinstanceid' @browserPluginNode.removeAttribute webViewConstants.ATTRIBUTE_INTERNALINSTANCEID
@internalInstanceId = parseInt newValue @internalInstanceId = parseInt newValue
if !!@guestInstanceId and @guestInstanceId != 0 if !!@guestInstanceId and @guestInstanceId != 0
@ -337,12 +308,12 @@ class WebView
not @beforeFirstNavigation not @beforeFirstNavigation
parseSrcAttribute: (result) -> parseSrcAttribute: (result) ->
unless @partition.validPartitionId unless @attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId
result.error = webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE result.error = webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE
return return
@src = @webviewNode.getAttribute 'src' @attributes[webViewConstants.ATTRIBUTE_SRC].setValue @webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC)
return unless @src return unless @attributes[webViewConstants.ATTRIBUTE_SRC].getValue()
unless @guestInstanceId? unless @guestInstanceId?
if @beforeFirstNavigation if @beforeFirstNavigation
@ -351,14 +322,15 @@ class WebView
return return
# Navigate to |this.src|. # Navigate to |this.src|.
urlOptions = if @httpreferrer then {@httpreferrer} else {} httpreferrer = @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
remote.getGuestWebContents(@guestInstanceId).loadUrl @src, urlOptions urlOptions = if httpreferrer then {httpreferrer} else {}
remote.getGuestWebContents(@guestInstanceId).loadUrl @attributes[webViewConstants.ATTRIBUTE_SRC].getValue(), urlOptions
parseAttributes: -> parseAttributes: ->
return unless @elementAttached return unless @elementAttached
hasNavigated = @hasNavigated() hasNavigated = @hasNavigated()
attributeValue = @webviewNode.getAttribute 'partition' attributeValue = @webviewNode.getAttribute webViewConstants.ATTRIBUTE_PARTITION
result = @partition.fromAttribute attributeValue, hasNavigated result = @attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue attributeValue
@parseSrcAttribute result @parseSrcAttribute result
createGuest: -> createGuest: ->
@ -408,34 +380,34 @@ class WebView
# Updates state upon loadcommit. # Updates state upon loadcommit.
onLoadCommit: (@baseUrlForDataUrl, @currentEntryIndex, @entryCount, @processId, url, isTopLevel) -> onLoadCommit: (@baseUrlForDataUrl, @currentEntryIndex, @entryCount, @processId, url, isTopLevel) ->
oldValue = @webviewNode.getAttribute 'src' oldValue = @webviewNode.getAttribute webViewConstants.ATTRIBUTE_SRC
newValue = url newValue = url
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 use the flag ignoreNextSrcAttributeChange here.
this.ignoreNextSrcAttributeChange = true this.ignoreNextSrcAttributeChange = true
this.webviewNode.setAttribute 'src', newValue this.webviewNode.setAttribute webViewConstants.ATTRIBUTE_SRC, newValue
onAttach: (storagePartitionId) -> onAttach: (storagePartitionId) ->
@webviewNode.setAttribute 'partition', storagePartitionId @webviewNode.setAttribute webViewConstants.ATTRIBUTE_PARTITION, storagePartitionId
@partition.fromAttribute storagePartitionId, this.hasNavigated() @attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue storagePartitionId
buildAttachParams: (isNewWindow) -> buildAttachParams: (isNewWindow) ->
allowtransparency: @allowtransparency || false allowtransparency: @attributes[webViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue() || false
autosize: @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_AUTOSIZE autosize: @webviewNode.hasAttribute webViewConstants.ATTRIBUTE_AUTOSIZE
instanceId: @viewInstanceId instanceId: @viewInstanceId
maxheight: parseInt @maxheight || 0 maxheight: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0
maxwidth: parseInt @maxwidth || 0 maxwidth: parseInt @attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0
minheight: parseInt @minheight || 0 minheight: parseInt @attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0
minwidth: parseInt @minwidth || 0 minwidth: parseInt @attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0
# We don't need to navigate new window from here. # We don't need to navigate new window from here.
src: if isNewWindow then undefined else @src src: if isNewWindow then undefined else @attributes[webViewConstants.ATTRIBUTE_SRC].getValue()
# If we have a partition from the opener, that will also be already # If we have a partition from the opener, that will also be already
# set via this.onAttach(). # set via this.onAttach().
storagePartitionId: @partition.toAttribute() storagePartitionId: @attributes[webViewConstants.ATTRIBUTE_PARTITION].getValue()
userAgentOverride: @userAgentOverride userAgentOverride: @userAgentOverride
httpreferrer: @httpreferrer httpreferrer: @attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
attachWindow: (guestInstanceId, isNewWindow) -> attachWindow: (guestInstanceId, isNewWindow) ->
@guestInstanceId = guestInstanceId @guestInstanceId = guestInstanceId
@ -552,3 +524,5 @@ listener = (event) ->
registerWebViewElement() registerWebViewElement()
window.removeEventListener event.type, listener, useCapture window.removeEventListener event.type, listener, useCapture
window.addEventListener 'readystatechange', listener, true window.addEventListener 'readystatechange', listener, true
module.exports = WebView