| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Shim that simulates a <browser> tag via Mutation Observers. | 5 // Shim that simulates a <webview> tag via Mutation Observers. |
| 6 // | 6 // |
| 7 // The actual tag is implemented via the browser plugin. The internals of this | 7 // The actual tag is implemented via the browser plugin. The internals of this |
| 8 // are hidden via Shadow DOM. | 8 // are hidden via Shadow DOM. |
| 9 | 9 |
| 10 var BROWSER_TAG_ATTRIBUTES = ['src', 'width', 'height']; | 10 var WEB_VIEW_ATTRIBUTES = ['src', 'width', 'height']; |
| 11 | 11 |
| 12 var BROWSER_TAG_READONLY_ATTRIBUTES = ['contentWindow']; | 12 var WEB_VIEW_READONLY_ATTRIBUTES = ['contentWindow']; |
| 13 | 13 |
| 14 // All exposed api methods for <browser>, these are forwarded to the browser | 14 // All exposed api methods for <webview>, these are forwarded to the browser |
| 15 // plugin. | 15 // plugin. |
| 16 var BROWSER_TAG_API_METHODS = [ | 16 var WEB_VIEW_API_METHODS = [ |
| 17 'addEventListener', | 17 'addEventListener', |
| 18 'back', | 18 'back', |
| 19 'canGoBack', | 19 'canGoBack', |
| 20 'canGoForward', | 20 'canGoForward', |
| 21 'forward', | 21 'forward', |
| 22 'getProcessId', | 22 'getProcessId', |
| 23 'go', | 23 'go', |
| 24 'reload', | 24 'reload', |
| 25 'removeEventListener', | 25 'removeEventListener', |
| 26 'stop', | 26 'stop', |
| 27 'terminate' | 27 'terminate' |
| 28 ]; | 28 ]; |
| 29 | 29 |
| 30 window.addEventListener('DOMContentLoaded', function() { | 30 window.addEventListener('DOMContentLoaded', function() { |
| 31 // Handle <browser> tags already in the document. | 31 // Handle <webview> tags already in the document. |
| 32 var browserNodes = document.body.querySelectorAll('browser'); | 32 var webViewNodes = document.body.querySelectorAll('webview'); |
| 33 for (var i = 0, browserNode; browserNode = browserNodes[i]; i++) { | 33 for (var i = 0, webViewNode; webViewNode = webViewNodes[i]; i++) { |
| 34 new BrowserTag(browserNode); | 34 new WebView(webViewNode); |
| 35 } | 35 } |
| 36 | 36 |
| 37 // Handle <browser> tags added later. | 37 // Handle <webview> tags added later. |
| 38 var documentObserver = new WebKitMutationObserver(function(mutations) { | 38 var documentObserver = new WebKitMutationObserver(function(mutations) { |
| 39 mutations.forEach(function(mutation) { | 39 mutations.forEach(function(mutation) { |
| 40 for (var i = 0, addedNode; addedNode = mutation.addedNodes[i]; i++) { | 40 for (var i = 0, addedNode; addedNode = mutation.addedNodes[i]; i++) { |
| 41 if (addedNode.tagName == 'BROWSER') { | 41 if (addedNode.tagName == 'WEBVIEW') { |
| 42 new BrowserTag(addedNode); | 42 new WebView(addedNode); |
| 43 } | 43 } |
| 44 } | 44 } |
| 45 }); | 45 }); |
| 46 }); | 46 }); |
| 47 documentObserver.observe(document, {subtree: true, childList: true}); | 47 documentObserver.observe(document, {subtree: true, childList: true}); |
| 48 }); | 48 }); |
| 49 | 49 |
| 50 /** | 50 /** |
| 51 * @constructor | 51 * @constructor |
| 52 */ | 52 */ |
| 53 function BrowserTag(node) { | 53 function WebView(node) { |
| 54 this.node_ = node; | 54 this.node_ = node; |
| 55 var shadowRoot = new WebKitShadowRoot(node); | 55 var shadowRoot = new WebKitShadowRoot(node); |
| 56 | 56 |
| 57 this.objectNode_ = document.createElement('object'); | 57 this.objectNode_ = document.createElement('object'); |
| 58 this.objectNode_.type = 'application/browser-plugin'; | 58 this.objectNode_.type = 'application/browser-plugin'; |
| 59 BROWSER_TAG_ATTRIBUTES.forEach(this.copyAttribute_, this); | 59 WEB_VIEW_ATTRIBUTES.forEach(this.copyAttribute_, this); |
| 60 | 60 |
| 61 shadowRoot.appendChild(this.objectNode_); | 61 shadowRoot.appendChild(this.objectNode_); |
| 62 | 62 |
| 63 // this.objectNode_[apiMethod] are defined after the shadow object is appended | 63 // this.objectNode_[apiMethod] are defined after the shadow object is appended |
| 64 // to the shadow root. | 64 // to the shadow root. |
| 65 BROWSER_TAG_API_METHODS.forEach(function(apiMethod) { | 65 WEB_VIEW_API_METHODS.forEach(function(apiMethod) { |
| 66 node[apiMethod] = this.objectNode_[apiMethod].bind(this.objectNode_); | 66 node[apiMethod] = this.objectNode_[apiMethod].bind(this.objectNode_); |
| 67 }, this); | 67 }, this); |
| 68 | 68 |
| 69 // Map attribute modifications on the <browser> tag to changes on the | 69 // Map attribute modifications on the <webview> tag to changes on the |
| 70 // underlying <object> node. | 70 // underlying <object> node. |
| 71 var handleMutation = this.handleMutation_.bind(this); | 71 var handleMutation = this.handleMutation_.bind(this); |
| 72 var observer = new WebKitMutationObserver(function(mutations) { | 72 var observer = new WebKitMutationObserver(function(mutations) { |
| 73 mutations.forEach(handleMutation); | 73 mutations.forEach(handleMutation); |
| 74 }); | 74 }); |
| 75 observer.observe( | 75 observer.observe( |
| 76 this.node_, | 76 this.node_, |
| 77 {attributes: true, attributeFilter: BROWSER_TAG_ATTRIBUTES}); | 77 {attributes: true, attributeFilter: WEB_VIEW_ATTRIBUTES}); |
| 78 | 78 |
| 79 var objectNode = this.objectNode_; | 79 var objectNode = this.objectNode_; |
| 80 // Expose getters and setters for the attributes. | 80 // Expose getters and setters for the attributes. |
| 81 BROWSER_TAG_ATTRIBUTES.forEach(function(attributeName) { | 81 WEB_VIEW_ATTRIBUTES.forEach(function(attributeName) { |
| 82 Object.defineProperty(this.node_, attributeName, { | 82 Object.defineProperty(this.node_, attributeName, { |
| 83 get: function() { | 83 get: function() { |
| 84 if (attributeName == 'src') { | 84 if (attributeName == 'src') { |
| 85 // Always read src attribute from the plugin <object> since: a) It can | 85 // Always read src attribute from the plugin <object> since: a) It can |
| 86 // have different value when empty src is set. b) BrowserPlugin | 86 // have different value when empty src is set. b) BrowserPlugin |
| 87 // updates its src attribute on guest-initiated navigations. | 87 // updates its src attribute on guest-initiated navigations. |
| 88 return objectNode.src; | 88 return objectNode.src; |
| 89 } | 89 } |
| 90 var value = node.getAttribute(attributeName); | 90 var value = node.getAttribute(attributeName); |
| 91 var numericValue = parseInt(value, 10); | 91 var numericValue = parseInt(value, 10); |
| 92 return isNaN(numericValue) ? value : numericValue; | 92 return isNaN(numericValue) ? value : numericValue; |
| 93 }, | 93 }, |
| 94 set: function(value) { | 94 set: function(value) { |
| 95 node.setAttribute(attributeName, value); | 95 node.setAttribute(attributeName, value); |
| 96 }, | 96 }, |
| 97 enumerable: true | 97 enumerable: true |
| 98 }); | 98 }); |
| 99 }, this); | 99 }, this); |
| 100 | 100 |
| 101 // We cannot use {writable: true} property descriptor because we want dynamic | 101 // We cannot use {writable: true} property descriptor because we want dynamic |
| 102 // getter value. | 102 // getter value. |
| 103 BROWSER_TAG_READONLY_ATTRIBUTES.forEach(function(attributeName) { | 103 WEB_VIEW_READONLY_ATTRIBUTES.forEach(function(attributeName) { |
| 104 Object.defineProperty(this.node_, attributeName, { | 104 Object.defineProperty(this.node_, attributeName, { |
| 105 get: function() { | 105 get: function() { |
| 106 // Read these attributes from the plugin <object>. | 106 // Read these attributes from the plugin <object>. |
| 107 return objectNode[attributeName]; | 107 return objectNode[attributeName]; |
| 108 }, | 108 }, |
| 109 // No setter. | 109 // No setter. |
| 110 enumerable: true | 110 enumerable: true |
| 111 }) | 111 }) |
| 112 }, this); | 112 }, this); |
| 113 }; | 113 }; |
| 114 | 114 |
| 115 /** | 115 /** |
| 116 * @private | 116 * @private |
| 117 */ | 117 */ |
| 118 BrowserTag.prototype.handleMutation_ = function(mutation) { | 118 WebView.prototype.handleMutation_ = function(mutation) { |
| 119 switch (mutation.attributeName) { | 119 switch (mutation.attributeName) { |
| 120 case 'src': | 120 case 'src': |
| 121 // We need to set .src directly on the shadow element so that | 121 // We need to set .src directly on the shadow element so that |
| 122 // BrowserPluginBindings catches this as src attribute mutation. The | 122 // BrowserPluginBindings catches this as src attribute mutation. The |
| 123 // bindings would catch 'SetAttribute' method call with src as argument | 123 // bindings would catch 'SetAttribute' method call with src as argument |
| 124 // otherwise. | 124 // otherwise. |
| 125 this.objectNode_.src = this.node_.getAttribute('src'); | 125 this.objectNode_.src = this.node_.getAttribute('src'); |
| 126 break; | 126 break; |
| 127 default: | 127 default: |
| 128 this.copyAttribute_(mutation.attributeName); | 128 this.copyAttribute_(mutation.attributeName); |
| 129 break; | 129 break; |
| 130 } | 130 } |
| 131 }; | 131 }; |
| 132 | 132 |
| 133 /** | 133 /** |
| 134 * @private | 134 * @private |
| 135 */ | 135 */ |
| 136 BrowserTag.prototype.copyAttribute_ = function(attributeName) { | 136 WebView.prototype.copyAttribute_ = function(attributeName) { |
| 137 this.objectNode_.setAttribute( | 137 this.objectNode_.setAttribute( |
| 138 attributeName, this.node_.getAttribute(attributeName)); | 138 attributeName, this.node_.getAttribute(attributeName)); |
| 139 }; | 139 }; |
| OLD | NEW |