| 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 GuestViewInternal = | 10 var GuestViewInternal = |
| 11 require('binding').Binding.create('guestViewInternal').generate(); | 11 require('binding').Binding.create('guestViewInternal').generate(); |
| 12 var guestViewInternalNatives = requireNative('guest_view_internal'); | 12 var guestViewInternalNatives = requireNative('guest_view_internal'); |
| 13 var IdGenerator = requireNative('id_generator'); | 13 var IdGenerator = requireNative('id_generator'); |
| 14 var WebViewConstants = require('webViewConstants').WebViewConstants; | 14 var WebViewConstants = require('webViewConstants').WebViewConstants; |
| 15 var WebViewEvents = require('webViewEvents').WebViewEvents; | 15 var WebViewEvents = require('webViewEvents').WebViewEvents; |
| 16 var WebViewInternal = require('webViewInternal').WebViewInternal; | 16 var WebViewInternal = require('webViewInternal').WebViewInternal; |
| 17 | 17 |
| 18 // Attributes. |
| 18 var AUTO_SIZE_ATTRIBUTES = [ | 19 var AUTO_SIZE_ATTRIBUTES = [ |
| 19 WebViewConstants.ATTRIBUTE_AUTOSIZE, | 20 WebViewConstants.ATTRIBUTE_AUTOSIZE, |
| 20 WebViewConstants.ATTRIBUTE_MAXHEIGHT, | 21 WebViewConstants.ATTRIBUTE_MAXHEIGHT, |
| 21 WebViewConstants.ATTRIBUTE_MAXWIDTH, | 22 WebViewConstants.ATTRIBUTE_MAXWIDTH, |
| 22 WebViewConstants.ATTRIBUTE_MINHEIGHT, | 23 WebViewConstants.ATTRIBUTE_MINHEIGHT, |
| 23 WebViewConstants.ATTRIBUTE_MINWIDTH | 24 WebViewConstants.ATTRIBUTE_MINWIDTH |
| 24 ]; | 25 ]; |
| 25 | 26 |
| 26 // Represents the state of the storage partition. | |
| 27 function Partition() { | |
| 28 this.validPartitionId = true; | |
| 29 this.persistStorage = false; | |
| 30 this.storagePartitionId = ''; | |
| 31 } | |
| 32 | |
| 33 Partition.prototype.toAttribute = function() { | |
| 34 if (!this.validPartitionId) { | |
| 35 return ''; | |
| 36 } | |
| 37 return (this.persistStorage ? 'persist:' : '') + this.storagePartitionId; | |
| 38 }; | |
| 39 | |
| 40 Partition.prototype.fromAttribute = function(value, hasNavigated) { | |
| 41 var result = {}; | |
| 42 if (hasNavigated) { | |
| 43 result.error = WebViewConstants.ERROR_MSG_ALREADY_NAVIGATED; | |
| 44 return result; | |
| 45 } | |
| 46 if (!value) { | |
| 47 value = ''; | |
| 48 } | |
| 49 | |
| 50 var LEN = 'persist:'.length; | |
| 51 if (value.substr(0, LEN) == 'persist:') { | |
| 52 value = value.substr(LEN); | |
| 53 if (!value) { | |
| 54 this.validPartitionId = false; | |
| 55 result.error = WebViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; | |
| 56 return result; | |
| 57 } | |
| 58 this.persistStorage = true; | |
| 59 } else { | |
| 60 this.persistStorage = false; | |
| 61 } | |
| 62 | |
| 63 this.storagePartitionId = value; | |
| 64 return result; | |
| 65 }; | |
| 66 | |
| 67 // Represents the internal state of the WebView node. | 27 // Represents the internal state of the WebView node. |
| 68 function WebView(webviewNode) { | 28 function WebView(webviewNode) { |
| 69 privates(webviewNode).internal = this; | 29 privates(webviewNode).internal = this; |
| 70 this.webviewNode = webviewNode; | 30 this.webviewNode = webviewNode; |
| 71 this.attached = false; | 31 this.attached = false; |
| 72 this.pendingGuestCreation = false; | 32 this.pendingGuestCreation = false; |
| 73 this.elementAttached = false; | 33 this.elementAttached = false; |
| 74 | 34 |
| 75 this.beforeFirstNavigation = true; | 35 this.beforeFirstNavigation = true; |
| 76 this.contentWindow = null; | 36 this.contentWindow = null; |
| 77 this.validPartitionId = true; | 37 this.validPartitionId = true; |
| 78 // Used to save some state upon deferred attachment. | 38 // Used to save some state upon deferred attachment. |
| 79 // If <object> bindings is not available, we defer attachment. | 39 // If <object> bindings is not available, we defer attachment. |
| 80 // This state contains whether or not the attachment request was for | 40 // This state contains whether or not the attachment request was for |
| 81 // newwindow. | 41 // newwindow. |
| 82 this.deferredAttachState = null; | 42 this.deferredAttachState = null; |
| 83 | 43 |
| 84 // on* Event handlers. | 44 // on* Event handlers. |
| 85 this.on = {}; | 45 this.on = {}; |
| 86 | 46 |
| 87 this.browserPluginNode = this.createBrowserPluginNode(); | 47 this.browserPluginNode = this.createBrowserPluginNode(); |
| 88 var shadowRoot = this.webviewNode.createShadowRoot(); | 48 var shadowRoot = this.webviewNode.createShadowRoot(); |
| 89 this.partition = new Partition(); | 49 this.setupWebViewAttributes(); |
| 90 | |
| 91 this.setupWebViewSrcAttributeMutationObserver(); | 50 this.setupWebViewSrcAttributeMutationObserver(); |
| 92 this.setupFocusPropagation(); | 51 this.setupFocusPropagation(); |
| 93 this.setupWebviewNodeProperties(); | 52 this.setupWebviewNodeProperties(); |
| 94 | 53 |
| 95 this.viewInstanceId = IdGenerator.GetNextId(); | 54 this.viewInstanceId = IdGenerator.GetNextId(); |
| 96 | 55 |
| 97 new WebViewEvents(this, this.viewInstanceId); | 56 new WebViewEvents(this, this.viewInstanceId); |
| 98 | 57 |
| 99 shadowRoot.appendChild(this.browserPluginNode); | 58 shadowRoot.appendChild(this.browserPluginNode); |
| 100 } | 59 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 117 // already picked up a partition ID. Thus, we need to reset the initialization | 76 // already picked up a partition ID. Thus, we need to reset the initialization |
| 118 // state. However, it may be the case that beforeFirstNavigation is false BUT | 77 // state. However, it may be the case that beforeFirstNavigation is false BUT |
| 119 // guestInstanceId has yet to be initialized. This means that we have not | 78 // guestInstanceId has yet to be initialized. This means that we have not |
| 120 // heard back from createGuest yet. We will not reset the flag in this case so | 79 // heard back from createGuest yet. We will not reset the flag in this case so |
| 121 // that we don't end up allocating a second guest. | 80 // that we don't end up allocating a second guest. |
| 122 if (this.guestInstanceId) { | 81 if (this.guestInstanceId) { |
| 123 GuestViewInternal.destroyGuest(this.guestInstanceId); | 82 GuestViewInternal.destroyGuest(this.guestInstanceId); |
| 124 this.guestInstanceId = undefined; | 83 this.guestInstanceId = undefined; |
| 125 this.beforeFirstNavigation = true; | 84 this.beforeFirstNavigation = true; |
| 126 this.validPartitionId = true; | 85 this.validPartitionId = true; |
| 127 this.partition.validPartitionId = true; | 86 this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].validPartitionId = |
| 87 true; |
| 128 this.contentWindow = null; | 88 this.contentWindow = null; |
| 129 } | 89 } |
| 130 this.internalInstanceId = 0; | 90 this.internalInstanceId = 0; |
| 131 }; | 91 }; |
| 132 | 92 |
| 133 // Sets the <webview>.request property. | 93 // Sets the <webview>.request property. |
| 134 WebView.prototype.setRequestPropertyOnWebViewNode = function(request) { | 94 WebView.prototype.setRequestPropertyOnWebViewNode = function(request) { |
| 135 Object.defineProperty( | 95 Object.defineProperty( |
| 136 this.webviewNode, | 96 this.webviewNode, |
| 137 'request', | 97 'request', |
| (...skipping 24 matching lines...) Expand all Loading... |
| 162 | 122 |
| 163 // Validation helper function for executeScript() and insertCSS(). | 123 // Validation helper function for executeScript() and insertCSS(). |
| 164 WebView.prototype.validateExecuteCodeCall = function() { | 124 WebView.prototype.validateExecuteCodeCall = function() { |
| 165 if (!this.guestInstanceId) { | 125 if (!this.guestInstanceId) { |
| 166 throw new Error(WebViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT); | 126 throw new Error(WebViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT); |
| 167 } | 127 } |
| 168 }; | 128 }; |
| 169 | 129 |
| 170 WebView.prototype.setupAutoSizeProperties = function() { | 130 WebView.prototype.setupAutoSizeProperties = function() { |
| 171 $Array.forEach(AUTO_SIZE_ATTRIBUTES, function(attributeName) { | 131 $Array.forEach(AUTO_SIZE_ATTRIBUTES, function(attributeName) { |
| 172 this[attributeName] = this.webviewNode.getAttribute(attributeName); | 132 this.attributes[attributeName].setValue( |
| 133 this.webviewNode.getAttribute(attributeName)); |
| 173 Object.defineProperty(this.webviewNode, attributeName, { | 134 Object.defineProperty(this.webviewNode, attributeName, { |
| 174 get: function() { | 135 get: function() { |
| 175 return this[attributeName]; | 136 return this.attributes[attributeName].getValue(); |
| 176 }.bind(this), | 137 }.bind(this), |
| 177 set: function(value) { | 138 set: function(value) { |
| 178 this.webviewNode.setAttribute(attributeName, value); | 139 this.webviewNode.setAttribute(attributeName, value); |
| 179 }.bind(this), | 140 }.bind(this), |
| 180 enumerable: true | 141 enumerable: true |
| 181 }); | 142 }); |
| 182 }.bind(this), this); | 143 }.bind(this), this); |
| 183 }; | 144 }; |
| 184 | 145 |
| 185 WebView.prototype.setupWebviewNodeProperties = function() { | 146 WebView.prototype.setupWebviewNodeProperties = function() { |
| 186 this.setupAutoSizeProperties(); | 147 this.setupAutoSizeProperties(); |
| 187 | 148 |
| 188 Object.defineProperty(this.webviewNode, | 149 Object.defineProperty(this.webviewNode, |
| 189 WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, { | 150 WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, { |
| 190 get: function() { | 151 get: function() { |
| 191 return this.allowtransparency; | 152 return this.attributes[WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY]. |
| 153 getValue(); |
| 192 }.bind(this), | 154 }.bind(this), |
| 193 set: function(value) { | 155 set: function(value) { |
| 194 this.webviewNode.setAttribute( | 156 this.webviewNode.setAttribute( |
| 195 WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, | 157 WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, |
| 196 value); | 158 value); |
| 197 }.bind(this), | 159 }.bind(this), |
| 198 enumerable: true | 160 enumerable: true |
| 199 }); | 161 }); |
| 200 | 162 |
| 201 // We cannot use {writable: true} property descriptor because we want a | 163 // We cannot use {writable: true} property descriptor because we want a |
| 202 // dynamic getter value. | 164 // dynamic getter value. |
| 203 Object.defineProperty(this.webviewNode, 'contentWindow', { | 165 Object.defineProperty(this.webviewNode, 'contentWindow', { |
| 204 get: function() { | 166 get: function() { |
| 205 if (this.contentWindow) { | 167 if (this.contentWindow) { |
| 206 return this.contentWindow; | 168 return this.contentWindow; |
| 207 } | 169 } |
| 208 window.console.error( | 170 window.console.error( |
| 209 WebViewConstants.ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE); | 171 WebViewConstants.ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE); |
| 210 }.bind(this), | 172 }.bind(this), |
| 211 // No setter. | 173 // No setter. |
| 212 enumerable: true | 174 enumerable: true |
| 213 }); | 175 }); |
| 214 | 176 |
| 215 Object.defineProperty(this.webviewNode, 'name', { | 177 Object.defineProperty(this.webviewNode, WebViewConstants.ATTRIBUTE_NAME, { |
| 216 get: function() { | 178 get: function() { |
| 217 return this.name; | 179 return this.attributes[WebViewConstants.ATTRIBUTE_NAME].getValue(); |
| 218 }.bind(this), | 180 }.bind(this), |
| 219 set: function(value) { | 181 set: function(value) { |
| 220 this.webviewNode.setAttribute('name', value); | 182 this.webviewNode.setAttribute(WebViewConstants.ATTRIBUTE_NAME, value); |
| 221 }.bind(this), | 183 }.bind(this), |
| 222 enumerable: true | 184 enumerable: true |
| 223 }); | 185 }); |
| 224 | 186 |
| 225 Object.defineProperty(this.webviewNode, 'partition', { | 187 Object.defineProperty(this.webviewNode, |
| 188 WebViewConstants.ATTRIBUTE_PARTITION, { |
| 226 get: function() { | 189 get: function() { |
| 227 return this.partition.toAttribute(); | 190 return this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].getValue(); |
| 228 }.bind(this), | 191 }.bind(this), |
| 229 set: function(value) { | 192 set: function(value) { |
| 230 var result = this.partition.fromAttribute(value, this.hasNavigated()); | 193 var result = this.attributes[WebViewConstants.ATTRIBUTE_PARTITION]. |
| 194 setValue(value); |
| 231 if (result.error) { | 195 if (result.error) { |
| 232 throw result.error; | 196 throw result.error; |
| 233 } | 197 } |
| 234 this.webviewNode.setAttribute('partition', value); | 198 this.webviewNode.setAttribute(WebViewConstants.ATTRIBUTE_PARTITION, |
| 199 value); |
| 235 }.bind(this), | 200 }.bind(this), |
| 236 enumerable: true | 201 enumerable: true |
| 237 }); | 202 }); |
| 238 | 203 |
| 239 this.src = this.webviewNode.getAttribute('src'); | 204 this.attributes[WebViewConstants.ATTRIBUTE_SRC].setValue( |
| 240 Object.defineProperty(this.webviewNode, 'src', { | 205 this.webviewNode.getAttribute(WebViewConstants.ATTRIBUTE_SRC)); |
| 206 Object.defineProperty(this.webviewNode, WebViewConstants.ATTRIBUTE_SRC, { |
| 241 get: function() { | 207 get: function() { |
| 242 return this.src; | 208 return this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(); |
| 243 }.bind(this), | 209 }.bind(this), |
| 244 set: function(value) { | 210 set: function(value) { |
| 245 this.webviewNode.setAttribute('src', value); | 211 this.webviewNode.setAttribute(WebViewConstants.ATTRIBUTE_SRC, value); |
| 246 }.bind(this), | 212 }.bind(this), |
| 247 // No setter. | |
| 248 enumerable: true | 213 enumerable: true |
| 249 }); | 214 }); |
| 250 }; | 215 }; |
| 251 | 216 |
| 252 // The purpose of this mutation observer is to catch assignment to the src | 217 // The purpose of this mutation observer is to catch assignment to the src |
| 253 // attribute without any changes to its value. This is useful in the case | 218 // attribute without any changes to its value. This is useful in the case |
| 254 // where the webview guest has crashed and navigating to the same address | 219 // where the webview guest has crashed and navigating to the same address |
| 255 // spawns off a new process. | 220 // spawns off a new process. |
| 256 WebView.prototype.setupWebViewSrcAttributeMutationObserver = | 221 WebView.prototype.setupWebViewSrcAttributeMutationObserver = |
| 257 function() { | 222 function() { |
| 258 this.srcAndPartitionObserver = new MutationObserver(function(mutations) { | 223 this.srcAndPartitionObserver = new MutationObserver(function(mutations) { |
| 259 $Array.forEach(mutations, function(mutation) { | 224 $Array.forEach(mutations, function(mutation) { |
| 260 var oldValue = mutation.oldValue; | 225 var oldValue = mutation.oldValue; |
| 261 var newValue = this.webviewNode.getAttribute(mutation.attributeName); | 226 var newValue = this.webviewNode.getAttribute(mutation.attributeName); |
| 262 if (oldValue != newValue) { | 227 if (oldValue != newValue) { |
| 263 return; | 228 return; |
| 264 } | 229 } |
| 265 this.handleWebviewAttributeMutation( | 230 this.handleWebviewAttributeMutation( |
| 266 mutation.attributeName, oldValue, newValue); | 231 mutation.attributeName, oldValue, newValue); |
| 267 }.bind(this)); | 232 }.bind(this)); |
| 268 }.bind(this)); | 233 }.bind(this)); |
| 269 var params = { | 234 var params = { |
| 270 attributes: true, | 235 attributes: true, |
| 271 attributeOldValue: true, | 236 attributeOldValue: true, |
| 272 attributeFilter: ['src', 'partition'] | 237 attributeFilter: [WebViewConstants.ATTRIBUTE_SRC, |
| 238 WebViewConstants.ATTRIBUTE_PARTITION] |
| 273 }; | 239 }; |
| 274 this.srcAndPartitionObserver.observe(this.webviewNode, params); | 240 this.srcAndPartitionObserver.observe(this.webviewNode, params); |
| 275 }; | 241 }; |
| 276 | 242 |
| 277 // This observer monitors mutations to attributes of the <webview> and | 243 // This observer monitors mutations to attributes of the <webview> and |
| 278 // updates the BrowserPlugin properties accordingly. In turn, updating | 244 // updates the BrowserPlugin properties accordingly. In turn, updating |
| 279 // a BrowserPlugin property will update the corresponding BrowserPlugin | 245 // a BrowserPlugin property will update the corresponding BrowserPlugin |
| 280 // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more | 246 // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more |
| 281 // details. | 247 // details. |
| 282 WebView.prototype.handleWebviewAttributeMutation = | 248 WebView.prototype.handleWebviewAttributeMutation = |
| 283 function(name, oldValue, newValue) { | 249 function(name, oldValue, newValue) { |
| 284 if (AUTO_SIZE_ATTRIBUTES.indexOf(name) > -1) { | 250 if (AUTO_SIZE_ATTRIBUTES.indexOf(name) > -1) { |
| 285 this[name] = newValue; | 251 this.attributes[name].setValue(newValue); |
| 286 if (!this.guestInstanceId) { | 252 if (!this.guestInstanceId) { |
| 287 return; | 253 return; |
| 288 } | 254 } |
| 289 // Convert autosize attribute to boolean. | 255 // Convert autosize attribute to boolean. |
| 290 var autosize = this.webviewNode.hasAttribute( | 256 var autosize = this.webviewNode.hasAttribute( |
| 291 WebViewConstants.ATTRIBUTE_AUTOSIZE); | 257 WebViewConstants.ATTRIBUTE_AUTOSIZE); |
| 292 GuestViewInternal.setAutoSize(this.guestInstanceId, { | 258 GuestViewInternal.setAutoSize(this.guestInstanceId, { |
| 293 'enableAutoSize': autosize, | 259 'enableAutoSize': autosize, |
| 294 'min': { | 260 'min': { |
| 295 'width': parseInt(this.minwidth || 0), | 261 'width': parseInt(this. |
| 296 'height': parseInt(this.minheight || 0) | 262 attributes[WebViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0), |
| 263 'height': parseInt(this. |
| 264 attributes[WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0) |
| 297 }, | 265 }, |
| 298 'max': { | 266 'max': { |
| 299 'width': parseInt(this.maxwidth || 0), | 267 'width': parseInt(this. |
| 300 'height': parseInt(this.maxheight || 0) | 268 attributes[WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0), |
| 269 'height': parseInt(this. |
| 270 attributes[WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0) |
| 301 } | 271 } |
| 302 }); | 272 }); |
| 303 return; | 273 return; |
| 304 } else if (name == WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY) { | 274 } else if (name == WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY) { |
| 305 // We treat null attribute (attribute removed) and the empty string as | 275 // We treat null attribute (attribute removed) and the empty string as |
| 306 // one case. | 276 // one case. |
| 307 oldValue = oldValue || ''; | 277 oldValue = oldValue || ''; |
| 308 newValue = newValue || ''; | 278 newValue = newValue || ''; |
| 309 | 279 |
| 310 if (oldValue === newValue) { | 280 if (oldValue === newValue) { |
| 311 return; | 281 return; |
| 312 } | 282 } |
| 313 this.allowtransparency = newValue != ''; | 283 this.attributes[WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY]. |
| 284 setValue(newValue != ''); |
| 314 | 285 |
| 315 if (!this.guestInstanceId) { | 286 if (!this.guestInstanceId) { |
| 316 return; | 287 return; |
| 317 } | 288 } |
| 318 | 289 |
| 319 WebViewInternal.setAllowTransparency(this.guestInstanceId, | 290 WebViewInternal.setAllowTransparency( |
| 320 this.allowtransparency); | 291 this.guestInstanceId, |
| 292 this.attributes[WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY]. |
| 293 getValue()); |
| 321 return; | 294 return; |
| 322 } else if (name == 'name') { | 295 } else if (name == WebViewConstants.ATTRIBUTE_NAME) { |
| 323 // We treat null attribute (attribute removed) and the empty string as | 296 // We treat null attribute (attribute removed) and the empty string as |
| 324 // one case. | 297 // one case. |
| 325 oldValue = oldValue || ''; | 298 oldValue = oldValue || ''; |
| 326 newValue = newValue || ''; | 299 newValue = newValue || ''; |
| 327 | 300 |
| 328 if (oldValue === newValue) { | 301 if (oldValue === newValue) { |
| 329 return; | 302 return; |
| 330 } | 303 } |
| 331 this.name = newValue; | 304 this.attributes[WebViewConstants.ATTRIBUTE_NAME].setValue(newValue); |
| 332 if (!this.guestInstanceId) { | 305 if (!this.guestInstanceId) { |
| 333 return; | 306 return; |
| 334 } | 307 } |
| 335 WebViewInternal.setName(this.guestInstanceId, newValue); | 308 WebViewInternal.setName(this.guestInstanceId, newValue); |
| 336 return; | 309 return; |
| 337 } else if (name == 'src') { | 310 } else if (name == WebViewConstants.ATTRIBUTE_SRC) { |
| 338 // We treat null attribute (attribute removed) and the empty string as | 311 // We treat null attribute (attribute removed) and the empty string as |
| 339 // one case. | 312 // one case. |
| 340 oldValue = oldValue || ''; | 313 oldValue = oldValue || ''; |
| 341 newValue = newValue || ''; | 314 newValue = newValue || ''; |
| 342 // Once we have navigated, we don't allow clearing the src attribute. | 315 // Once we have navigated, we don't allow clearing the src attribute. |
| 343 // Once <webview> enters a navigated state, it cannot be return back to a | 316 // Once <webview> enters a navigated state, it cannot be return back to a |
| 344 // placeholder state. | 317 // placeholder state. |
| 345 if (newValue == '' && oldValue != '') { | 318 if (newValue == '' && oldValue != '') { |
| 346 // src attribute changes normally initiate a navigation. We suppress | 319 // src attribute changes normally initiate a navigation. We suppress |
| 347 // the next src attribute handler call to avoid reloading the page | 320 // the next src attribute handler call to avoid reloading the page |
| 348 // on every guest-initiated navigation. | 321 // on every guest-initiated navigation. |
| 349 this.ignoreNextSrcAttributeChange = true; | 322 this.ignoreNextSrcAttributeChange = true; |
| 350 this.webviewNode.setAttribute('src', oldValue); | 323 this.webviewNode.setAttribute(WebViewConstants.ATTRIBUTE_SRC, oldValue); |
| 351 return; | 324 return; |
| 352 } | 325 } |
| 353 this.src = newValue; | 326 this.attributes[WebViewConstants.ATTRIBUTE_SRC].setValue(newValue); |
| 354 if (this.ignoreNextSrcAttributeChange) { | 327 if (this.ignoreNextSrcAttributeChange) { |
| 355 // Don't allow the src mutation observer to see this change. | 328 // Don't allow the src mutation observer to see this change. |
| 356 this.srcAndPartitionObserver.takeRecords(); | 329 this.srcAndPartitionObserver.takeRecords(); |
| 357 this.ignoreNextSrcAttributeChange = false; | 330 this.ignoreNextSrcAttributeChange = false; |
| 358 return; | 331 return; |
| 359 } | 332 } |
| 360 var result = {}; | 333 var result = {}; |
| 361 this.parseSrcAttribute(result); | 334 this.parseSrcAttribute(result); |
| 362 | 335 |
| 363 if (result.error) { | 336 if (result.error) { |
| 364 throw result.error; | 337 throw result.error; |
| 365 } | 338 } |
| 366 } else if (name == 'partition') { | 339 } else if (name == WebViewConstants.ATTRIBUTE_PARTITION) { |
| 367 // Note that throwing error here won't synchronously propagate. | 340 // Note that throwing error here won't synchronously propagate. |
| 368 this.partition.fromAttribute(newValue, this.hasNavigated()); | 341 this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].setValue(newValue); |
| 369 } | 342 } |
| 370 }; | 343 }; |
| 371 | 344 |
| 372 WebView.prototype.handleBrowserPluginAttributeMutation = | 345 WebView.prototype.handleBrowserPluginAttributeMutation = |
| 373 function(name, oldValue, newValue) { | 346 function(name, oldValue, newValue) { |
| 374 if (name == 'internalinstanceid' && !oldValue && !!newValue) { | 347 if (name == WebViewConstants.ATTRIBUTE_INTERNALINSTANCEID && |
| 375 this.browserPluginNode.removeAttribute('internalinstanceid'); | 348 !oldValue && !!newValue) { |
| 349 this.browserPluginNode.removeAttribute( |
| 350 WebViewConstants.ATTRIBUTE_INTERNALINSTANCEID); |
| 376 this.internalInstanceId = parseInt(newValue); | 351 this.internalInstanceId = parseInt(newValue); |
| 377 | 352 |
| 378 if (!!this.guestInstanceId && this.guestInstanceId != 0) { | 353 if (!!this.guestInstanceId && this.guestInstanceId != 0) { |
| 379 var isNewWindow = this.deferredAttachState ? | 354 var isNewWindow = this.deferredAttachState ? |
| 380 this.deferredAttachState.isNewWindow : false; | 355 this.deferredAttachState.isNewWindow : false; |
| 381 var params = this.buildAttachParams(isNewWindow); | 356 var params = this.buildAttachParams(isNewWindow); |
| 382 guestViewInternalNatives.AttachGuest( | 357 guestViewInternalNatives.AttachGuest( |
| 383 this.internalInstanceId, | 358 this.internalInstanceId, |
| 384 this.guestInstanceId, | 359 this.guestInstanceId, |
| 385 params, | 360 params, |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 458 // Returns if <object> is in the render tree. | 433 // Returns if <object> is in the render tree. |
| 459 WebView.prototype.isPluginInRenderTree = function() { | 434 WebView.prototype.isPluginInRenderTree = function() { |
| 460 return !!this.internalInstanceId && this.internalInstanceId != 0; | 435 return !!this.internalInstanceId && this.internalInstanceId != 0; |
| 461 }; | 436 }; |
| 462 | 437 |
| 463 WebView.prototype.hasNavigated = function() { | 438 WebView.prototype.hasNavigated = function() { |
| 464 return !this.beforeFirstNavigation; | 439 return !this.beforeFirstNavigation; |
| 465 }; | 440 }; |
| 466 | 441 |
| 467 WebView.prototype.parseSrcAttribute = function(result) { | 442 WebView.prototype.parseSrcAttribute = function(result) { |
| 468 if (!this.partition.validPartitionId) { | 443 if (!this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].validPartitionId) { |
| 469 result.error = WebViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; | 444 result.error = WebViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; |
| 470 return; | 445 return; |
| 471 } | 446 } |
| 472 this.src = this.webviewNode.getAttribute('src'); | 447 this.attributes[WebViewConstants.ATTRIBUTE_SRC].setValue( |
| 448 this.webviewNode.getAttribute(WebViewConstants.ATTRIBUTE_SRC)); |
| 473 | 449 |
| 474 if (!this.src) { | 450 if (!this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue()) { |
| 475 return; | 451 return; |
| 476 } | 452 } |
| 477 | 453 |
| 478 if (this.guestInstanceId == undefined) { | 454 if (this.guestInstanceId == undefined) { |
| 479 if (this.beforeFirstNavigation) { | 455 if (this.beforeFirstNavigation) { |
| 480 this.beforeFirstNavigation = false; | 456 this.beforeFirstNavigation = false; |
| 481 this.createGuest(); | 457 this.createGuest(); |
| 482 } | 458 } |
| 483 return; | 459 return; |
| 484 } | 460 } |
| 485 | 461 |
| 486 // Navigate to |this.src|. | 462 // Navigate to |this.src|. |
| 487 WebViewInternal.navigate(this.guestInstanceId, this.src); | 463 WebViewInternal.navigate( |
| 464 this.guestInstanceId, |
| 465 this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue()); |
| 488 }; | 466 }; |
| 489 | 467 |
| 490 WebView.prototype.parseAttributes = function() { | 468 WebView.prototype.parseAttributes = function() { |
| 491 if (!this.elementAttached) { | 469 if (!this.elementAttached) { |
| 492 return; | 470 return; |
| 493 } | 471 } |
| 494 var hasNavigated = this.hasNavigated(); | 472 var attributeValue = this.webviewNode.getAttribute( |
| 495 var attributeValue = this.webviewNode.getAttribute('partition'); | 473 WebViewConstants.ATTRIBUTE_PARTITION); |
| 496 var result = this.partition.fromAttribute(attributeValue, hasNavigated); | 474 var result = this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].setValue( |
| 475 attributeValue); |
| 497 this.parseSrcAttribute(result); | 476 this.parseSrcAttribute(result); |
| 498 }; | 477 }; |
| 499 | 478 |
| 500 WebView.prototype.createGuest = function() { | 479 WebView.prototype.createGuest = function() { |
| 501 if (this.pendingGuestCreation) { | 480 if (this.pendingGuestCreation) { |
| 502 return; | 481 return; |
| 503 } | 482 } |
| 504 var storagePartitionId = | 483 var storagePartitionId = |
| 505 this.webviewNode.getAttribute(WebViewConstants.ATTRIBUTE_PARTITION) || | 484 this.webviewNode.getAttribute(WebViewConstants.ATTRIBUTE_PARTITION) || |
| 506 this.webviewNode[WebViewConstants.ATTRIBUTE_PARTITION]; | 485 this.webviewNode[WebViewConstants.ATTRIBUTE_PARTITION]; |
| 507 var params = { | 486 var params = { |
| 508 'storagePartitionId': storagePartitionId | 487 'storagePartitionId': storagePartitionId |
| 509 }; | 488 }; |
| 510 GuestViewInternal.createGuest( | 489 GuestViewInternal.createGuest( |
| 511 'webview', | 490 'webview', |
| 512 params, | 491 params, |
| 513 function(guestInstanceId) { | 492 function(guestInstanceId) { |
| 514 this.pendingGuestCreation = false; | 493 this.pendingGuestCreation = false; |
| 515 if (!this.elementAttached) { | 494 if (!this.elementAttached) { |
| 516 GuestViewInternal.destroyGuest(guestInstanceId); | 495 GuestViewInternal.destroyGuest(guestInstanceId); |
| 517 return; | 496 return; |
| 518 } | 497 } |
| 519 this.attachWindow(guestInstanceId, false); | 498 this.attachWindow(guestInstanceId, false); |
| 520 }.bind(this) | 499 }.bind(this) |
| 521 ); | 500 ); |
| 522 this.pendingGuestCreation = true; | 501 this.pendingGuestCreation = true; |
| 523 }; | 502 }; |
| 524 | 503 |
| 525 WebView.prototype.onFrameNameChanged = function(name) { | 504 WebView.prototype.onFrameNameChanged = function(name) { |
| 526 this.name = name || ''; | 505 this.attributes[WebViewConstants.ATTRIBUTE_NAME].setValue(name || ''); |
| 527 if (this.name === '') { | 506 if (this.attributes[WebViewConstants.ATTRIBUTE_NAME].getValue() === '') { |
| 528 this.webviewNode.removeAttribute('name'); | 507 this.webviewNode.removeAttribute(WebViewConstants.ATTRIBUTE_NAME); |
| 529 } else { | 508 } else { |
| 530 this.webviewNode.setAttribute('name', this.name); | 509 this.webviewNode.setAttribute( |
| 510 WebViewConstants.ATTRIBUTE_NAME, |
| 511 this.attributes[WebViewConstants.ATTRIBUTE_NAME].getValue()); |
| 531 } | 512 } |
| 532 }; | 513 }; |
| 533 | 514 |
| 534 WebView.prototype.dispatchEvent = function(webViewEvent) { | 515 WebView.prototype.dispatchEvent = function(webViewEvent) { |
| 535 return this.webviewNode.dispatchEvent(webViewEvent); | 516 return this.webviewNode.dispatchEvent(webViewEvent); |
| 536 }; | 517 }; |
| 537 | 518 |
| 538 // Adds an 'on<event>' property on the webview, which can be used to set/unset | 519 // Adds an 'on<event>' property on the webview, which can be used to set/unset |
| 539 // an event handler. | 520 // an event handler. |
| 540 WebView.prototype.setupEventProperty = function(eventName) { | 521 WebView.prototype.setupEventProperty = function(eventName) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 555 }; | 536 }; |
| 556 | 537 |
| 557 // Updates state upon loadcommit. | 538 // Updates state upon loadcommit. |
| 558 WebView.prototype.onLoadCommit = function( | 539 WebView.prototype.onLoadCommit = function( |
| 559 baseUrlForDataUrl, currentEntryIndex, entryCount, | 540 baseUrlForDataUrl, currentEntryIndex, entryCount, |
| 560 processId, url, isTopLevel) { | 541 processId, url, isTopLevel) { |
| 561 this.baseUrlForDataUrl = baseUrlForDataUrl; | 542 this.baseUrlForDataUrl = baseUrlForDataUrl; |
| 562 this.currentEntryIndex = currentEntryIndex; | 543 this.currentEntryIndex = currentEntryIndex; |
| 563 this.entryCount = entryCount; | 544 this.entryCount = entryCount; |
| 564 this.processId = processId; | 545 this.processId = processId; |
| 565 var oldValue = this.webviewNode.getAttribute('src'); | 546 var oldValue = this.webviewNode.getAttribute(WebViewConstants.ATTRIBUTE_SRC); |
| 566 var newValue = url; | 547 var newValue = url; |
| 567 if (isTopLevel && (oldValue != newValue)) { | 548 if (isTopLevel && (oldValue != newValue)) { |
| 568 // Touching the src attribute triggers a navigation. To avoid | 549 // Touching the src attribute triggers a navigation. To avoid |
| 569 // triggering a page reload on every guest-initiated navigation, | 550 // triggering a page reload on every guest-initiated navigation, |
| 570 // we use the flag ignoreNextSrcAttributeChange here. | 551 // we use the flag ignoreNextSrcAttributeChange here. |
| 571 this.ignoreNextSrcAttributeChange = true; | 552 this.ignoreNextSrcAttributeChange = true; |
| 572 this.webviewNode.setAttribute('src', newValue); | 553 this.webviewNode.setAttribute(WebViewConstants.ATTRIBUTE_SRC, newValue); |
| 573 } | 554 } |
| 574 }; | 555 }; |
| 575 | 556 |
| 576 WebView.prototype.onAttach = function(storagePartitionId) { | 557 WebView.prototype.onAttach = function(storagePartitionId) { |
| 577 this.webviewNode.setAttribute('partition', storagePartitionId); | 558 this.webviewNode.setAttribute(WebViewConstants.ATTRIBUTE_PARTITION, |
| 578 this.partition.fromAttribute(storagePartitionId, this.hasNavigated()); | 559 storagePartitionId); |
| 560 this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].setValue( |
| 561 storagePartitionId); |
| 579 }; | 562 }; |
| 580 | 563 |
| 581 WebView.prototype.buildAttachParams = function(isNewWindow) { | 564 WebView.prototype.buildAttachParams = function(isNewWindow) { |
| 582 var params = { | 565 var params = { |
| 583 'allowtransparency': this.allowtransparency || false, | 566 'allowtransparency': this.attributes[ |
| 567 WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY].getValue() || false, |
| 584 'autosize': this.webviewNode.hasAttribute( | 568 'autosize': this.webviewNode.hasAttribute( |
| 585 WebViewConstants.ATTRIBUTE_AUTOSIZE), | 569 WebViewConstants.ATTRIBUTE_AUTOSIZE), |
| 586 'instanceId': this.viewInstanceId, | 570 'instanceId': this.viewInstanceId, |
| 587 'maxheight': parseInt(this.maxheight || 0), | 571 'maxheight': parseInt(this.attributes[WebViewConstants.ATTRIBUTE_MAXHEIGHT]. |
| 588 'maxwidth': parseInt(this.maxwidth || 0), | 572 getValue() || 0), |
| 589 'minheight': parseInt(this.minheight || 0), | 573 'maxwidth': parseInt(this.attributes[WebViewConstants.ATTRIBUTE_MAXWIDTH]. |
| 590 'minwidth': parseInt(this.minwidth || 0), | 574 getValue() || 0), |
| 591 'name': this.name, | 575 'minheight': parseInt(this.attributes[WebViewConstants.ATTRIBUTE_MINHEIGHT]. |
| 576 getValue() || 0), |
| 577 'minwidth': parseInt(this.attributes[WebViewConstants.ATTRIBUTE_MINWIDTH]. |
| 578 getValue() || 0), |
| 579 'name': this.attributes[WebViewConstants.ATTRIBUTE_NAME].getValue(), |
| 592 // We don't need to navigate new window from here. | 580 // We don't need to navigate new window from here. |
| 593 'src': isNewWindow ? undefined : this.src, | 581 'src': isNewWindow ? undefined : |
| 582 this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(), |
| 594 // If we have a partition from the opener, that will also be already | 583 // If we have a partition from the opener, that will also be already |
| 595 // set via this.onAttach(). | 584 // set via this.onAttach(). |
| 596 'storagePartitionId': this.partition.toAttribute(), | 585 'storagePartitionId': this.attributes[WebViewConstants.ATTRIBUTE_PARTITION]. |
| 586 getValue(), |
| 597 'userAgentOverride': this.userAgentOverride | 587 'userAgentOverride': this.userAgentOverride |
| 598 }; | 588 }; |
| 599 return params; | 589 return params; |
| 600 }; | 590 }; |
| 601 | 591 |
| 602 WebView.prototype.attachWindow = function(guestInstanceId, | 592 WebView.prototype.attachWindow = function(guestInstanceId, |
| 603 isNewWindow) { | 593 isNewWindow) { |
| 604 this.guestInstanceId = guestInstanceId; | 594 this.guestInstanceId = guestInstanceId; |
| 605 var params = this.buildAttachParams(isNewWindow); | 595 var params = this.buildAttachParams(isNewWindow); |
| 606 | 596 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 644 if (!this.guestInstanceId) { | 634 if (!this.guestInstanceId) { |
| 645 return; | 635 return; |
| 646 } | 636 } |
| 647 var args = $Array.concat([this.guestInstanceId], $Array.slice(arguments)); | 637 var args = $Array.concat([this.guestInstanceId], $Array.slice(arguments)); |
| 648 $Function.apply(WebViewInternal.clearData, null, args); | 638 $Function.apply(WebViewInternal.clearData, null, args); |
| 649 }; | 639 }; |
| 650 | 640 |
| 651 // Injects JavaScript code into the guest page. | 641 // Injects JavaScript code into the guest page. |
| 652 WebView.prototype.executeScript = function(var_args) { | 642 WebView.prototype.executeScript = function(var_args) { |
| 653 this.validateExecuteCodeCall(); | 643 this.validateExecuteCodeCall(); |
| 654 var webviewSrc = this.src; | 644 var webviewSrc = this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(); |
| 655 if (this.baseUrlForDataUrl != '') { | 645 if (this.baseUrlForDataUrl != '') { |
| 656 webviewSrc = this.baseUrlForDataUrl; | 646 webviewSrc = this.baseUrlForDataUrl; |
| 657 } | 647 } |
| 658 var args = $Array.concat([this.guestInstanceId, webviewSrc], | 648 var args = $Array.concat([this.guestInstanceId, webviewSrc], |
| 659 $Array.slice(arguments)); | 649 $Array.slice(arguments)); |
| 660 $Function.apply(WebViewInternal.executeScript, null, args); | 650 $Function.apply(WebViewInternal.executeScript, null, args); |
| 661 }; | 651 }; |
| 662 | 652 |
| 663 // Initiates a find-in-page request. | 653 // Initiates a find-in-page request. |
| 664 WebView.prototype.find = function(search_text, options, callback) { | 654 WebView.prototype.find = function(search_text, options, callback) { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 697 WebView.prototype.go = function(relativeIndex, callback) { | 687 WebView.prototype.go = function(relativeIndex, callback) { |
| 698 if (!this.guestInstanceId) { | 688 if (!this.guestInstanceId) { |
| 699 return; | 689 return; |
| 700 } | 690 } |
| 701 WebViewInternal.go(this.guestInstanceId, relativeIndex, callback); | 691 WebViewInternal.go(this.guestInstanceId, relativeIndex, callback); |
| 702 }; | 692 }; |
| 703 | 693 |
| 704 // Injects CSS into the guest page. | 694 // Injects CSS into the guest page. |
| 705 WebView.prototype.insertCSS = function(var_args) { | 695 WebView.prototype.insertCSS = function(var_args) { |
| 706 this.validateExecuteCodeCall(); | 696 this.validateExecuteCodeCall(); |
| 707 var webviewSrc = this.src; | 697 var webviewSrc = this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(); |
| 708 if (this.baseUrlForDataUrl != '') { | 698 if (this.baseUrlForDataUrl != '') { |
| 709 webviewSrc = this.baseUrlForDataUrl; | 699 webviewSrc = this.baseUrlForDataUrl; |
| 710 } | 700 } |
| 711 var args = $Array.concat([this.guestInstanceId, webviewSrc], | 701 var args = $Array.concat([this.guestInstanceId, webviewSrc], |
| 712 $Array.slice(arguments)); | 702 $Array.slice(arguments)); |
| 713 $Function.apply(WebViewInternal.insertCSS, null, args); | 703 $Function.apply(WebViewInternal.insertCSS, null, args); |
| 714 }; | 704 }; |
| 715 | 705 |
| 716 // Indicates whether or not the webview's user agent string has been overridden. | 706 // Indicates whether or not the webview's user agent string has been overridden. |
| 717 WebView.prototype.isUserAgentOverridden = function() { | 707 WebView.prototype.isUserAgentOverridden = function() { |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 913 WebView.prototype.maybeGetChromeWebViewEvents = function() {}; | 903 WebView.prototype.maybeGetChromeWebViewEvents = function() {}; |
| 914 | 904 |
| 915 // Implemented when the experimental WebView API is available. | 905 // Implemented when the experimental WebView API is available. |
| 916 WebView.maybeGetExperimentalAPIs = function() {}; | 906 WebView.maybeGetExperimentalAPIs = function() {}; |
| 917 WebView.prototype.maybeGetExperimentalEvents = function() {}; | 907 WebView.prototype.maybeGetExperimentalEvents = function() {}; |
| 918 WebView.prototype.setupExperimentalContextMenus = function() {}; | 908 WebView.prototype.setupExperimentalContextMenus = function() {}; |
| 919 | 909 |
| 920 // Exports. | 910 // Exports. |
| 921 exports.WebView = WebView; | 911 exports.WebView = WebView; |
| 922 exports.WebViewInternal = WebViewInternal; | 912 exports.WebViewInternal = WebViewInternal; |
| OLD | NEW |