| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // This module implements WebView (<webview>) as a custom element that wraps a | |
| 6 // BrowserPlugin object element. The object element is hidden within | |
| 7 // the shadow DOM of the WebView element. | |
| 8 | |
| 9 var DocumentNatives = requireNative('document_natives'); | |
| 10 var GuestView = require('guestView').GuestView; | |
| 11 var GuestViewContainer = require('guestViewContainer').GuestViewContainer; | |
| 12 var WebViewConstants = require('webViewConstants').WebViewConstants; | |
| 13 var WebViewEvents = require('webViewEvents').WebViewEvents; | |
| 14 var WebViewInternal = require('webViewInternal').WebViewInternal; | |
| 15 | |
| 16 // Represents the internal state of <webview>. | |
| 17 function WebViewImpl(webviewElement) { | |
| 18 GuestViewContainer.call(this, webviewElement, 'webview'); | |
| 19 | |
| 20 this.setupWebViewAttributes(); | |
| 21 this.setupElementProperties(); | |
| 22 | |
| 23 new WebViewEvents(this, this.viewInstanceId); | |
| 24 } | |
| 25 | |
| 26 WebViewImpl.prototype.__proto__ = GuestViewContainer.prototype; | |
| 27 | |
| 28 WebViewImpl.VIEW_TYPE = 'WebView'; | |
| 29 | |
| 30 // Add extra functionality to |this.element|. | |
| 31 WebViewImpl.setupElement = function(proto) { | |
| 32 // Public-facing API methods. | |
| 33 var apiMethods = WebViewImpl.getApiMethods(); | |
| 34 | |
| 35 // Add the experimental API methods, if available. | |
| 36 var experimentalApiMethods = | |
| 37 WebViewImpl.maybeGetExperimentalApiMethods(); | |
| 38 apiMethods = $Array.concat(apiMethods, experimentalApiMethods); | |
| 39 | |
| 40 // Create default implementations for undefined API methods. | |
| 41 var createDefaultApiMethod = function(m) { | |
| 42 return function(var_args) { | |
| 43 if (!this.guest.getId()) { | |
| 44 return false; | |
| 45 } | |
| 46 var args = $Array.concat([this.guest.getId()], $Array.slice(arguments)); | |
| 47 $Function.apply(WebViewInternal[m], null, args); | |
| 48 return true; | |
| 49 }; | |
| 50 }; | |
| 51 for (var i = 0; i != apiMethods.length; ++i) { | |
| 52 if (WebViewImpl.prototype[apiMethods[i]] == undefined) { | |
| 53 WebViewImpl.prototype[apiMethods[i]] = | |
| 54 createDefaultApiMethod(apiMethods[i]); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 // Forward proto.foo* method calls to WebViewImpl.foo*. | |
| 59 GuestViewContainer.forwardApiMethods(proto, apiMethods); | |
| 60 }; | |
| 61 | |
| 62 // Initiates navigation once the <webview> element is attached to the DOM. | |
| 63 WebViewImpl.prototype.onElementAttached = function() { | |
| 64 for (var i in this.attributes) { | |
| 65 this.attributes[i].attach(); | |
| 66 } | |
| 67 }; | |
| 68 | |
| 69 // Resets some state upon detaching <webview> element from the DOM. | |
| 70 WebViewImpl.prototype.onElementDetached = function() { | |
| 71 this.guest.destroy(); | |
| 72 for (var i in this.attributes) { | |
| 73 this.attributes[i].detach(); | |
| 74 } | |
| 75 }; | |
| 76 | |
| 77 // Sets the <webview>.request property. | |
| 78 WebViewImpl.prototype.setRequestPropertyOnWebViewElement = function(request) { | |
| 79 Object.defineProperty( | |
| 80 this.element, | |
| 81 'request', | |
| 82 { | |
| 83 value: request, | |
| 84 enumerable: true | |
| 85 } | |
| 86 ); | |
| 87 }; | |
| 88 | |
| 89 WebViewImpl.prototype.setupElementProperties = function() { | |
| 90 // We cannot use {writable: true} property descriptor because we want a | |
| 91 // dynamic getter value. | |
| 92 Object.defineProperty(this.element, 'contentWindow', { | |
| 93 get: function() { | |
| 94 if (this.guest.getContentWindow()) { | |
| 95 return this.guest.getContentWindow(); | |
| 96 } | |
| 97 window.console.error( | |
| 98 WebViewConstants.ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE); | |
| 99 }.bind(this), | |
| 100 // No setter. | |
| 101 enumerable: true | |
| 102 }); | |
| 103 }; | |
| 104 | |
| 105 // This observer monitors mutations to attributes of the <webview>. | |
| 106 WebViewImpl.prototype.handleAttributeMutation = function( | |
| 107 attributeName, oldValue, newValue) { | |
| 108 if (!this.attributes[attributeName]) { | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 // Let the changed attribute handle its own mutation; | |
| 113 this.attributes[attributeName].maybeHandleMutation(oldValue, newValue); | |
| 114 }; | |
| 115 | |
| 116 WebViewImpl.prototype.onSizeChanged = function(webViewEvent) { | |
| 117 var newWidth = webViewEvent.newWidth; | |
| 118 var newHeight = webViewEvent.newHeight; | |
| 119 | |
| 120 var element = this.element; | |
| 121 | |
| 122 var width = element.offsetWidth; | |
| 123 var height = element.offsetHeight; | |
| 124 | |
| 125 // Check the current bounds to make sure we do not resize <webview> | |
| 126 // outside of current constraints. | |
| 127 var maxWidth = this.attributes[ | |
| 128 WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || width; | |
| 129 var minWidth = this.attributes[ | |
| 130 WebViewConstants.ATTRIBUTE_MINWIDTH].getValue() || width; | |
| 131 var maxHeight = this.attributes[ | |
| 132 WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || height; | |
| 133 var minHeight = this.attributes[ | |
| 134 WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || height; | |
| 135 | |
| 136 minWidth = Math.min(minWidth, maxWidth); | |
| 137 minHeight = Math.min(minHeight, maxHeight); | |
| 138 | |
| 139 if (!this.attributes[WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue() || | |
| 140 (newWidth >= minWidth && | |
| 141 newWidth <= maxWidth && | |
| 142 newHeight >= minHeight && | |
| 143 newHeight <= maxHeight)) { | |
| 144 element.style.width = newWidth + 'px'; | |
| 145 element.style.height = newHeight + 'px'; | |
| 146 // Only fire the DOM event if the size of the <webview> has actually | |
| 147 // changed. | |
| 148 this.dispatchEvent(webViewEvent); | |
| 149 } | |
| 150 }; | |
| 151 | |
| 152 WebViewImpl.prototype.createGuest = function() { | |
| 153 this.guest.create(this.buildParams(), function() { | |
| 154 this.attachWindow(); | |
| 155 }.bind(this)); | |
| 156 }; | |
| 157 | |
| 158 WebViewImpl.prototype.onFrameNameChanged = function(name) { | |
| 159 this.attributes[WebViewConstants.ATTRIBUTE_NAME].setValueIgnoreMutation(name); | |
| 160 }; | |
| 161 | |
| 162 // Updates state upon loadcommit. | |
| 163 WebViewImpl.prototype.onLoadCommit = function( | |
| 164 baseUrlForDataUrl, currentEntryIndex, entryCount, | |
| 165 processId, url, isTopLevel) { | |
| 166 this.baseUrlForDataUrl = baseUrlForDataUrl; | |
| 167 this.currentEntryIndex = currentEntryIndex; | |
| 168 this.entryCount = entryCount; | |
| 169 this.processId = processId; | |
| 170 if (isTopLevel) { | |
| 171 // Touching the src attribute triggers a navigation. To avoid | |
| 172 // triggering a page reload on every guest-initiated navigation, | |
| 173 // we do not handle this mutation. | |
| 174 this.attributes[ | |
| 175 WebViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(url); | |
| 176 } | |
| 177 }; | |
| 178 | |
| 179 WebViewImpl.prototype.onAttach = function(storagePartitionId) { | |
| 180 this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].setValueIgnoreMutation( | |
| 181 storagePartitionId); | |
| 182 }; | |
| 183 | |
| 184 WebViewImpl.prototype.buildContainerParams = function() { | |
| 185 var params = { 'userAgentOverride': this.userAgentOverride }; | |
| 186 for (var i in this.attributes) { | |
| 187 params[i] = this.attributes[i].getValue(); | |
| 188 } | |
| 189 return params; | |
| 190 }; | |
| 191 | |
| 192 WebViewImpl.prototype.attachWindow = function(opt_guestInstanceId) { | |
| 193 // If |opt_guestInstanceId| was provided, then a different existing guest is | |
| 194 // being attached to this webview, and the current one will get destroyed. | |
| 195 if (opt_guestInstanceId) { | |
| 196 if (this.guest.getId() == opt_guestInstanceId) { | |
| 197 return true; | |
| 198 } | |
| 199 this.guest.destroy(); | |
| 200 this.guest = new GuestView('webview', opt_guestInstanceId); | |
| 201 } | |
| 202 | |
| 203 return GuestViewContainer.prototype.attachWindow.call(this); | |
| 204 }; | |
| 205 | |
| 206 // Shared implementation of executeScript() and insertCSS(). | |
| 207 WebViewImpl.prototype.executeCode = function(func, args) { | |
| 208 if (!this.guest.getId()) { | |
| 209 window.console.error(WebViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT); | |
| 210 return false; | |
| 211 } | |
| 212 | |
| 213 var webviewSrc = this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(); | |
| 214 if (this.baseUrlForDataUrl) { | |
| 215 webviewSrc = this.baseUrlForDataUrl; | |
| 216 } | |
| 217 | |
| 218 args = $Array.concat([this.guest.getId(), webviewSrc], | |
| 219 $Array.slice(args)); | |
| 220 $Function.apply(func, null, args); | |
| 221 return true; | |
| 222 } | |
| 223 | |
| 224 // Implemented when the ChromeWebView API is available. | |
| 225 WebViewImpl.prototype.maybeGetChromeWebViewEvents = function() {}; | |
| 226 | |
| 227 // Implemented when the experimental WebView API is available. | |
| 228 WebViewImpl.maybeGetExperimentalApiMethods = function() { return []; }; | |
| 229 WebViewImpl.prototype.setupExperimentalContextMenus = function() {}; | |
| 230 | |
| 231 GuestViewContainer.registerElement(WebViewImpl); | |
| 232 | |
| 233 // Exports. | |
| 234 exports.WebViewImpl = WebViewImpl; | |
| OLD | NEW |