Chromium Code Reviews| Index: chrome/renderer/resources/extensions/web_view.js |
| diff --git a/chrome/renderer/resources/extensions/web_view.js b/chrome/renderer/resources/extensions/web_view.js |
| index 77bc1a433ce395b36a6da4cb29b393cdc6999db8..7dd16d5a3d7ac7fe43233f0b0065bb3427903cb0 100644 |
| --- a/chrome/renderer/resources/extensions/web_view.js |
| +++ b/chrome/renderer/resources/extensions/web_view.js |
| @@ -8,6 +8,7 @@ |
| var DocumentNatives = requireNative('document_natives'); |
| var EventBindings = require('event_bindings'); |
| +var GuestView = require('binding').Binding.create('guestview').generate(); |
| var IdGenerator = requireNative('id_generator'); |
| var MessagingNatives = requireNative('messaging_natives'); |
| var WebRequestEvent = require('webRequestInternal').WebRequestEvent; |
| @@ -21,18 +22,73 @@ var WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight'; |
| var WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth'; |
| var WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight'; |
| var WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth'; |
| +var WEB_VIEW_ATTRIBUTE_PARTITION = 'partition'; |
| + |
| +var ERROR_MSG_ALREADY_NAVIGATED = |
| + 'The object has already navigated, so its partition cannot be changed.'; |
| +var ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = |
| + 'Invalid partition attribute.'; |
| + |
| +var LOG = function(msg) { |
| + window.console.log(msg); |
| +}; |
| /** @type {Array.<string>} */ |
| var WEB_VIEW_ATTRIBUTES = [ |
| 'allowtransparency', |
| 'autosize', |
| - 'partition', |
| WEB_VIEW_ATTRIBUTE_MINHEIGHT, |
| WEB_VIEW_ATTRIBUTE_MINWIDTH, |
| WEB_VIEW_ATTRIBUTE_MAXHEIGHT, |
| WEB_VIEW_ATTRIBUTE_MAXWIDTH |
| ]; |
| +/** @class representing state of storage partition. */ |
| +function Partition() { |
| + this.validPartitionId = true; |
| + this.persist_storage_ = false; |
| + this.storage_partition_id = ''; |
| +}; |
| + |
| +Partition.prototype.toAttribute = function() { |
| + LOG('Partition.toAttribute'); |
| + if (!this.validPartitionId) { |
| + LOG('Warning we have invalid partition id'); |
| + return ''; |
| + } |
| + return (this.persist_storage_ ? 'persist:' : '') + this.storage_partition_id; |
| +}; |
| + |
| +Partition.prototype.fromAttribute = function(value, hasNavigated) { |
| + LOG('Partition.fromAttribute'); |
| + LOG('value: ' + value + ', hasNavigated: ' + hasNavigated); |
| + var result = {}; |
| + if (hasNavigated) { |
| + result.error = ERROR_MSG_ALREADY_NAVIGATED; |
| + return result; |
| + } |
| + if (!value) { |
| + LOG('Partition.fromAttribute got empty value'); |
| + value = ''; |
| + } |
| + |
| + var LEN = 'persist:'.length; |
| + if (value.substr(0, LEN) == 'persist:') { |
| + value = value.substr(LEN); |
| + if (!value) { |
| + this.validPartitionId = false; |
| + result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; |
| + return result; |
| + } |
| + this.persist_storage_ = true; |
| + } else { |
| + this.persist_storage_ = false; |
| + } |
| + |
| + this.storage_partition_id = value; |
| + return result; |
| +}; |
| + |
| var CreateEvent = function(name) { |
| var eventOpts = {supportsListeners: true, supportsFilters: true}; |
| return new EventBindings.Event(name, undefined, eventOpts); |
| @@ -173,9 +229,14 @@ WebViewInternal.maybeRegisterExperimentalAPIs = function(proto) {} |
| * @constructor |
| */ |
| function WebViewInternal(webviewNode) { |
| + LOG('WebViewInternal.constructor'); |
| privates(webviewNode).internal = this; |
| this.webviewNode = webviewNode; |
| this.attached = false; |
| + |
| + this.beforeFirstNavigation = true; |
| + this.validPartitionId = true; |
| + |
| this.browserPluginNode = this.createBrowserPluginNode(); |
| var shadowRoot = this.webviewNode.createShadowRoot(); |
| shadowRoot.appendChild(this.browserPluginNode); |
| @@ -183,6 +244,13 @@ function WebViewInternal(webviewNode) { |
| this.setupWebviewNodeAttributes(); |
| this.setupFocusPropagation(); |
| this.setupWebviewNodeProperties(); |
| + |
| + this.viewInstanceId = IdGenerator.GetNextId(); |
| + |
| + //this.allocateInstanceId(); |
| + this.partition = new Partition(); |
| + this.parseAttributes(); |
| + |
| this.setupWebviewNodeEvents(); |
| } |
| @@ -195,8 +263,7 @@ WebViewInternal.prototype.createBrowserPluginNode = function() { |
| var browserPluginNode = new WebViewInternal.BrowserPlugin(); |
| privates(browserPluginNode).internal = this; |
| - var ALL_ATTRIBUTES = WEB_VIEW_ATTRIBUTES.concat(['src']); |
| - $Array.forEach(ALL_ATTRIBUTES, function(attributeName) { |
| + $Array.forEach(WEB_VIEW_ATTRIBUTES, function(attributeName) { |
| // Only copy attributes that have been assigned values, rather than copying |
| // a series of undefined attributes to BrowserPlugin. |
| if (this.webviewNode.hasAttribute(attributeName)) { |
| @@ -399,6 +466,26 @@ WebViewInternal.prototype.setupWebviewNodeProperties = function() { |
| enumerable: true |
| }); |
| + Object.defineProperty(this.webviewNode, 'partition', { |
| + get: function() { |
| + window.console.log('getter.partition'); |
| + var ret = self.partition.toAttribute(); |
| + window.console.log('returning: ' + ret); |
| + return ret; |
| + }, |
| + set: function(value) { |
| + window.console.log('setter.partition, value: ' + value); |
| + var result = self.partition.fromAttribute(value, self.hasNavigated()); |
| + if (result.error) { |
| + throw result.error; |
| + } |
| + self.webviewNode.setAttribute('partition', value); |
| + |
| + // TODO(lazyboy): CanRemovePartitionAttribute() when removeAttribute(). |
| + }, |
| + enumerable: true |
| + }); |
| + |
| // We cannot use {writable: true} property descriptor because we want a |
| // dynamic getter value. |
| Object.defineProperty(this.webviewNode, 'contentWindow', { |
| @@ -429,7 +516,7 @@ WebViewInternal.prototype.setupWebViewSrcAttributeMutationObserver = |
| // where the webview guest has crashed and navigating to the same address |
| // spawns off a new process. |
| var self = this; |
| - this.srcObserver = new MutationObserver(function(mutations) { |
| + this.srcAndPartitionObserver = new MutationObserver(function(mutations) { |
| $Array.forEach(mutations, function(mutation) { |
| var oldValue = mutation.oldValue; |
| var newValue = self.webviewNode.getAttribute(mutation.attributeName); |
| @@ -443,9 +530,9 @@ WebViewInternal.prototype.setupWebViewSrcAttributeMutationObserver = |
| var params = { |
| attributes: true, |
| attributeOldValue: true, |
| - attributeFilter: ['src'] |
| + attributeFilter: ['src', 'partition'] |
| }; |
| - this.srcObserver.observe(this.webviewNode, params); |
| + this.srcAndPartitionObserver.observe(this.webviewNode, params); |
| }; |
| /** |
| @@ -453,6 +540,7 @@ WebViewInternal.prototype.setupWebViewSrcAttributeMutationObserver = |
| */ |
| WebViewInternal.prototype.handleWebviewAttributeMutation = |
| function(name, oldValue, newValue) { |
| + window.console.log('handleWebviewAttributeMutation, name = ' + name); |
| // 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 |
| @@ -478,6 +566,7 @@ WebViewInternal.prototype.handleWebviewAttributeMutation = |
| // one case. |
| oldValue = oldValue || ''; |
| newValue = newValue || ''; |
| + LOG('WebViewInternal.handleWebviewAttributeMutation, old = ' + oldValue + ', new = ' + 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. |
| @@ -492,15 +581,32 @@ WebViewInternal.prototype.handleWebviewAttributeMutation = |
| this.src = newValue; |
| if (this.ignoreNextSrcAttributeChange) { |
| // Don't allow the src mutation observer to see this change. |
| - this.srcObserver.takeRecords(); |
| + this.srcAndPartitionObserver.takeRecords(); |
| this.ignoreNextSrcAttributeChange = false; |
| return; |
| } |
| + LOG('srcAttributeChanged'); |
| + var result = {}; |
| + this.parseSrcAttribute(result); |
| + |
| + if (result.error) { |
| + throw result.error; |
| + } |
| + } else if (name == 'partition') { |
| + LOG('handleWebviewAttributeMutation.partition'); |
| + LOG('newValue: ' + newValue + ', oldValue: ' + oldValue); |
| + // TODO(lazyboy): Do we do extra work here if we get here from |
| + // Object.set.partition? |
| + this.partition.fromAttribute(newValue, this.hasNavigated()); |
| } |
| - if (this.browserPluginNode.hasOwnProperty(name)) { |
| - this.browserPluginNode[name] = newValue; |
| - } else { |
| - this.browserPluginNode.setAttribute(name, newValue); |
| + |
| + // No <webview> -> <object> mutation propagation for these attributes. |
| + if (name != 'src' && name != 'partition') { |
|
Fady Samuel
2014/06/03 16:36:34
Early exit preferred?
lazyboy
2014/06/03 19:17:58
Done.
|
| + if (this.browserPluginNode.hasOwnProperty(name)) { |
| + this.browserPluginNode[name] = newValue; |
| + } else { |
| + this.browserPluginNode.setAttribute(name, newValue); |
| + } |
| } |
| }; |
| @@ -509,6 +615,12 @@ WebViewInternal.prototype.handleWebviewAttributeMutation = |
| */ |
| WebViewInternal.prototype.handleBrowserPluginAttributeMutation = |
| function(name, newValue) { |
| + if (name == 'src') { |
| + // name == 'src' here implies autoNavigate = true; |
| + this.autoNavigate_ = true; |
| + this.partition.fromAttribute('persist:custom_plugin', this.hasNavigated()); |
| + } |
| + |
| // This observer monitors mutations to attributes of the BrowserPlugin and |
| // updates the <webview> attributes accordingly. |
| // |newValue| is null if the attribute |name| has been removed. |
| @@ -597,23 +709,89 @@ WebViewInternal.prototype.handleSizeChangedEvent = |
| node.dispatchEvent(webViewEvent); |
| }; |
| +WebViewInternal.prototype.hasNavigated = function() { |
| + return !this.beforeFirstNavigation; |
| +}; |
| + |
| +/** @return {boolean} */ |
| +WebViewInternal.prototype.parseSrcAttribute = function(result) { |
| + LOG('parseSrcAttribute'); |
| + if (!this.partition.validPartitionId) { |
| + LOG('ERROR_MSG_INVALID_PARTITION_ATTRIBUTE'); |
| + result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; |
| + return false; |
| + } |
| + |
| + this.src = this.webviewNode.getAttribute('src'); |
| + LOG('parseSrcAttribute, srcAttribute: ' + this.src); |
|
Fady Samuel
2014/06/03 16:36:34
Can we get rid of all these LOGs?
lazyboy
2014/06/03 19:17:58
Done.
|
| + |
| + if (!this.src) { |
| + LOG('src empty'); |
| + return true; |
| + } |
| + |
| + LOG('hasGuestInstanceID: ' + this.hasGuestInstanceID()); |
| + LOG('beforeFirstNavigation: ' + this.beforeFirstNavigation); |
| + if (!this.hasGuestInstanceID()) { |
| + if (this.beforeFirstNavigation) { |
| + this.beforeFirstNavigation = false; |
| + this.allocateInstanceId(); |
| + } |
| + return true; |
| + } |
| + |
| + LOG('Webview.navigate, i: ' + this.instanceId + ', src = ' + this.src); |
| + |
| + // Navigate to this.src. |
| + WebView.navigate(this.instanceId, this.src); |
| + return true; |
| +}; |
| + |
| +/** @return {boolean} */ |
| +WebViewInternal.prototype.parseAttributes = function() { |
| + LOG('parseAttributes'); |
| + var hasNavigated = this.hasNavigated(); |
| + |
| + var attributeValue = this.webviewNode.getAttribute('partition'); |
| + LOG('parseAttributes, partition.ttributeValue: ' + attributeValue); |
| + var result = this.partition.fromAttribute(attributeValue, hasNavigated); |
| + return this.parseSrcAttribute(result); |
| +}; |
| + |
| +WebViewInternal.prototype.hasGuestInstanceID = function() { |
| + return this.instanceId != undefined; |
| +}; |
| + |
| +WebViewInternal.prototype.allocateInstanceId = function() { |
| + LOG('allocateInstanceId'); |
| + //this.viewInstanceId = IdGenerator.GetNextId(); |
| + LOG('cur partition: ' + this.webviewNode.getAttribute('partition')); |
| + LOG('cur partition prop: ' + this.webviewNode['partition']); |
| + |
| + // Parse .src and .partition. |
| + |
| + var self = this; |
| + GuestView.allocateInstanceId( |
| + 'webview', |
|
Fady Samuel
2014/06/03 16:36:34
Are we doing anything with this at this point? If
lazyboy
2014/06/03 19:17:58
Done.
|
| + this.viewInstanceId, |
|
Fady Samuel
2014/06/03 16:36:34
Do we need this?
lazyboy
2014/06/03 19:17:58
Nope, removed.
|
| + function(info) { |
| + LOG('allocateInstanceId.callback.info: ' + info); |
| + self.instanceId = info; |
| + // TODO(lazyboy): Make sure this.autoNavigate_ stuff correctly updated |
| + // |self.src| at this point. |
| + self.attachWindowAndSetUpEvents(self.instanceId, self.src); |
| + }); |
| +}; |
| + |
| /** |
| * @private |
| */ |
| WebViewInternal.prototype.setupWebviewNodeEvents = function() { |
| - var self = this; |
| - this.viewInstanceId = IdGenerator.GetNextId(); |
| - var onInstanceIdAllocated = function(e) { |
| - var detail = e.detail ? JSON.parse(e.detail) : {}; |
| - self.attachWindowAndSetUpEvents(detail.windowId); |
| - }; |
| - this.browserPluginNode.addEventListener('-internal-instanceid-allocated', |
| - onInstanceIdAllocated); |
| this.setupWebRequestEvents(); |
| this.setupExperimentalContextMenus_(); |
| this.on = {}; |
| - var events = self.getEvents(); |
| + var events = this.getEvents(); |
| for (var eventName in events) { |
| this.setupEventProperty(eventName); |
| } |
| @@ -845,8 +1023,21 @@ WebViewInternal.prototype.handleNewWindowEvent = |
| // asynchronously. |
| setTimeout(function() { |
| var webViewInternal = privates(webview).internal; |
| + LOG('=== Attach w/o a src'); |
| + |
| + if (event.storagePartitionId) { |
| + LOG('read partition string: ' + event.storagePartitionId); |
| + webViewInternal.webviewNode.setAttribute( |
| + 'partition', event.storagePartitionId); |
| + var partition = new Partition(); |
| + partition.fromAttribute(event.storagePartitionId, |
| + webViewInternal.hasNavigated()); |
| + webViewInternal.partition = partition; |
| + } |
| + |
| var attached = |
| - webViewInternal.attachWindowAndSetUpEvents(event.windowId); |
| + webViewInternal.attachWindowAndSetUpEvents( |
| + event.windowId, undefined, event.storagePartitionId); |
| if (!attached) { |
| window.console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH); |
| @@ -1057,16 +1248,22 @@ WebViewInternal.prototype.setUserAgentOverride = function(userAgentOverride) { |
| }; |
| /** @private */ |
| -WebViewInternal.prototype.attachWindowAndSetUpEvents = function(instanceId) { |
| +WebViewInternal.prototype.attachWindowAndSetUpEvents = function( |
| + instanceId, opt_src, opt_partitionId) { |
| this.instanceId = instanceId; |
| + // If we have a partition from the opener, use that instead. |
| + var storagePartitionId = |
| + opt_partitionId || |
| + this.webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) || |
| + this.webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION]; |
| var params = { |
| 'api': 'webview', |
| 'instanceId': this.viewInstanceId, |
| - 'name': this.name |
| + 'name': this.name, |
| + 'src': opt_src, |
| + 'storagePartitionId': storagePartitionId, |
| + 'userAgentOverride': this.userAgentOverride |
| }; |
| - if (this.userAgentOverride) { |
| - params['userAgentOverride'] = this.userAgentOverride; |
| - } |
| this.setupNameAttribute(); |
| var events = this.getEvents(); |
| for (var eventName in events) { |
| @@ -1118,6 +1315,8 @@ function registerWebViewElement() { |
| new WebViewInternal(this); |
| }; |
| + proto.customElementDetached = false; |
| + |
| proto.attributeChangedCallback = function(name, oldValue, newValue) { |
| var internal = privates(this).internal; |
| if (!internal) { |
| @@ -1126,6 +1325,27 @@ function registerWebViewElement() { |
| internal.handleWebviewAttributeMutation(name, oldValue, newValue); |
| }; |
| + proto.detachedCallback = function() { |
| + LOG('<webview> got detached'); |
| + LOG('this.customElementDetached was: ' + this.customElementDetached); |
| + this.customElementDetached = true; |
| + }; |
|
Fady Samuel
2014/06/03 16:36:34
Empty line between these two functions please.
lazyboy
2014/06/03 19:17:58
Done.
|
| + proto.attachedCallback = function() { |
| + LOG('attached, for the first time or subsequent, must init later'); |
| + LOG('this.customElementDetached was: ' + this.customElementDetached); |
| + if (this.customElementDetached) { |
| + var otherThis = privates(this).internal; |
| + LOG('allocateInstanceId again'); |
| + otherThis.instanceId = undefined; |
|
Fady Samuel
2014/06/03 16:36:34
Maybe move this into a helper that makes it clear
lazyboy
2014/06/03 19:17:58
Done.
|
| + otherThis.beforeFirstNavigation = true; |
| + otherThis.validPartitionId = true; |
| + otherThis.partition.validPartitionId = true; |
| + |
| + otherThis.allocateInstanceId(); |
| + } |
| + this.customElementDetached = false; |
| + }; |
| + |
| proto.back = function() { |
| this.go(-1); |
| }; |