| 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 // This module implements Webview (<webview>) as a custom element that wraps a | 5 // This module implements Webview (<webview>) as a custom element that wraps a |
| 6 // BrowserPlugin object element. The object element is hidden within | 6 // BrowserPlugin object element. The object element is hidden within |
| 7 // the shadow DOM of the Webview element. | 7 // the shadow DOM of the Webview element. |
| 8 | 8 |
| 9 var DocumentNatives = requireNative('document_natives'); | 9 var DocumentNatives = requireNative('document_natives'); |
| 10 var EventBindings = require('event_bindings'); | |
| 11 var GuestViewInternal = | 10 var GuestViewInternal = |
| 12 require('binding').Binding.create('guestViewInternal').generate(); | 11 require('binding').Binding.create('guestViewInternal').generate(); |
| 13 var IdGenerator = requireNative('id_generator'); | 12 var IdGenerator = requireNative('id_generator'); |
| 14 var MessagingNatives = requireNative('messaging_natives'); | |
| 15 var WebRequestEvent = require('webRequestInternal').WebRequestEvent; | |
| 16 var WebRequestSchema = | |
| 17 requireNative('schema_registry').GetSchema('webRequest'); | |
| 18 var DeclarativeWebRequestSchema = | |
| 19 requireNative('schema_registry').GetSchema('declarativeWebRequest'); | |
| 20 var WebView = require('webview').WebView; | 13 var WebView = require('webview').WebView; |
| 14 var WebViewEvents = require('webViewEvents').WebViewEvents; |
| 21 | 15 |
| 22 var WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight'; | 16 var WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight'; |
| 23 var WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth'; | 17 var WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth'; |
| 24 var WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight'; | 18 var WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight'; |
| 25 var WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth'; | 19 var WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth'; |
| 26 var WEB_VIEW_ATTRIBUTE_PARTITION = 'partition'; | 20 var WEB_VIEW_ATTRIBUTE_PARTITION = 'partition'; |
| 27 | 21 |
| 28 var ERROR_MSG_ALREADY_NAVIGATED = | 22 var ERROR_MSG_ALREADY_NAVIGATED = |
| 29 'The object has already navigated, so its partition cannot be changed.'; | 23 'The object has already navigated, so its partition cannot be changed.'; |
| 30 var ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.'; | 24 var ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.'; |
| 31 | 25 |
| 32 /** @type {Array.<string>} */ | 26 /** @type {Array.<string>} */ |
| 33 var WEB_VIEW_ATTRIBUTES = [ | 27 var WEB_VIEW_ATTRIBUTES = [ |
| 34 'allowtransparency', | 28 'allowtransparency', |
| 35 'autosize', | 29 'autosize', |
| 36 WEB_VIEW_ATTRIBUTE_MINHEIGHT, | 30 WEB_VIEW_ATTRIBUTE_MINHEIGHT, |
| 37 WEB_VIEW_ATTRIBUTE_MINWIDTH, | 31 WEB_VIEW_ATTRIBUTE_MINWIDTH, |
| 38 WEB_VIEW_ATTRIBUTE_MAXHEIGHT, | 32 WEB_VIEW_ATTRIBUTE_MAXHEIGHT, |
| 39 WEB_VIEW_ATTRIBUTE_MAXWIDTH | 33 WEB_VIEW_ATTRIBUTE_MAXWIDTH |
| 40 ]; | 34 ]; |
| 41 | 35 |
| 42 /** @class representing state of storage partition. */ | 36 /** @class representing state of storage partition. */ |
| 43 function Partition() { | 37 function Partition() { |
| 44 this.validPartitionId = true; | 38 this.validPartitionId = true; |
| 45 this.persist_storage_ = false; | 39 this.persistStorage = false; |
| 46 this.storage_partition_id = ''; | 40 this.storagePartitionId = ''; |
| 47 }; | 41 }; |
| 48 | 42 |
| 49 Partition.prototype.toAttribute = function() { | 43 Partition.prototype.toAttribute = function() { |
| 50 if (!this.validPartitionId) { | 44 if (!this.validPartitionId) { |
| 51 return ''; | 45 return ''; |
| 52 } | 46 } |
| 53 return (this.persist_storage_ ? 'persist:' : '') + this.storage_partition_id; | 47 return (this.persistStorage ? 'persist:' : '') + this.storagePartitionId; |
| 54 }; | 48 }; |
| 55 | 49 |
| 56 Partition.prototype.fromAttribute = function(value, hasNavigated) { | 50 Partition.prototype.fromAttribute = function(value, hasNavigated) { |
| 57 var result = {}; | 51 var result = {}; |
| 58 if (hasNavigated) { | 52 if (hasNavigated) { |
| 59 result.error = ERROR_MSG_ALREADY_NAVIGATED; | 53 result.error = ERROR_MSG_ALREADY_NAVIGATED; |
| 60 return result; | 54 return result; |
| 61 } | 55 } |
| 62 if (!value) { | 56 if (!value) { |
| 63 value = ''; | 57 value = ''; |
| 64 } | 58 } |
| 65 | 59 |
| 66 var LEN = 'persist:'.length; | 60 var LEN = 'persist:'.length; |
| 67 if (value.substr(0, LEN) == 'persist:') { | 61 if (value.substr(0, LEN) == 'persist:') { |
| 68 value = value.substr(LEN); | 62 value = value.substr(LEN); |
| 69 if (!value) { | 63 if (!value) { |
| 70 this.validPartitionId = false; | 64 this.validPartitionId = false; |
| 71 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; | 65 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; |
| 72 return result; | 66 return result; |
| 73 } | 67 } |
| 74 this.persist_storage_ = true; | 68 this.persistStorage = true; |
| 75 } else { | 69 } else { |
| 76 this.persist_storage_ = false; | 70 this.persistStorage = false; |
| 77 } | 71 } |
| 78 | 72 |
| 79 this.storage_partition_id = value; | 73 this.storagePartitionId = value; |
| 80 return result; | 74 return result; |
| 81 }; | 75 }; |
| 82 | 76 |
| 83 var CreateEvent = function(name) { | |
| 84 var eventOpts = {supportsListeners: true, supportsFilters: true}; | |
| 85 return new EventBindings.Event(name, undefined, eventOpts); | |
| 86 }; | |
| 87 | |
| 88 // WEB_VIEW_EVENTS is a map of stable <webview> DOM event names to their | |
| 89 // associated extension event descriptor objects. | |
| 90 // An event listener will be attached to the extension event |evt| specified in | |
| 91 // the descriptor. | |
| 92 // |fields| specifies the public-facing fields in the DOM event that are | |
| 93 // accessible to <webview> developers. | |
| 94 // |customHandler| allows a handler function to be called each time an extension | |
| 95 // event is caught by its event listener. The DOM event should be dispatched | |
| 96 // within this handler function. With no handler function, the DOM event | |
| 97 // will be dispatched by default each time the extension event is caught. | |
| 98 // |cancelable| (default: false) specifies whether the event's default | |
| 99 // behavior can be canceled. If the default action associated with the event | |
| 100 // is prevented, then its dispatch function will return false in its event | |
| 101 // handler. The event must have a custom handler for this to be meaningful. | |
| 102 | |
| 103 var FrameNameChangedEvent = CreateEvent('webview.onFrameNameChanged'); | |
| 104 | |
| 105 var WEB_VIEW_EVENTS = { | |
| 106 'close': { | |
| 107 evt: CreateEvent('webview.onClose'), | |
| 108 fields: [] | |
| 109 }, | |
| 110 'consolemessage': { | |
| 111 evt: CreateEvent('webview.onConsoleMessage'), | |
| 112 fields: ['level', 'message', 'line', 'sourceId'] | |
| 113 }, | |
| 114 'contentload': { | |
| 115 evt: CreateEvent('webview.onContentLoad'), | |
| 116 fields: [] | |
| 117 }, | |
| 118 'contextmenu': { | |
| 119 evt: CreateEvent('webview.contextmenu'), | |
| 120 cancelable: true, | |
| 121 customHandler: function(webViewInternal, event, webViewEvent) { | |
| 122 webViewInternal.maybeHandleContextMenu(event, webViewEvent); | |
| 123 }, | |
| 124 fields: ['items'] | |
| 125 }, | |
| 126 'dialog': { | |
| 127 cancelable: true, | |
| 128 customHandler: function(webViewInternal, event, webViewEvent) { | |
| 129 webViewInternal.handleDialogEvent(event, webViewEvent); | |
| 130 }, | |
| 131 evt: CreateEvent('webview.onDialog'), | |
| 132 fields: ['defaultPromptText', 'messageText', 'messageType', 'url'] | |
| 133 }, | |
| 134 'exit': { | |
| 135 evt: CreateEvent('webview.onExit'), | |
| 136 fields: ['processId', 'reason'] | |
| 137 }, | |
| 138 'loadabort': { | |
| 139 cancelable: true, | |
| 140 customHandler: function(webViewInternal, event, webViewEvent) { | |
| 141 webViewInternal.handleLoadAbortEvent(event, webViewEvent); | |
| 142 }, | |
| 143 evt: CreateEvent('webview.onLoadAbort'), | |
| 144 fields: ['url', 'isTopLevel', 'reason'] | |
| 145 }, | |
| 146 'loadcommit': { | |
| 147 customHandler: function(webViewInternal, event, webViewEvent) { | |
| 148 webViewInternal.handleLoadCommitEvent(event, webViewEvent); | |
| 149 }, | |
| 150 evt: CreateEvent('webview.onLoadCommit'), | |
| 151 fields: ['url', 'isTopLevel'] | |
| 152 }, | |
| 153 'loadprogress': { | |
| 154 evt: CreateEvent('webview.onLoadProgress'), | |
| 155 fields: ['url', 'progress'] | |
| 156 }, | |
| 157 'loadredirect': { | |
| 158 evt: CreateEvent('webview.onLoadRedirect'), | |
| 159 fields: ['isTopLevel', 'oldUrl', 'newUrl'] | |
| 160 }, | |
| 161 'loadstart': { | |
| 162 evt: CreateEvent('webview.onLoadStart'), | |
| 163 fields: ['url', 'isTopLevel'] | |
| 164 }, | |
| 165 'loadstop': { | |
| 166 evt: CreateEvent('webview.onLoadStop'), | |
| 167 fields: [] | |
| 168 }, | |
| 169 'newwindow': { | |
| 170 cancelable: true, | |
| 171 customHandler: function(webViewInternal, event, webViewEvent) { | |
| 172 webViewInternal.handleNewWindowEvent(event, webViewEvent); | |
| 173 }, | |
| 174 evt: CreateEvent('webview.onNewWindow'), | |
| 175 fields: [ | |
| 176 'initialHeight', | |
| 177 'initialWidth', | |
| 178 'targetUrl', | |
| 179 'windowOpenDisposition', | |
| 180 'name' | |
| 181 ] | |
| 182 }, | |
| 183 'permissionrequest': { | |
| 184 cancelable: true, | |
| 185 customHandler: function(webViewInternal, event, webViewEvent) { | |
| 186 webViewInternal.handlePermissionEvent(event, webViewEvent); | |
| 187 }, | |
| 188 evt: CreateEvent('webview.onPermissionRequest'), | |
| 189 fields: [ | |
| 190 'identifier', | |
| 191 'lastUnlockedBySelf', | |
| 192 'name', | |
| 193 'permission', | |
| 194 'requestMethod', | |
| 195 'url', | |
| 196 'userGesture' | |
| 197 ] | |
| 198 }, | |
| 199 'responsive': { | |
| 200 evt: CreateEvent('webview.onResponsive'), | |
| 201 fields: ['processId'] | |
| 202 }, | |
| 203 'sizechanged': { | |
| 204 evt: CreateEvent('webview.onSizeChanged'), | |
| 205 customHandler: function(webViewInternal, event, webViewEvent) { | |
| 206 webViewInternal.handleSizeChangedEvent(event, webViewEvent); | |
| 207 }, | |
| 208 fields: ['oldHeight', 'oldWidth', 'newHeight', 'newWidth'] | |
| 209 }, | |
| 210 'unresponsive': { | |
| 211 evt: CreateEvent('webview.onUnresponsive'), | |
| 212 fields: ['processId'] | |
| 213 } | |
| 214 }; | |
| 215 | |
| 216 // Implemented when the experimental API is available. | 77 // Implemented when the experimental API is available. |
| 217 WebViewInternal.maybeRegisterExperimentalAPIs = function(proto) {} | 78 WebViewInternal.maybeRegisterExperimentalAPIs = function(proto) {} |
| 218 | 79 |
| 219 /** | 80 /** |
| 220 * @constructor | 81 * @constructor |
| 221 */ | 82 */ |
| 222 function WebViewInternal(webviewNode) { | 83 function WebViewInternal(webviewNode) { |
| 223 privates(webviewNode).internal = this; | 84 privates(webviewNode).internal = this; |
| 224 this.webviewNode = webviewNode; | 85 this.webviewNode = webviewNode; |
| 225 this.attached = false; | 86 this.attached = false; |
| 226 | 87 |
| 227 this.beforeFirstNavigation = true; | 88 this.beforeFirstNavigation = true; |
| 228 this.validPartitionId = true; | 89 this.validPartitionId = true; |
| 229 | 90 |
| 91 // on* Event handlers. |
| 92 this.on = {}; |
| 93 |
| 230 this.browserPluginNode = this.createBrowserPluginNode(); | 94 this.browserPluginNode = this.createBrowserPluginNode(); |
| 231 var shadowRoot = this.webviewNode.createShadowRoot(); | 95 var shadowRoot = this.webviewNode.createShadowRoot(); |
| 232 shadowRoot.appendChild(this.browserPluginNode); | 96 shadowRoot.appendChild(this.browserPluginNode); |
| 233 | 97 |
| 234 this.setupWebviewNodeAttributes(); | 98 this.setupWebviewNodeAttributes(); |
| 235 this.setupFocusPropagation(); | 99 this.setupFocusPropagation(); |
| 236 this.setupWebviewNodeProperties(); | 100 this.setupWebviewNodeProperties(); |
| 237 | 101 |
| 238 this.viewInstanceId = IdGenerator.GetNextId(); | 102 this.viewInstanceId = IdGenerator.GetNextId(); |
| 239 | 103 |
| 240 this.partition = new Partition(); | 104 this.partition = new Partition(); |
| 241 this.parseAttributes(); | 105 this.parseAttributes(); |
| 242 | 106 |
| 243 this.setupWebviewNodeEvents(); | 107 new WebViewEvents(this, this.viewInstanceId); |
| 244 } | 108 } |
| 245 | 109 |
| 246 /** | 110 /** |
| 247 * @private | 111 * @private |
| 248 */ | 112 */ |
| 249 WebViewInternal.prototype.createBrowserPluginNode = function() { | 113 WebViewInternal.prototype.createBrowserPluginNode = function() { |
| 250 // We create BrowserPlugin as a custom element in order to observe changes | 114 // We create BrowserPlugin as a custom element in order to observe changes |
| 251 // to attributes synchronously. | 115 // to attributes synchronously. |
| 252 var browserPluginNode = new WebViewInternal.BrowserPlugin(); | 116 var browserPluginNode = new WebViewInternal.BrowserPlugin(); |
| 253 privates(browserPluginNode).internal = this; | 117 privates(browserPluginNode).internal = this; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 264 // window.DOMContentLoaded event). | 128 // window.DOMContentLoaded event). |
| 265 // So copy from property if copying from attribute fails. | 129 // So copy from property if copying from attribute fails. |
| 266 browserPluginNode.setAttribute( | 130 browserPluginNode.setAttribute( |
| 267 attributeName, this.webviewNode[attributeName]); | 131 attributeName, this.webviewNode[attributeName]); |
| 268 } | 132 } |
| 269 }, this); | 133 }, this); |
| 270 | 134 |
| 271 return browserPluginNode; | 135 return browserPluginNode; |
| 272 }; | 136 }; |
| 273 | 137 |
| 138 WebViewInternal.prototype.getInstanceId = function() { |
| 139 return this.instanceId; |
| 140 }; |
| 141 |
| 274 /** | 142 /** |
| 275 * @private | |
| 276 * Resets some state upon reattaching <webview> element to the DOM. | 143 * Resets some state upon reattaching <webview> element to the DOM. |
| 277 */ | 144 */ |
| 278 WebViewInternal.prototype.resetUponReattachment = function() { | 145 WebViewInternal.prototype.resetUponReattachment = function() { |
| 279 this.instanceId = undefined; | 146 this.instanceId = undefined; |
| 280 this.beforeFirstNavigation = true; | 147 this.beforeFirstNavigation = true; |
| 281 this.validPartitionId = true; | 148 this.validPartitionId = true; |
| 282 this.partition.validPartitionId = true; | 149 this.partition.validPartitionId = true; |
| 283 }; | 150 }; |
| 284 | 151 |
| 285 /** | 152 // Sets <webview>.request property. |
| 286 * @private | 153 WebViewInternal.prototype.setRequestPropertyOnWebViewNode = function(request) { |
| 287 */ | 154 Object.defineProperty( |
| 155 this.webviewNode, |
| 156 'request', |
| 157 { |
| 158 value: request, |
| 159 enumerable: true |
| 160 } |
| 161 ); |
| 162 }; |
| 163 |
| 288 WebViewInternal.prototype.setupFocusPropagation = function() { | 164 WebViewInternal.prototype.setupFocusPropagation = function() { |
| 289 if (!this.webviewNode.hasAttribute('tabIndex')) { | 165 if (!this.webviewNode.hasAttribute('tabIndex')) { |
| 290 // <webview> needs a tabIndex in order to be focusable. | 166 // <webview> needs a tabIndex in order to be focusable. |
| 291 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute | 167 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute |
| 292 // to allow <webview> to be focusable. | 168 // to allow <webview> to be focusable. |
| 293 // See http://crbug.com/231664. | 169 // See http://crbug.com/231664. |
| 294 this.webviewNode.setAttribute('tabIndex', -1); | 170 this.webviewNode.setAttribute('tabIndex', -1); |
| 295 } | 171 } |
| 296 var self = this; | 172 var self = this; |
| 297 this.webviewNode.addEventListener('focus', function(e) { | 173 this.webviewNode.addEventListener('focus', function(e) { |
| (...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 633 if (newValue != this.webviewNode.getAttribute(name)) { | 509 if (newValue != this.webviewNode.getAttribute(name)) { |
| 634 this.webviewNode.setAttribute(name, newValue); | 510 this.webviewNode.setAttribute(name, newValue); |
| 635 } | 511 } |
| 636 } else { | 512 } else { |
| 637 // If an attribute is removed from the BrowserPlugin, then remove it | 513 // If an attribute is removed from the BrowserPlugin, then remove it |
| 638 // from the <webview> as well. | 514 // from the <webview> as well. |
| 639 this.webviewNode.removeAttribute(name); | 515 this.webviewNode.removeAttribute(name); |
| 640 } | 516 } |
| 641 }; | 517 }; |
| 642 | 518 |
| 643 /** | 519 WebViewInternal.prototype.onSizeChanged = function(newWidth, newHeight) { |
| 644 * @private | |
| 645 */ | |
| 646 WebViewInternal.prototype.getEvents = function() { | |
| 647 var experimentalEvents = this.maybeGetExperimentalEvents(); | |
| 648 for (var eventName in experimentalEvents) { | |
| 649 WEB_VIEW_EVENTS[eventName] = experimentalEvents[eventName]; | |
| 650 } | |
| 651 return WEB_VIEW_EVENTS; | |
| 652 }; | |
| 653 | |
| 654 WebViewInternal.prototype.handleSizeChangedEvent = | |
| 655 function(event, webViewEvent) { | |
| 656 var node = this.webviewNode; | 520 var node = this.webviewNode; |
| 657 | 521 |
| 658 var width = node.offsetWidth; | 522 var width = node.offsetWidth; |
| 659 var height = node.offsetHeight; | 523 var height = node.offsetHeight; |
| 660 | 524 |
| 661 // Check the current bounds to make sure we do not resize <webview> | 525 // Check the current bounds to make sure we do not resize <webview> |
| 662 // outside of current constraints. | 526 // outside of current constraints. |
| 663 var maxWidth; | 527 var maxWidth; |
| 664 if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXWIDTH) && | 528 if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXWIDTH) && |
| 665 node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]) { | 529 node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]) { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 690 if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINHEIGHT) && | 554 if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINHEIGHT) && |
| 691 node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]) { | 555 node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]) { |
| 692 minHeight = node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]; | 556 minHeight = node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]; |
| 693 } else { | 557 } else { |
| 694 minHeight = height; | 558 minHeight = height; |
| 695 } | 559 } |
| 696 if (minHeight > maxHeight) { | 560 if (minHeight > maxHeight) { |
| 697 minHeight = maxHeight; | 561 minHeight = maxHeight; |
| 698 } | 562 } |
| 699 | 563 |
| 700 if (webViewEvent.newWidth >= minWidth && | 564 if (newWidth >= minWidth && |
| 701 webViewEvent.newWidth <= maxWidth && | 565 newWidth <= maxWidth && |
| 702 webViewEvent.newHeight >= minHeight && | 566 newHeight >= minHeight && |
| 703 webViewEvent.newHeight <= maxHeight) { | 567 newHeight <= maxHeight) { |
| 704 node.style.width = webViewEvent.newWidth + 'px'; | 568 node.style.width = newWidth + 'px'; |
| 705 node.style.height = webViewEvent.newHeight + 'px'; | 569 node.style.height = newHeight + 'px'; |
| 706 } | 570 } |
| 707 node.dispatchEvent(webViewEvent); | |
| 708 }; | 571 }; |
| 709 | 572 |
| 710 WebViewInternal.prototype.hasNavigated = function() { | 573 WebViewInternal.prototype.hasNavigated = function() { |
| 711 return !this.beforeFirstNavigation; | 574 return !this.beforeFirstNavigation; |
| 712 }; | 575 }; |
| 713 | 576 |
| 714 /** @return {boolean} */ | 577 /** @return {boolean} */ |
| 715 WebViewInternal.prototype.parseSrcAttribute = function(result) { | 578 WebViewInternal.prototype.parseSrcAttribute = function(result) { |
| 716 if (!this.partition.validPartitionId) { | 579 if (!this.partition.validPartitionId) { |
| 717 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; | 580 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 753 var self = this; | 616 var self = this; |
| 754 GuestViewInternal.allocateInstanceId( | 617 GuestViewInternal.allocateInstanceId( |
| 755 function(instanceId) { | 618 function(instanceId) { |
| 756 self.instanceId = instanceId; | 619 self.instanceId = instanceId; |
| 757 // TODO(lazyboy): Make sure this.autoNavigate_ stuff correctly updated | 620 // TODO(lazyboy): Make sure this.autoNavigate_ stuff correctly updated |
| 758 // |self.src| at this point. | 621 // |self.src| at this point. |
| 759 self.attachWindowAndSetUpEvents(self.instanceId, self.src); | 622 self.attachWindowAndSetUpEvents(self.instanceId, self.src); |
| 760 }); | 623 }); |
| 761 }; | 624 }; |
| 762 | 625 |
| 763 /** | 626 WebViewInternal.prototype.onFrameNameChanged = function(name) { |
| 764 * @private | 627 this.name = name || ''; |
| 765 */ | 628 if (this.name === '') { |
| 766 WebViewInternal.prototype.setupWebviewNodeEvents = function() { | 629 this.webviewNode.removeAttribute('name'); |
| 767 this.setupWebRequestEvents(); | 630 } else { |
| 768 this.setupExperimentalContextMenus_(); | 631 this.webviewNode.setAttribute('name', this.name); |
| 769 | |
| 770 this.on = {}; | |
| 771 var events = this.getEvents(); | |
| 772 for (var eventName in events) { | |
| 773 this.setupEventProperty(eventName); | |
| 774 } | 632 } |
| 775 }; | 633 }; |
| 776 | 634 |
| 777 /** | 635 WebViewInternal.prototype.dispatchEvent = function(webViewEvent) { |
| 778 * @private | 636 return this.webviewNode.dispatchEvent(webViewEvent); |
| 779 */ | |
| 780 WebViewInternal.prototype.setupNameAttribute = function() { | |
| 781 var self = this; | |
| 782 FrameNameChangedEvent.addListener(function(event) { | |
| 783 self.name = event.name || ''; | |
| 784 if (self.name === '') { | |
| 785 self.webviewNode.removeAttribute('name'); | |
| 786 } else { | |
| 787 self.webviewNode.setAttribute('name', self.name); | |
| 788 } | |
| 789 }, {instanceId: self.instanceId}); | |
| 790 }; | |
| 791 | |
| 792 /** | |
| 793 * @private | |
| 794 */ | |
| 795 WebViewInternal.prototype.setupEvent = function(eventName, eventInfo) { | |
| 796 var self = this; | |
| 797 var webviewNode = this.webviewNode; | |
| 798 eventInfo.evt.addListener(function(event) { | |
| 799 var details = {bubbles:true}; | |
| 800 if (eventInfo.cancelable) | |
| 801 details.cancelable = true; | |
| 802 var webViewEvent = new Event(eventName, details); | |
| 803 $Array.forEach(eventInfo.fields, function(field) { | |
| 804 if (event[field] !== undefined) { | |
| 805 webViewEvent[field] = event[field]; | |
| 806 } | |
| 807 }); | |
| 808 if (eventInfo.customHandler) { | |
| 809 eventInfo.customHandler(self, event, webViewEvent); | |
| 810 return; | |
| 811 } | |
| 812 webviewNode.dispatchEvent(webViewEvent); | |
| 813 }, {instanceId: self.instanceId}); | |
| 814 }; | 637 }; |
| 815 | 638 |
| 816 /** | 639 /** |
| 817 * Adds an 'on<event>' property on the webview, which can be used to set/unset | 640 * Adds an 'on<event>' property on the webview, which can be used to set/unset |
| 818 * an event handler. | 641 * an event handler. |
| 819 * @private | |
| 820 */ | 642 */ |
| 821 WebViewInternal.prototype.setupEventProperty = function(eventName) { | 643 WebViewInternal.prototype.setupEventProperty = function(eventName) { |
| 822 var propertyName = 'on' + eventName.toLowerCase(); | 644 var propertyName = 'on' + eventName.toLowerCase(); |
| 823 var self = this; | 645 var self = this; |
| 824 var webviewNode = this.webviewNode; | 646 var webviewNode = this.webviewNode; |
| 825 Object.defineProperty(webviewNode, propertyName, { | 647 Object.defineProperty(webviewNode, propertyName, { |
| 826 get: function() { | 648 get: function() { |
| 827 return self.on[propertyName]; | 649 return self.on[propertyName]; |
| 828 }, | 650 }, |
| 829 set: function(value) { | 651 set: function(value) { |
| 830 if (self.on[propertyName]) | 652 if (self.on[propertyName]) |
| 831 webviewNode.removeEventListener(eventName, self.on[propertyName]); | 653 webviewNode.removeEventListener(eventName, self.on[propertyName]); |
| 832 self.on[propertyName] = value; | 654 self.on[propertyName] = value; |
| 833 if (value) | 655 if (value) |
| 834 webviewNode.addEventListener(eventName, value); | 656 webviewNode.addEventListener(eventName, value); |
| 835 }, | 657 }, |
| 836 enumerable: true | 658 enumerable: true |
| 837 }); | 659 }); |
| 838 }; | 660 }; |
| 839 | 661 |
| 840 /** | 662 // Updates state upon loadcommit. |
| 841 * @private | 663 WebViewInternal.prototype.onLoadCommit = function( |
| 842 */ | 664 currentEntryIndex, entryCount, processId, url, isTopLevel) { |
| 843 WebViewInternal.prototype.getPermissionTypes = function() { | 665 this.currentEntryIndex = currentEntryIndex; |
| 844 var permissions = | 666 this.entryCount = entryCount; |
| 845 ['media', | 667 this.processId = processId; |
| 846 'geolocation', | |
| 847 'pointerLock', | |
| 848 'download', | |
| 849 'loadplugin', | |
| 850 'filesystem']; | |
| 851 return permissions.concat(this.maybeGetExperimentalPermissions()); | |
| 852 }; | |
| 853 | |
| 854 /** | |
| 855 * @private | |
| 856 */ | |
| 857 WebViewInternal.prototype.handleDialogEvent = | |
| 858 function(event, webViewEvent) { | |
| 859 var showWarningMessage = function(dialogType) { | |
| 860 var VOWELS = ['a', 'e', 'i', 'o', 'u']; | |
| 861 var WARNING_MSG_DIALOG_BLOCKED = '<webview>: %1 %2 dialog was blocked.'; | |
| 862 var article = (VOWELS.indexOf(dialogType.charAt(0)) >= 0) ? 'An' : 'A'; | |
| 863 var output = WARNING_MSG_DIALOG_BLOCKED.replace('%1', article); | |
| 864 output = output.replace('%2', dialogType); | |
| 865 window.console.warn(output); | |
| 866 }; | |
| 867 | |
| 868 var self = this; | |
| 869 var webviewNode = this.webviewNode; | |
| 870 var requestId = event.requestId; | |
| 871 var actionTaken = false; | |
| 872 | |
| 873 var validateCall = function() { | |
| 874 var ERROR_MSG_DIALOG_ACTION_ALREADY_TAKEN = '<webview>: ' + | |
| 875 'An action has already been taken for this "dialog" event.'; | |
| 876 | |
| 877 if (actionTaken) { | |
| 878 throw new Error(ERROR_MSG_DIALOG_ACTION_ALREADY_TAKEN); | |
| 879 } | |
| 880 actionTaken = true; | |
| 881 }; | |
| 882 | |
| 883 var dialog = { | |
| 884 ok: function(user_input) { | |
| 885 validateCall(); | |
| 886 user_input = user_input || ''; | |
| 887 WebView.setPermission(self.instanceId, requestId, 'allow', user_input); | |
| 888 }, | |
| 889 cancel: function() { | |
| 890 validateCall(); | |
| 891 WebView.setPermission(self.instanceId, requestId, 'deny'); | |
| 892 } | |
| 893 }; | |
| 894 webViewEvent.dialog = dialog; | |
| 895 | |
| 896 var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent); | |
| 897 if (actionTaken) { | |
| 898 return; | |
| 899 } | |
| 900 | |
| 901 if (defaultPrevented) { | |
| 902 // Tell the JavaScript garbage collector to track lifetime of |dialog| and | |
| 903 // call back when the dialog object has been collected. | |
| 904 MessagingNatives.BindToGC(dialog, function() { | |
| 905 // Avoid showing a warning message if the decision has already been made. | |
| 906 if (actionTaken) { | |
| 907 return; | |
| 908 } | |
| 909 WebView.setPermission( | |
| 910 self.instanceId, requestId, 'default', '', function(allowed) { | |
| 911 if (allowed) { | |
| 912 return; | |
| 913 } | |
| 914 showWarningMessage(event.messageType); | |
| 915 }); | |
| 916 }); | |
| 917 } else { | |
| 918 actionTaken = true; | |
| 919 // The default action is equivalent to canceling the dialog. | |
| 920 WebView.setPermission( | |
| 921 self.instanceId, requestId, 'default', '', function(allowed) { | |
| 922 if (allowed) { | |
| 923 return; | |
| 924 } | |
| 925 showWarningMessage(event.messageType); | |
| 926 }); | |
| 927 } | |
| 928 }; | |
| 929 | |
| 930 /** | |
| 931 * @private | |
| 932 */ | |
| 933 WebViewInternal.prototype.handleLoadAbortEvent = | |
| 934 function(event, webViewEvent) { | |
| 935 var showWarningMessage = function(reason) { | |
| 936 var WARNING_MSG_LOAD_ABORTED = '<webview>: ' + | |
| 937 'The load has aborted with reason "%1".'; | |
| 938 window.console.warn(WARNING_MSG_LOAD_ABORTED.replace('%1', reason)); | |
| 939 }; | |
| 940 if (this.webviewNode.dispatchEvent(webViewEvent)) { | |
| 941 showWarningMessage(event.reason); | |
| 942 } | |
| 943 }; | |
| 944 | |
| 945 /** | |
| 946 * @private | |
| 947 */ | |
| 948 WebViewInternal.prototype.handleLoadCommitEvent = | |
| 949 function(event, webViewEvent) { | |
| 950 this.currentEntryIndex = event.currentEntryIndex; | |
| 951 this.entryCount = event.entryCount; | |
| 952 this.processId = event.processId; | |
| 953 var oldValue = this.webviewNode.getAttribute('src'); | 668 var oldValue = this.webviewNode.getAttribute('src'); |
| 954 var newValue = event.url; | 669 var newValue = url; |
| 955 if (event.isTopLevel && (oldValue != newValue)) { | 670 if (isTopLevel && (oldValue != newValue)) { |
| 956 // Touching the src attribute triggers a navigation. To avoid | 671 // Touching the src attribute triggers a navigation. To avoid |
| 957 // triggering a page reload on every guest-initiated navigation, | 672 // triggering a page reload on every guest-initiated navigation, |
| 958 // we use the flag ignoreNextSrcAttributeChange here. | 673 // we use the flag ignoreNextSrcAttributeChange here. |
| 959 this.ignoreNextSrcAttributeChange = true; | 674 this.ignoreNextSrcAttributeChange = true; |
| 960 this.webviewNode.setAttribute('src', newValue); | 675 this.webviewNode.setAttribute('src', newValue); |
| 961 } | 676 } |
| 962 this.webviewNode.dispatchEvent(webViewEvent); | |
| 963 } | |
| 964 | |
| 965 /** | |
| 966 * @private | |
| 967 */ | |
| 968 WebViewInternal.prototype.handleNewWindowEvent = | |
| 969 function(event, webViewEvent) { | |
| 970 var ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN = '<webview>: ' + | |
| 971 'An action has already been taken for this "newwindow" event.'; | |
| 972 | |
| 973 var ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH = '<webview>: ' + | |
| 974 'Unable to attach the new window to the provided webview.'; | |
| 975 | |
| 976 var ERROR_MSG_WEBVIEW_EXPECTED = '<webview> element expected.'; | |
| 977 | |
| 978 var showWarningMessage = function() { | |
| 979 var WARNING_MSG_NEWWINDOW_BLOCKED = '<webview>: A new window was blocked.'; | |
| 980 window.console.warn(WARNING_MSG_NEWWINDOW_BLOCKED); | |
| 981 }; | |
| 982 | |
| 983 var self = this; | |
| 984 var webviewNode = this.webviewNode; | |
| 985 | |
| 986 var requestId = event.requestId; | |
| 987 var actionTaken = false; | |
| 988 | |
| 989 var validateCall = function () { | |
| 990 if (actionTaken) { | |
| 991 throw new Error(ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN); | |
| 992 } | |
| 993 actionTaken = true; | |
| 994 }; | |
| 995 | |
| 996 var windowObj = { | |
| 997 attach: function(webview) { | |
| 998 validateCall(); | |
| 999 if (!webview || !webview.tagName || webview.tagName != 'WEBVIEW') | |
| 1000 throw new Error(ERROR_MSG_WEBVIEW_EXPECTED); | |
| 1001 // Attach happens asynchronously to give the tagWatcher an opportunity | |
| 1002 // to pick up the new webview before attach operates on it, if it hasn't | |
| 1003 // been attached to the DOM already. | |
| 1004 // Note: Any subsequent errors cannot be exceptions because they happen | |
| 1005 // asynchronously. | |
| 1006 setTimeout(function() { | |
| 1007 var webViewInternal = privates(webview).internal; | |
| 1008 if (event.storagePartitionId) { | |
| 1009 webViewInternal.webviewNode.setAttribute('partition', | |
| 1010 event.storagePartitionId); | |
| 1011 var partition = new Partition(); | |
| 1012 partition.fromAttribute(event.storagePartitionId, | |
| 1013 webViewInternal.hasNavigated()); | |
| 1014 webViewInternal.partition = partition; | |
| 1015 } | |
| 1016 | |
| 1017 var attached = | |
| 1018 webViewInternal.attachWindowAndSetUpEvents( | |
| 1019 event.windowId, undefined, event.storagePartitionId); | |
| 1020 | |
| 1021 if (!attached) { | |
| 1022 window.console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH); | |
| 1023 } | |
| 1024 // If the object being passed into attach is not a valid <webview> | |
| 1025 // then we will fail and it will be treated as if the new window | |
| 1026 // was rejected. The permission API plumbing is used here to clean | |
| 1027 // up the state created for the new window if attaching fails. | |
| 1028 WebView.setPermission( | |
| 1029 self.instanceId, requestId, attached ? 'allow' : 'deny'); | |
| 1030 }, 0); | |
| 1031 }, | |
| 1032 discard: function() { | |
| 1033 validateCall(); | |
| 1034 WebView.setPermission(self.instanceId, requestId, 'deny'); | |
| 1035 } | |
| 1036 }; | |
| 1037 webViewEvent.window = windowObj; | |
| 1038 | |
| 1039 var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent); | |
| 1040 if (actionTaken) { | |
| 1041 return; | |
| 1042 } | |
| 1043 | |
| 1044 if (defaultPrevented) { | |
| 1045 // Make browser plugin track lifetime of |windowObj|. | |
| 1046 MessagingNatives.BindToGC(windowObj, function() { | |
| 1047 // Avoid showing a warning message if the decision has already been made. | |
| 1048 if (actionTaken) { | |
| 1049 return; | |
| 1050 } | |
| 1051 WebView.setPermission( | |
| 1052 self.instanceId, requestId, 'default', '', function(allowed) { | |
| 1053 if (allowed) { | |
| 1054 return; | |
| 1055 } | |
| 1056 showWarningMessage(); | |
| 1057 }); | |
| 1058 }); | |
| 1059 } else { | |
| 1060 actionTaken = true; | |
| 1061 // The default action is to discard the window. | |
| 1062 WebView.setPermission( | |
| 1063 self.instanceId, requestId, 'default', '', function(allowed) { | |
| 1064 if (allowed) { | |
| 1065 return; | |
| 1066 } | |
| 1067 showWarningMessage(); | |
| 1068 }); | |
| 1069 } | |
| 1070 }; | 677 }; |
| 1071 | 678 |
| 1072 WebViewInternal.prototype.handlePermissionEvent = | 679 WebViewInternal.prototype.onAttach = function(storagePartitionId) { |
| 1073 function(event, webViewEvent) { | 680 this.webviewNode.setAttribute('partition', storagePartitionId); |
| 1074 var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' + | 681 this.partition.fromAttribute(storagePartitionId, this.hasNavigated()); |
| 1075 'Permission has already been decided for this "permissionrequest" event.'; | |
| 1076 | |
| 1077 var showWarningMessage = function(permission) { | |
| 1078 var WARNING_MSG_PERMISSION_DENIED = '<webview>: ' + | |
| 1079 'The permission request for "%1" has been denied.'; | |
| 1080 window.console.warn( | |
| 1081 WARNING_MSG_PERMISSION_DENIED.replace('%1', permission)); | |
| 1082 }; | |
| 1083 | |
| 1084 var requestId = event.requestId; | |
| 1085 var self = this; | |
| 1086 | |
| 1087 if (this.getPermissionTypes().indexOf(event.permission) < 0) { | |
| 1088 // The permission type is not allowed. Trigger the default response. | |
| 1089 WebView.setPermission( | |
| 1090 self.instanceId, requestId, 'default', '', function(allowed) { | |
| 1091 if (allowed) { | |
| 1092 return; | |
| 1093 } | |
| 1094 showWarningMessage(event.permission); | |
| 1095 }); | |
| 1096 return; | |
| 1097 } | |
| 1098 | |
| 1099 var webviewNode = this.webviewNode; | |
| 1100 var decisionMade = false; | |
| 1101 | |
| 1102 var validateCall = function() { | |
| 1103 if (decisionMade) { | |
| 1104 throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED); | |
| 1105 } | |
| 1106 decisionMade = true; | |
| 1107 }; | |
| 1108 | |
| 1109 // Construct the event.request object. | |
| 1110 var request = { | |
| 1111 allow: function() { | |
| 1112 validateCall(); | |
| 1113 WebView.setPermission(self.instanceId, requestId, 'allow'); | |
| 1114 }, | |
| 1115 deny: function() { | |
| 1116 validateCall(); | |
| 1117 WebView.setPermission(self.instanceId, requestId, 'deny'); | |
| 1118 } | |
| 1119 }; | |
| 1120 webViewEvent.request = request; | |
| 1121 | |
| 1122 var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent); | |
| 1123 if (decisionMade) { | |
| 1124 return; | |
| 1125 } | |
| 1126 | |
| 1127 if (defaultPrevented) { | |
| 1128 // Make browser plugin track lifetime of |request|. | |
| 1129 MessagingNatives.BindToGC(request, function() { | |
| 1130 // Avoid showing a warning message if the decision has already been made. | |
| 1131 if (decisionMade) { | |
| 1132 return; | |
| 1133 } | |
| 1134 WebView.setPermission( | |
| 1135 self.instanceId, requestId, 'default', '', function(allowed) { | |
| 1136 if (allowed) { | |
| 1137 return; | |
| 1138 } | |
| 1139 showWarningMessage(event.permission); | |
| 1140 }); | |
| 1141 }); | |
| 1142 } else { | |
| 1143 decisionMade = true; | |
| 1144 WebView.setPermission( | |
| 1145 self.instanceId, requestId, 'default', '', function(allowed) { | |
| 1146 if (allowed) { | |
| 1147 return; | |
| 1148 } | |
| 1149 showWarningMessage(event.permission); | |
| 1150 }); | |
| 1151 } | |
| 1152 }; | 682 }; |
| 1153 | 683 |
| 1154 var WebRequestMessageEvent = CreateEvent('webview.onMessage'); | |
| 1155 | |
| 1156 function DeclarativeWebRequestEvent(opt_eventName, | |
| 1157 opt_argSchemas, | |
| 1158 opt_eventOptions, | |
| 1159 opt_webViewInstanceId) { | |
| 1160 var subEventName = opt_eventName + '/' + IdGenerator.GetNextId(); | |
| 1161 EventBindings.Event.call(this, subEventName, opt_argSchemas, opt_eventOptions, | |
| 1162 opt_webViewInstanceId); | |
| 1163 | |
| 1164 var self = this; | |
| 1165 // TODO(lazyboy): When do we dispose this listener? | |
| 1166 WebRequestMessageEvent.addListener(function() { | |
| 1167 // Re-dispatch to subEvent's listeners. | |
| 1168 $Function.apply(self.dispatch, self, $Array.slice(arguments)); | |
| 1169 }, {instanceId: opt_webViewInstanceId || 0}); | |
| 1170 } | |
| 1171 | |
| 1172 DeclarativeWebRequestEvent.prototype = { | |
| 1173 __proto__: EventBindings.Event.prototype | |
| 1174 }; | |
| 1175 | |
| 1176 /** | |
| 1177 * @private | |
| 1178 */ | |
| 1179 WebViewInternal.prototype.setupWebRequestEvents = function() { | |
| 1180 var self = this; | |
| 1181 var request = {}; | |
| 1182 var createWebRequestEvent = function(webRequestEvent) { | |
| 1183 return function() { | |
| 1184 if (!self[webRequestEvent.name]) { | |
| 1185 self[webRequestEvent.name] = | |
| 1186 new WebRequestEvent( | |
| 1187 'webview.' + webRequestEvent.name, | |
| 1188 webRequestEvent.parameters, | |
| 1189 webRequestEvent.extraParameters, webRequestEvent.options, | |
| 1190 self.viewInstanceId); | |
| 1191 } | |
| 1192 return self[webRequestEvent.name]; | |
| 1193 }; | |
| 1194 }; | |
| 1195 | |
| 1196 var createDeclarativeWebRequestEvent = function(webRequestEvent) { | |
| 1197 return function() { | |
| 1198 if (!self[webRequestEvent.name]) { | |
| 1199 // The onMessage event gets a special event type because we want | |
| 1200 // the listener to fire only for messages targeted for this particular | |
| 1201 // <webview>. | |
| 1202 var EventClass = webRequestEvent.name === 'onMessage' ? | |
| 1203 DeclarativeWebRequestEvent : EventBindings.Event; | |
| 1204 self[webRequestEvent.name] = | |
| 1205 new EventClass( | |
| 1206 'webview.' + webRequestEvent.name, | |
| 1207 webRequestEvent.parameters, | |
| 1208 webRequestEvent.options, | |
| 1209 self.viewInstanceId); | |
| 1210 } | |
| 1211 return self[webRequestEvent.name]; | |
| 1212 }; | |
| 1213 }; | |
| 1214 | |
| 1215 for (var i = 0; i < DeclarativeWebRequestSchema.events.length; ++i) { | |
| 1216 var eventSchema = DeclarativeWebRequestSchema.events[i]; | |
| 1217 var webRequestEvent = createDeclarativeWebRequestEvent(eventSchema); | |
| 1218 Object.defineProperty( | |
| 1219 request, | |
| 1220 eventSchema.name, | |
| 1221 { | |
| 1222 get: webRequestEvent, | |
| 1223 enumerable: true | |
| 1224 } | |
| 1225 ); | |
| 1226 } | |
| 1227 | |
| 1228 // Populate the WebRequest events from the API definition. | |
| 1229 for (var i = 0; i < WebRequestSchema.events.length; ++i) { | |
| 1230 var webRequestEvent = createWebRequestEvent(WebRequestSchema.events[i]); | |
| 1231 Object.defineProperty( | |
| 1232 request, | |
| 1233 WebRequestSchema.events[i].name, | |
| 1234 { | |
| 1235 get: webRequestEvent, | |
| 1236 enumerable: true | |
| 1237 } | |
| 1238 ); | |
| 1239 } | |
| 1240 Object.defineProperty( | |
| 1241 this.webviewNode, | |
| 1242 'request', | |
| 1243 { | |
| 1244 value: request, | |
| 1245 enumerable: true | |
| 1246 } | |
| 1247 ); | |
| 1248 }; | |
| 1249 | 684 |
| 1250 /** @private */ | 685 /** @private */ |
| 1251 WebViewInternal.prototype.getUserAgent = function() { | 686 WebViewInternal.prototype.getUserAgent = function() { |
| 1252 return this.userAgentOverride || navigator.userAgent; | 687 return this.userAgentOverride || navigator.userAgent; |
| 1253 }; | 688 }; |
| 1254 | 689 |
| 1255 /** @private */ | 690 /** @private */ |
| 1256 WebViewInternal.prototype.isUserAgentOverridden = function() { | 691 WebViewInternal.prototype.isUserAgentOverridden = function() { |
| 1257 return !!this.userAgentOverride && | 692 return !!this.userAgentOverride && |
| 1258 this.userAgentOverride != navigator.userAgent; | 693 this.userAgentOverride != navigator.userAgent; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 1279 this.webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) || | 714 this.webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) || |
| 1280 this.webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION]; | 715 this.webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION]; |
| 1281 var params = { | 716 var params = { |
| 1282 'api': 'webview', | 717 'api': 'webview', |
| 1283 'instanceId': this.viewInstanceId, | 718 'instanceId': this.viewInstanceId, |
| 1284 'name': this.name, | 719 'name': this.name, |
| 1285 'src': opt_src, | 720 'src': opt_src, |
| 1286 'storagePartitionId': storagePartitionId, | 721 'storagePartitionId': storagePartitionId, |
| 1287 'userAgentOverride': this.userAgentOverride | 722 'userAgentOverride': this.userAgentOverride |
| 1288 }; | 723 }; |
| 1289 this.setupNameAttribute(); | |
| 1290 var events = this.getEvents(); | |
| 1291 for (var eventName in events) { | |
| 1292 this.setupEvent(eventName, events[eventName]); | |
| 1293 } | |
| 1294 | 724 |
| 1295 return this.browserPluginNode['-internal-attach'](this.instanceId, params); | 725 return this.browserPluginNode['-internal-attach'](this.instanceId, params); |
| 1296 }; | 726 }; |
| 1297 | 727 |
| 1298 // Registers browser plugin <object> custom element. | 728 // Registers browser plugin <object> custom element. |
| 1299 function registerBrowserPluginElement() { | 729 function registerBrowserPluginElement() { |
| 1300 var proto = Object.create(HTMLObjectElement.prototype); | 730 var proto = Object.create(HTMLObjectElement.prototype); |
| 1301 | 731 |
| 1302 proto.createdCallback = function() { | 732 proto.createdCallback = function() { |
| 1303 this.setAttribute('type', 'application/browser-plugin'); | 733 this.setAttribute('type', 'application/browser-plugin'); |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1424 */ | 854 */ |
| 1425 WebViewInternal.prototype.maybeGetExperimentalPermissions = function() { | 855 WebViewInternal.prototype.maybeGetExperimentalPermissions = function() { |
| 1426 return []; | 856 return []; |
| 1427 }; | 857 }; |
| 1428 | 858 |
| 1429 /** | 859 /** |
| 1430 * Calls to show contextmenu right away instead of dispatching a 'contextmenu' | 860 * Calls to show contextmenu right away instead of dispatching a 'contextmenu' |
| 1431 * event. | 861 * event. |
| 1432 * This will be overridden in web_view_experimental.js to implement contextmenu | 862 * This will be overridden in web_view_experimental.js to implement contextmenu |
| 1433 * API. | 863 * API. |
| 1434 * @private | |
| 1435 */ | 864 */ |
| 1436 WebViewInternal.prototype.maybeHandleContextMenu = function(e, webViewEvent) { | 865 WebViewInternal.prototype.maybeHandleContextMenu = function(e, webViewEvent) { |
| 1437 var requestId = e.requestId; | 866 var requestId = e.requestId; |
| 1438 // Setting |params| = undefined will show the context menu unmodified, hence | 867 // Setting |params| = undefined will show the context menu unmodified, hence |
| 1439 // the 'contextmenu' API is disabled for stable channel. | 868 // the 'contextmenu' API is disabled for stable channel. |
| 1440 var params = undefined; | 869 var params = undefined; |
| 1441 WebView.showContextMenu(this.instanceId, requestId, params); | 870 WebView.showContextMenu(this.instanceId, requestId, params); |
| 1442 }; | 871 }; |
| 1443 | 872 |
| 1444 /** | 873 /** |
| 1445 * Implemented when the experimental API is available. | 874 * Implemented when the experimental API is available. |
| 1446 * @private | 875 * @private |
| 1447 */ | 876 */ |
| 1448 WebViewInternal.prototype.setupExperimentalContextMenus_ = function() {}; | 877 WebViewInternal.prototype.setupExperimentalContextMenus = function() {}; |
| 1449 | 878 |
| 1450 exports.WebView = WebView; | 879 exports.WebView = WebView; |
| 1451 exports.WebViewInternal = WebViewInternal; | 880 exports.WebViewInternal = WebViewInternal; |
| 1452 exports.CreateEvent = CreateEvent; | |
| OLD | NEW |