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 f0578f7ae76c0d5be08fe63fa17a66c46c24723f..47fb6b00b438c7d984003a0ea3956b62497db6e8 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,63 @@ 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.'; |
| /** @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() { |
| + if (!this.validPartitionId) { |
| + return ''; |
| + } |
| + return (this.persist_storage_ ? 'persist:' : '') + this.storage_partition_id; |
| +}; |
| + |
| +Partition.prototype.fromAttribute = function(value, hasNavigated) { |
| + var result = {}; |
| + if (hasNavigated) { |
| + result.error = ERROR_MSG_ALREADY_NAVIGATED; |
| + return result; |
| + } |
| + if (!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); |
| @@ -176,6 +222,10 @@ function WebViewInternal(webviewNode) { |
| 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 +233,13 @@ function WebViewInternal(webviewNode) { |
| this.setupWebviewNodeAttributes(); |
| this.setupFocusPropagation(); |
| this.setupWebviewNodeProperties(); |
| + |
| + this.viewInstanceId = IdGenerator.GetNextId(); |
| + |
| + //this.allocateInstanceId(); |
|
Fady Samuel
2014/06/03 19:40:40
Remove?
lazyboy
2014/06/03 22:30:11
Done.
|
| + this.partition = new Partition(); |
| + this.parseAttributes(); |
| + |
| this.setupWebviewNodeEvents(); |
| } |
| @@ -195,8 +252,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)) { |
| @@ -217,6 +273,17 @@ WebViewInternal.prototype.createBrowserPluginNode = function() { |
| /** |
| * @private |
| + * Resets some state upon re-attaching <webview> element to the DOM. |
| + */ |
| +WebViewInternal.prototype.resetUponReAttachment = function() { |
|
Fady Samuel
2014/06/03 19:40:40
Call it resetUponReattachment. Reattachment is one
lazyboy
2014/06/03 22:30:11
Done.
|
| + this.instanceId = undefined; |
| + this.beforeFirstNavigation = true; |
| + this.validPartitionId = true; |
| + this.partition.validPartitionId = true; |
| +}; |
| + |
| +/** |
| + * @private |
| */ |
| WebViewInternal.prototype.setupFocusPropagation = function() { |
| if (!this.webviewNode.hasAttribute('tabIndex')) { |
| @@ -399,6 +466,26 @@ WebViewInternal.prototype.setupWebviewNodeProperties = function() { |
| enumerable: true |
| }); |
| + Object.defineProperty(this.webviewNode, 'partition', { |
| + get: function() { |
| + window.console.log('getter.partition'); |
|
Fady Samuel
2014/06/03 19:40:40
Remove
lazyboy
2014/06/03 22:30:11
Wrong grep, fixed.
|
| + var ret = self.partition.toAttribute(); |
|
Fady Samuel
2014/06/03 19:40:40
Return this directly?
lazyboy
2014/06/03 22:30:11
Done.
|
| + window.console.log('returning: ' + ret); |
|
Fady Samuel
2014/06/03 19:40:40
Remove
lazyboy
2014/06/03 22:30:11
Done.
|
| + return ret; |
| + }, |
| + set: function(value) { |
| + window.console.log('setter.partition, value: ' + value); |
|
Fady Samuel
2014/06/03 19:40:40
Remove.
lazyboy
2014/06/03 22:30:11
Done.
|
| + var result = self.partition.fromAttribute(value, self.hasNavigated()); |
| + if (result.error) { |
| + throw result.error; |
| + } |
| + self.webviewNode.setAttribute('partition', value); |
| + |
| + // TODO(lazyboy): CanRemovePartitionAttribute() when removeAttribute(). |
|
Fady Samuel
2014/06/03 19:40:40
Fix this?
lazyboy
2014/06/03 22:30:11
This should be taken care of.
I was thinking webvi
|
| + }, |
| + 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 |
| @@ -492,11 +580,27 @@ 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; |
| } |
| + var result = {}; |
| + this.parseSrcAttribute(result); |
| + |
| + if (result.error) { |
| + throw result.error; |
| + } |
| + } else if (name == 'partition') { |
| + // TODO(lazyboy): Do we do extra work here if we get here from |
| + // Object.set.partition? |
| + this.partition.fromAttribute(newValue, this.hasNavigated()); |
| } |
| + |
| + // No <webview> -> <object> mutation propagation for these attributes. |
| + if (name == 'src' || name == 'partition') { |
| + return; |
| + } |
| + |
| if (this.browserPluginNode.hasOwnProperty(name)) { |
| this.browserPluginNode[name] = newValue; |
| } else { |
| @@ -509,6 +613,12 @@ WebViewInternal.prototype.handleWebviewAttributeMutation = |
| */ |
| WebViewInternal.prototype.handleBrowserPluginAttributeMutation = |
| function(name, newValue) { |
| + if (name == 'src') { |
|
lazyboy
2014/06/03 22:30:11
FYI,
I'm removing this part as this won't help wit
|
| + // 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 +707,69 @@ WebViewInternal.prototype.handleSizeChangedEvent = |
| node.dispatchEvent(webViewEvent); |
| }; |
| +WebViewInternal.prototype.hasNavigated = function() { |
| + return !this.beforeFirstNavigation; |
| +}; |
| + |
| +/** @return {boolean} */ |
| +WebViewInternal.prototype.parseSrcAttribute = function(result) { |
| + if (!this.partition.validPartitionId) { |
| + result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; |
| + return false; |
| + } |
| + this.src = this.webviewNode.getAttribute('src'); |
| + |
| + if (!this.src) { |
| + return true; |
| + } |
| + |
| + if (!this.hasGuestInstanceID()) { |
| + if (this.beforeFirstNavigation) { |
| + this.beforeFirstNavigation = false; |
| + this.allocateInstanceId(); |
| + } |
| + return true; |
| + } |
| + |
| + // Navigate to this.src. |
| + WebView.navigate(this.instanceId, this.src); |
| + return true; |
| +}; |
| + |
| +/** @return {boolean} */ |
| +WebViewInternal.prototype.parseAttributes = function() { |
| + var hasNavigated = this.hasNavigated(); |
| + |
|
Fady Samuel
2014/06/03 19:40:40
Unnecessary new line?
lazyboy
2014/06/03 22:30:11
Removed.
|
| + var attributeValue = this.webviewNode.getAttribute('partition'); |
| + var result = this.partition.fromAttribute(attributeValue, hasNavigated); |
| + return this.parseSrcAttribute(result); |
| +}; |
| + |
| +WebViewInternal.prototype.hasGuestInstanceID = function() { |
| + return this.instanceId != undefined; |
| +}; |
| + |
| +WebViewInternal.prototype.allocateInstanceId = function() { |
| + // Parse .src and .partition. |
| + var self = this; |
| + GuestView.allocateInstanceId( |
| + function(info) { |
|
Fady Samuel
2014/06/03 19:40:40
Give it a more specific name? instanceId?
lazyboy
2014/06/03 22:30:11
Done.
|
| + 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 +1001,18 @@ WebViewInternal.prototype.handleNewWindowEvent = |
| // asynchronously. |
| setTimeout(function() { |
| var webViewInternal = privates(webview).internal; |
| + if (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); |
| @@ -1100,16 +1266,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) { |
| @@ -1161,6 +1333,8 @@ function registerWebViewElement() { |
| new WebViewInternal(this); |
| }; |
| + proto.customElementDetached = false; |
| + |
| proto.attributeChangedCallback = function(name, oldValue, newValue) { |
| var internal = privates(this).internal; |
| if (!internal) { |
| @@ -1169,6 +1343,19 @@ function registerWebViewElement() { |
| internal.handleWebviewAttributeMutation(name, oldValue, newValue); |
| }; |
| + proto.detachedCallback = function() { |
| + this.customElementDetached = true; |
| + }; |
| + |
| + proto.attachedCallback = function() { |
| + if (this.customElementDetached) { |
| + var otherThis = privates(this).internal; |
|
Fady Samuel
2014/06/03 19:40:40
otherThis is a confusing name. This is grabbing th
lazyboy
2014/06/03 22:30:11
Done.
|
| + otherThis.resetUponReAttachment(); |
| + otherThis.allocateInstanceId(); |
| + } |
| + this.customElementDetached = false; |
|
Fady Samuel
2014/06/03 19:40:40
Can't we move this to resetUponReattachment?
lazyboy
2014/06/03 22:30:11
Doesn't seem like, resetUponReattachment is under
|
| + }; |
| + |
| proto.back = function() { |
| this.go(-1); |
| }; |