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..2bc68c2dd33f5ab643abdd8e2c973f540261a6f5 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; |
Fady Samuel
2014/05/29 20:35:23
We haven't been using _ for privates now. Let's dr
lazyboy
2014/05/30 05:48:21
Ya, underscores are supposed to be for privates ac
|
+ 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') { |
+ 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); |
+ |
+ 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', |
+ this.viewInstanceId, |
+ 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; |
+ }; |
+ 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; |
+ otherThis.beforeFirstNavigation_ = true; |
+ otherThis.validPartitionId_ = true; |
+ otherThis.partition_.validPartitionId_ = true; |
+ |
+ otherThis.allocateInstanceId(); |
+ } |
+ this.customElementDetached = false; |
+ }; |
+ |
proto.back = function() { |
this.go(-1); |
}; |