| 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..50253872eb5ddaa44daa8feb048deff71e644f21 100644
|
| --- a/chrome/renderer/resources/extensions/web_view.js
|
| +++ b/chrome/renderer/resources/extensions/web_view.js
|
| @@ -8,6 +8,8 @@
|
|
|
| var DocumentNatives = requireNative('document_natives');
|
| var EventBindings = require('event_bindings');
|
| +var GuestViewInternal =
|
| + require('binding').Binding.create('guestViewInternal').generate();
|
| var IdGenerator = requireNative('id_generator');
|
| var MessagingNatives = requireNative('messaging_natives');
|
| var WebRequestEvent = require('webRequestInternal').WebRequestEvent;
|
| @@ -21,18 +23,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 +223,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 +234,12 @@ function WebViewInternal(webviewNode) {
|
| this.setupWebviewNodeAttributes();
|
| this.setupFocusPropagation();
|
| this.setupWebviewNodeProperties();
|
| +
|
| + this.viewInstanceId = IdGenerator.GetNextId();
|
| +
|
| + 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 reattaching <webview> element to the DOM.
|
| + */
|
| +WebViewInternal.prototype.resetUponReattachment = function() {
|
| + 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,20 @@ WebViewInternal.prototype.setupWebviewNodeProperties = function() {
|
| enumerable: true
|
| });
|
|
|
| + Object.defineProperty(this.webviewNode, 'partition', {
|
| + get: function() {
|
| + return self.partition.toAttribute();
|
| + },
|
| + set: function(value) {
|
| + var result = self.partition.fromAttribute(value, self.hasNavigated());
|
| + if (result.error) {
|
| + throw result.error;
|
| + }
|
| + self.webviewNode.setAttribute('partition', value);
|
| + },
|
| + enumerable: true
|
| + });
|
| +
|
| // We cannot use {writable: true} property descriptor because we want a
|
| // dynamic getter value.
|
| Object.defineProperty(this.webviewNode, 'contentWindow', {
|
| @@ -429,7 +510,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 +524,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);
|
| };
|
|
|
| /**
|
| @@ -492,11 +573,26 @@ 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') {
|
| + // Note that throwing error here won't synchronously propagate.
|
| + 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 {
|
| @@ -597,23 +693,68 @@ 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();
|
| + 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;
|
| + GuestViewInternal.allocateInstanceId(
|
| + function(instanceId) {
|
| + self.instanceId = instanceId;
|
| + // 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 +986,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 +1251,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 +1318,8 @@ function registerWebViewElement() {
|
| new WebViewInternal(this);
|
| };
|
|
|
| + proto.customElementDetached = false;
|
| +
|
| proto.attributeChangedCallback = function(name, oldValue, newValue) {
|
| var internal = privates(this).internal;
|
| if (!internal) {
|
| @@ -1169,6 +1328,19 @@ function registerWebViewElement() {
|
| internal.handleWebviewAttributeMutation(name, oldValue, newValue);
|
| };
|
|
|
| + proto.detachedCallback = function() {
|
| + this.customElementDetached = true;
|
| + };
|
| +
|
| + proto.attachedCallback = function() {
|
| + if (this.customElementDetached) {
|
| + var webViewInternal = privates(this).internal;
|
| + webViewInternal.resetUponReattachment();
|
| + webViewInternal.allocateInstanceId();
|
| + }
|
| + this.customElementDetached = false;
|
| + };
|
| +
|
| proto.back = function() {
|
| this.go(-1);
|
| };
|
|
|