diff options
author | fsamuel@chromium.org <fsamuel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-05 06:03:44 +0000 |
---|---|---|
committer | fsamuel@chromium.org <fsamuel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-05 06:03:44 +0000 |
commit | d30c5d41bea758225fa56f57ea848b0309fdb44f (patch) | |
tree | c085c72424e33748b544572733dc7697e7054169 /chrome/renderer | |
parent | 0f8e48e91bd7bc8cad19c5f163f888096ad4fac7 (diff) | |
download | chromium_src-d30c5d41bea758225fa56f57ea848b0309fdb44f.zip chromium_src-d30c5d41bea758225fa56f57ea848b0309fdb44f.tar.gz chromium_src-d30c5d41bea758225fa56f57ea848b0309fdb44f.tar.bz2 |
<webview>: Refactor web_view.js for readability
web_view.js was getting unwieldy. This CL borrows refactorings from ad_view.js
to clean up web_view.js. Eventually the common code between the two should be
moved into a common file. However, I'll leave that until after the big API
refactor lands by the end of this quarter.
BUG=none
Review URL: https://chromiumcodereview.appspot.com/16371002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@204169 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r-- | chrome/renderer/resources/extensions/web_view.js | 214 | ||||
-rw-r--r-- | chrome/renderer/resources/extensions/web_view_experimental.js | 67 |
2 files changed, 164 insertions, 117 deletions
diff --git a/chrome/renderer/resources/extensions/web_view.js b/chrome/renderer/resources/extensions/web_view.js index 3bb6f06..4138215 100644 --- a/chrome/renderer/resources/extensions/web_view.js +++ b/chrome/renderer/resources/extensions/web_view.js @@ -48,135 +48,177 @@ window.addEventListener('DOMContentLoaded', function() { /** * @constructor */ -function WebView(node) { - this.node_ = node; - var shadowRoot = node.webkitCreateShadowRoot(); +function WebView(webviewNode) { + this.webviewNode_ = webviewNode; + this.browserPluginNode_ = this.createBrowserPluginNode_(); + var shadowRoot = this.webviewNode_.webkitCreateShadowRoot(); + shadowRoot.appendChild(this.browserPluginNode_); - this.objectNode_ = document.createElement('object'); - this.objectNode_.type = 'application/browser-plugin'; + this.setupFocusPropagation_(); + this.setupWebviewNodeMethods_(); + this.setupWebviewNodeProperties_(); + this.setupWebviewNodeAttributes_(); + this.setupWebviewNodeEvents_(); + + // Experimental API + this.maybeSetupExperimentalAPI_(); +} + +/** + * @private + */ +WebView.prototype.createBrowserPluginNode_ = function() { + var browserPluginNode = document.createElement('object'); + browserPluginNode.type = 'application/browser-plugin'; // The <object> node fills in the <webview> container. - this.objectNode_.style.width = '100%'; - this.objectNode_.style.height = '100%'; + browserPluginNode.style.width = '100%'; + browserPluginNode.style.height = '100%'; forEach(WEB_VIEW_ATTRIBUTES, function(i, attributeName) { // Only copy attributes that have been assigned values, rather than copying // a series of undefined attributes to BrowserPlugin. - if (this.node_.hasAttribute(attributeName)) { - this.objectNode_.setAttribute( - attributeName, this.node_.getAttribute(attributeName)); + if (this.webviewNode_.hasAttribute(attributeName)) { + browserPluginNode.setAttribute( + attributeName, this.webviewNode_.getAttribute(attributeName)); } }, this); - if (!this.node_.hasAttribute('tabIndex')) { + return browserPluginNode; +} + +/** + * @private + */ +WebView.prototype.setupFocusPropagation_ = function() { + if (!this.webviewNode_.hasAttribute('tabIndex')) { // <webview> needs a tabIndex in order to respond to keyboard focus. // TODO(fsamuel): This introduces unexpected tab ordering. We need to find // a way to take keyboard focus without messing with tab ordering. // See http://crbug.com/231664. - this.node_.setAttribute('tabIndex', 0); + this.webviewNode_.setAttribute('tabIndex', 0); } var self = this; - this.node_.addEventListener('focus', function(e) { + this.webviewNode_.addEventListener('focus', function(e) { // Focus the BrowserPlugin when the <webview> takes focus. - self.objectNode_.focus(); + self.browserPluginNode_.focus(); }); - this.node_.addEventListener('blur', function(e) { + this.webviewNode_.addEventListener('blur', function(e) { // Blur the BrowserPlugin when the <webview> loses focus. - self.objectNode_.blur(); + self.browserPluginNode_.blur(); }); +} - shadowRoot.appendChild(this.objectNode_); - - // this.objectNode_[apiMethod] are not necessarily defined immediately after - // the shadow object is appended to the shadow root. +/** + * @private + */ +WebView.prototype.setupWebviewNodeMethods_ = function() { + // this.browserPluginNode_[apiMethod] are not necessarily defined immediately + // after the shadow object is appended to the shadow root. + var self = this; forEach(WEB_VIEW_API_METHODS, function(i, apiMethod) { - node[apiMethod] = function(var_args) { - return self.objectNode_[apiMethod].apply(self.objectNode_, arguments); + self.webviewNode_[apiMethod] = function(var_args) { + return self.browserPluginNode_[apiMethod].apply( + self.browserPluginNode_, arguments); }; }, this); +} - // Map attribute modifications on the <webview> tag to property changes in - // the underlying <object> node. - var handleMutation = function(i, mutation) { - this.handleMutation_(mutation); - }.bind(this); - var observer = new WebKitMutationObserver(function(mutations) { - forEach(mutations, handleMutation); - }); - observer.observe( - this.node_, - {attributes: true, attributeFilter: WEB_VIEW_ATTRIBUTES}); - - var handleObjectMutation = function(i, mutation) { - this.handleObjectMutation_(mutation); - }.bind(this); - var objectObserver = new WebKitMutationObserver(function(mutations) { - forEach(mutations, handleObjectMutation); - }); - objectObserver.observe( - this.objectNode_, - {attributes: true, attributeFilter: WEB_VIEW_ATTRIBUTES}); - - var objectNode = this.objectNode_; +/** + * @private + */ +WebView.prototype.setupWebviewNodeProperties_ = function() { + var browserPluginNode = this.browserPluginNode_; // Expose getters and setters for the attributes. forEach(WEB_VIEW_ATTRIBUTES, function(i, attributeName) { - Object.defineProperty(this.node_, attributeName, { + Object.defineProperty(this.webviewNode_, attributeName, { get: function() { - return objectNode[attributeName]; + return browserPluginNode[attributeName]; }, set: function(value) { - objectNode[attributeName] = value; + browserPluginNode[attributeName] = value; }, enumerable: true }); }, this); - // We cannot use {writable: true} property descriptor because we want dynamic // getter value. - Object.defineProperty(this.node_, 'contentWindow', { + Object.defineProperty(this.webviewNode_, 'contentWindow', { get: function() { // TODO(fsamuel): This is a workaround to enable // contentWindow.postMessage until http://crbug.com/152006 is fixed. - if (objectNode.contentWindow) - return objectNode.contentWindow.self; + if (browserPluginNode.contentWindow) + return browserPluginNode.contentWindow.self; console.error('contentWindow is not available at this time. ' + 'It will become available when the page has finished loading.'); }, // No setter. enumerable: true }); +} - for (var eventName in WEB_VIEW_EVENTS) { - this.setupEvent_(eventName, WEB_VIEW_EVENTS[eventName]); - } - this.maybeSetupNewWindowEvent_(); - this.maybeSetupPermissionEvent_(); - this.maybeSetupExecuteCodeAPI_(); - this.maybeSetupWebRequestEvents_(); +/** + * @private + */ +WebView.prototype.setupWebviewNodeAttributes_ = function() { + this.setupWebviewNodeObservers_(); + this.setupBrowserPluginNodeObservers_(); } /** * @private */ -WebView.prototype.handleMutation_ = function(mutation) { +WebView.prototype.setupWebviewNodeObservers_ = function() { + // Map attribute modifications on the <webview> tag to property changes in + // the underlying <object> node. + var handleMutation = function(i, mutation) { + this.handleWebviewAttributeMutation_(mutation); + }.bind(this); + var observer = new WebKitMutationObserver(function(mutations) { + forEach(mutations, handleMutation); + }); + observer.observe( + this.webviewNode_, + {attributes: true, attributeFilter: WEB_VIEW_ATTRIBUTES}); +} + +/** + * @private + */ +WebView.prototype.setupBrowserPluginNodeObservers_ = function() { + var handleMutation = function(i, mutation) { + this.handleBrowserPluginAttributeMutation_(mutation); + }.bind(this); + var objectObserver = new WebKitMutationObserver(function(mutations) { + forEach(mutations, handleMutation); + }); + objectObserver.observe( + this.browserPluginNode_, + {attributes: true, attributeFilter: WEB_VIEW_ATTRIBUTES}); +} + +/** + * @private + */ +WebView.prototype.handleWebviewAttributeMutation_ = function(mutation) { // 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 // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more // details. - this.objectNode_[mutation.attributeName] = - this.node_.getAttribute(mutation.attributeName); + this.browserPluginNode_[mutation.attributeName] = + this.webviewNode_.getAttribute(mutation.attributeName); }; /** * @private */ -WebView.prototype.handleObjectMutation_ = function(mutation) { +WebView.prototype.handleBrowserPluginAttributeMutation_ = function(mutation) { // This observer monitors mutations to attributes of the BrowserPlugin and // updates the <webview> attributes accordingly. - if (!this.objectNode_.hasAttribute(mutation.attributeName)) { + if (!this.browserPluginNode_.hasAttribute(mutation.attributeName)) { // If an attribute is removed from the BrowserPlugin, then remove it // from the <webview> as well. - this.node_.removeAttribute(mutation.attributeName); + this.webviewNode_.removeAttribute(mutation.attributeName); } else { // Update the <webview> attribute to match the BrowserPlugin attribute. // Note: Calling setAttribute on <webview> will trigger its mutation @@ -185,10 +227,10 @@ WebView.prototype.handleObjectMutation_ = function(mutation) { // again (such as navigation when crashed), this could end up in an infinite // loop. Thus, we avoid this loop by only updating the <webview> attribute // if the BrowserPlugin attributes differs from it. - var oldValue = this.node_.getAttribute(mutation.attributeName); - var newValue = this.objectNode_.getAttribute(mutation.attributeName); + var oldValue = this.webviewNode_.getAttribute(mutation.attributeName); + var newValue = this.browserPluginNode_.getAttribute(mutation.attributeName); if (newValue != oldValue) { - this.node_.setAttribute(mutation.attributeName, newValue); + this.webviewNode_.setAttribute(mutation.attributeName, newValue); } } }; @@ -196,15 +238,25 @@ WebView.prototype.handleObjectMutation_ = function(mutation) { /** * @private */ +WebView.prototype.setupWebviewNodeEvents_ = function() { + for (var eventName in WEB_VIEW_EVENTS) { + this.setupEvent_(eventName, WEB_VIEW_EVENTS[eventName]); + } +} + +/** + * @private + */ WebView.prototype.setupEvent_ = function(eventname, attribs) { - var node = this.node_; - this.objectNode_.addEventListener('-internal-' + eventname, function(e) { + var webviewNode = this.webviewNode_; + var internalname = '-internal-' + eventname; + this.browserPluginNode_.addEventListener(internalname, function(e) { var evt = new Event(eventname, { bubbles: true }); var detail = e.detail ? JSON.parse(e.detail) : {}; forEach(attribs, function(i, attribName) { evt[attribName] = detail[attribName]; }); - node.dispatchEvent(evt); + webviewNode.dispatchEvent(evt); }); }; @@ -212,24 +264,6 @@ WebView.prototype.setupEvent_ = function(eventname, attribs) { * Implemented when the experimental API is available. * @private */ -WebView.prototype.maybeSetupNewWindowEvent_ = function() {}; - -/** - * Implemented when experimental permission is available. - * @private - */ -WebView.prototype.maybeSetupPermissionEvent_ = function() {}; - -/** - * Implemented when experimental permission is available. - * @private - */ -WebView.prototype.maybeSetupExecuteScript_ = function() {}; - -/** - * Implemented when experimental permission is available. - * @private - */ -WebView.prototype.maybeSetupWebRequestEvents_ = function() {}; +WebView.prototype.maybeSetupExperimentalAPI_ = function() {}; exports.WebView = WebView; diff --git a/chrome/renderer/resources/extensions/web_view_experimental.js b/chrome/renderer/resources/extensions/web_view_experimental.js index b36271c..93eaa22 100644 --- a/chrome/renderer/resources/extensions/web_view_experimental.js +++ b/chrome/renderer/resources/extensions/web_view_experimental.js @@ -43,13 +43,24 @@ var ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN = '<webview>: ' + var ERROR_MSG_WEBVIEW_EXPECTED = '<webview> element expected.'; /** + * @private + */ +WebView.prototype.maybeSetupExperimentalAPI_ = function() { + this.setupNewWindowEvent_(); + this.setupPermissionEvent_(); + this.setupExecuteCodeAPI_(); + this.setupWebRequestEvents_(); +} + +/** * @param {!Object} detail The event details, originated from <object>. * @private */ -WebView.prototype.maybeSetupPermissionEvent_ = function() { - var node = this.node_; - var objectNode = this.objectNode_; - this.objectNode_.addEventListener('-internal-permissionrequest', function(e) { +WebView.prototype.setupPermissionEvent_ = function() { + var node = this.webviewNode_; + var browserPluginNode = this.browserPluginNode_; + var internalevent = '-internal-permissionrequest'; + browserPluginNode.addEventListener(internalevent, function(e) { var evt = new Event('permissionrequest', {bubbles: true, cancelable: true}); var detail = e.detail ? JSON.parse(e.detail) : {}; forEach(EXPOSED_PERMISSION_EVENT_ATTRIBS, function(i, attribName) { @@ -69,7 +80,7 @@ WebView.prototype.maybeSetupPermissionEvent_ = function() { if (decisionMade) { throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED); } else { - objectNode['-internal-setPermission'](requestId, true); + browserPluginNode['-internal-setPermission'](requestId, true); decisionMade = true; } }, @@ -77,7 +88,7 @@ WebView.prototype.maybeSetupPermissionEvent_ = function() { if (decisionMade) { throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED); } else { - objectNode['-internal-setPermission'](requestId, false); + browserPluginNode['-internal-setPermission'](requestId, false); decisionMade = true; } } @@ -85,13 +96,13 @@ WebView.prototype.maybeSetupPermissionEvent_ = function() { evt.request = request; // Make browser plugin track lifetime of |request|. - objectNode['-internal-persistObject']( + browserPluginNode['-internal-persistObject']( request, detail.permission, requestId); var defaultPrevented = !node.dispatchEvent(evt); if (!decisionMade && !defaultPrevented) { decisionMade = true; - objectNode['-internal-setPermission'](requestId, false); + browserPluginNode['-internal-setPermission'](requestId, false); } } }); @@ -100,17 +111,17 @@ WebView.prototype.maybeSetupPermissionEvent_ = function() { /** * @private */ -WebView.prototype.maybeSetupExecuteCodeAPI_ = function() { +WebView.prototype.setupExecuteCodeAPI_ = function() { var self = this; - this.node_['executeScript'] = function(var_args) { - var args = [self.objectNode_.getProcessId(), - self.objectNode_.getRouteId()].concat( + this.webviewNode_['executeScript'] = function(var_args) { + var args = [self.browserPluginNode_.getProcessId(), + self.browserPluginNode_.getRouteId()].concat( Array.prototype.slice.call(arguments)); chrome.webview.executeScript.apply(null, args); } - this.node_['insertCSS'] = function(var_args) { - var args = [self.objectNode_.getProcessId(), - self.objectNode_.getRouteId()].concat( + this.webviewNode_['insertCSS'] = function(var_args) { + var args = [self.browserPluginNode_.getProcessId(), + self.browserPluginNode_.getRouteId()].concat( Array.prototype.slice.call(arguments)); chrome.webview.insertCSS.apply(null, args); } @@ -119,7 +130,7 @@ WebView.prototype.maybeSetupExecuteCodeAPI_ = function() { /** * @private */ -WebView.prototype.maybeSetupNewWindowEvent_ = function() { +WebView.prototype.setupNewWindowEvent_ = function() { var NEW_WINDOW_EVENT_ATTRIBUTES = [ 'initialHeight', 'initialWidth', @@ -128,9 +139,9 @@ WebView.prototype.maybeSetupNewWindowEvent_ = function() { 'name' ]; - var node = this.node_; - var objectNode = this.objectNode_; - objectNode.addEventListener('-internal-newwindow', function(e) { + var node = this.webviewNode_; + var browserPluginNode = this.browserPluginNode_; + browserPluginNode.addEventListener('-internal-newwindow', function(e) { var evt = new Event('newwindow', { bubbles: true, cancelable: true }); var detail = e.detail ? JSON.parse(e.detail) : {}; @@ -158,7 +169,8 @@ WebView.prototype.maybeSetupNewWindowEvent_ = function() { // asynchronously. setTimeout(function() { var attached = - objectNode['-internal-attachWindowTo'](webview, detail.windowId); + browserPluginNode['-internal-attachWindowTo'](webview, + detail.windowId); if (!attached) { console.error('Unable to attach the new window to the provided ' + 'webview.'); @@ -167,24 +179,24 @@ WebView.prototype.maybeSetupNewWindowEvent_ = function() { // then we will fail and it will be treated as if the new window // was rejected. The permission API plumbing is used here to clean // up the state created for the new window if attaching fails. - objectNode['-internal-setPermission'](requestId, attached); + browserPluginNode['-internal-setPermission'](requestId, attached); }, 0); }, discard: function() { validateCall(); - objectNode['-internal-setPermission'](requestId, false); + browserPluginNode['-internal-setPermission'](requestId, false); } }; evt.window = window; // Make browser plugin track lifetime of |window|. - objectNode['-internal-persistObject']( + browserPluginNode['-internal-persistObject']( window, detail.permission, requestId); var defaultPrevented = !node.dispatchEvent(evt); if (!actionTaken && !defaultPrevented) { actionTaken = true; // The default action is to discard the window. - objectNode['-internal-setPermission'](requestId, false); + browserPluginNode['-internal-setPermission'](requestId, false); console.warn('<webview>: A new window was blocked.'); } }); @@ -193,14 +205,15 @@ WebView.prototype.maybeSetupNewWindowEvent_ = function() { /** * @private */ -WebView.prototype.maybeSetupWebRequestEvents_ = function() { +WebView.prototype.setupWebRequestEvents_ = function() { var self = this; // Populate the WebRequest events from the API definition. var webRequestDefinition = GetExtensionAPIDefinitions().filter(function(api) { return api.namespace == 'webRequest'; })[0]; for (var i = 0; i < webRequestDefinition.events.length; ++i) { - Object.defineProperty(self.node_, webRequestDefinition.events[i].name, { + Object.defineProperty(self.webviewNode_, + webRequestDefinition.events[i].name, { get: function(webRequestEvent) { return function() { if (!self[webRequestEvent.name + '_']) { @@ -209,7 +222,7 @@ WebView.prototype.maybeSetupWebRequestEvents_ = function() { 'webview.' + webRequestEvent.name, webRequestEvent.parameters, webRequestEvent.extraParameters, null, - self.objectNode_.getInstanceId()); + self.browserPluginNode_.getInstanceId()); } return self[webRequestEvent.name + '_']; } |