| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 the attributes of the <webview> tag. | |
| 6 | |
| 7 var GuestViewInternal = | |
| 8 require('binding').Binding.create('guestViewInternal').generate(); | |
| 9 var WebViewImpl = require('webView').WebViewImpl; | |
| 10 var WebViewConstants = require('webViewConstants').WebViewConstants; | |
| 11 var WebViewInternal = require('webViewInternal').WebViewInternal; | |
| 12 | |
| 13 // ----------------------------------------------------------------------------- | |
| 14 // Attribute objects. | |
| 15 | |
| 16 // Default implementation of a WebView attribute. | |
| 17 function WebViewAttribute(name, webViewImpl) { | |
| 18 this.name = name; | |
| 19 this.webViewImpl = webViewImpl; | |
| 20 this.ignoreMutation = false; | |
| 21 | |
| 22 this.defineProperty(); | |
| 23 } | |
| 24 | |
| 25 // Retrieves and returns the attribute's value. | |
| 26 WebViewAttribute.prototype.getValue = function() { | |
| 27 return this.webViewImpl.element.getAttribute(this.name) || ''; | |
| 28 }; | |
| 29 | |
| 30 // Sets the attribute's value. | |
| 31 WebViewAttribute.prototype.setValue = function(value) { | |
| 32 this.webViewImpl.element.setAttribute(this.name, value || ''); | |
| 33 }; | |
| 34 | |
| 35 // Changes the attribute's value without triggering its mutation handler. | |
| 36 WebViewAttribute.prototype.setValueIgnoreMutation = function(value) { | |
| 37 this.ignoreMutation = true; | |
| 38 this.setValue(value); | |
| 39 this.ignoreMutation = false; | |
| 40 } | |
| 41 | |
| 42 // Defines this attribute as a property on the webview node. | |
| 43 WebViewAttribute.prototype.defineProperty = function() { | |
| 44 Object.defineProperty(this.webViewImpl.element, this.name, { | |
| 45 get: function() { | |
| 46 return this.getValue(); | |
| 47 }.bind(this), | |
| 48 set: function(value) { | |
| 49 this.setValue(value); | |
| 50 }.bind(this), | |
| 51 enumerable: true | |
| 52 }); | |
| 53 }; | |
| 54 | |
| 55 // Called when the attribute's value changes. | |
| 56 WebViewAttribute.prototype.maybeHandleMutation = function(oldValue, newValue) { | |
| 57 if (this.ignoreMutation) { | |
| 58 return; | |
| 59 } | |
| 60 | |
| 61 this.handleMutation(oldValue, newValue); | |
| 62 }; | |
| 63 | |
| 64 // Called when a change that isn't ignored occurs to the attribute's value. | |
| 65 WebViewAttribute.prototype.handleMutation = function(oldValue, newValue) {}; | |
| 66 | |
| 67 // Called when the <webview> element is attached to the DOM tree. | |
| 68 WebViewAttribute.prototype.attach = function() {}; | |
| 69 | |
| 70 // Called when the <webview> element is detached from the DOM tree. | |
| 71 WebViewAttribute.prototype.detach = function() {}; | |
| 72 | |
| 73 // An attribute that is treated as a Boolean. | |
| 74 function BooleanAttribute(name, webViewImpl) { | |
| 75 WebViewAttribute.call(this, name, webViewImpl); | |
| 76 } | |
| 77 | |
| 78 BooleanAttribute.prototype.__proto__ = WebViewAttribute.prototype; | |
| 79 | |
| 80 BooleanAttribute.prototype.getValue = function() { | |
| 81 return this.webViewImpl.element.hasAttribute(this.name); | |
| 82 }; | |
| 83 | |
| 84 BooleanAttribute.prototype.setValue = function(value) { | |
| 85 if (!value) { | |
| 86 this.webViewImpl.element.removeAttribute(this.name); | |
| 87 } else { | |
| 88 this.webViewImpl.element.setAttribute(this.name, ''); | |
| 89 } | |
| 90 }; | |
| 91 | |
| 92 // Attribute that specifies whether transparency is allowed in the webview. | |
| 93 function AllowTransparencyAttribute(webViewImpl) { | |
| 94 BooleanAttribute.call( | |
| 95 this, WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY, webViewImpl); | |
| 96 } | |
| 97 | |
| 98 AllowTransparencyAttribute.prototype.__proto__ = BooleanAttribute.prototype; | |
| 99 | |
| 100 AllowTransparencyAttribute.prototype.handleMutation = function(oldValue, | |
| 101 newValue) { | |
| 102 if (!this.webViewImpl.guest.getId()) { | |
| 103 return; | |
| 104 } | |
| 105 | |
| 106 WebViewInternal.setAllowTransparency(this.webViewImpl.guest.getId(), | |
| 107 this.getValue()); | |
| 108 }; | |
| 109 | |
| 110 // Attribute that specifies whether transparency is allowed in the webview. | |
| 111 function AllowScalingAttribute(webViewImpl) { | |
| 112 BooleanAttribute.call( | |
| 113 this, WebViewConstants.ATTRIBUTE_ALLOWSCALING, webViewImpl); | |
| 114 } | |
| 115 | |
| 116 AllowScalingAttribute.prototype.__proto__ = BooleanAttribute.prototype; | |
| 117 | |
| 118 AllowScalingAttribute.prototype.handleMutation = function(oldValue, | |
| 119 newValue) { | |
| 120 if (!this.webViewImpl.guest.getId()) { | |
| 121 return; | |
| 122 } | |
| 123 | |
| 124 WebViewInternal.setAllowScaling(this.webViewImpl.guest.getId(), | |
| 125 this.getValue()); | |
| 126 }; | |
| 127 | |
| 128 // Attribute used to define the demension limits of autosizing. | |
| 129 function AutosizeDimensionAttribute(name, webViewImpl) { | |
| 130 WebViewAttribute.call(this, name, webViewImpl); | |
| 131 } | |
| 132 | |
| 133 AutosizeDimensionAttribute.prototype.__proto__ = WebViewAttribute.prototype; | |
| 134 | |
| 135 AutosizeDimensionAttribute.prototype.getValue = function() { | |
| 136 return parseInt(this.webViewImpl.element.getAttribute(this.name)) || 0; | |
| 137 }; | |
| 138 | |
| 139 AutosizeDimensionAttribute.prototype.handleMutation = function( | |
| 140 oldValue, newValue) { | |
| 141 if (!this.webViewImpl.guest.getId()) { | |
| 142 return; | |
| 143 } | |
| 144 this.webViewImpl.guest.setSize({ | |
| 145 'enableAutoSize': this.webViewImpl.attributes[ | |
| 146 WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue(), | |
| 147 'min': { | |
| 148 'width': this.webViewImpl.attributes[ | |
| 149 WebViewConstants.ATTRIBUTE_MINWIDTH].getValue(), | |
| 150 'height': this.webViewImpl.attributes[ | |
| 151 WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue() | |
| 152 }, | |
| 153 'max': { | |
| 154 'width': this.webViewImpl.attributes[ | |
| 155 WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue(), | |
| 156 'height': this.webViewImpl.attributes[ | |
| 157 WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() | |
| 158 } | |
| 159 }); | |
| 160 return; | |
| 161 }; | |
| 162 | |
| 163 // Attribute that specifies whether the webview should be autosized. | |
| 164 function AutosizeAttribute(webViewImpl) { | |
| 165 BooleanAttribute.call(this, WebViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl); | |
| 166 } | |
| 167 | |
| 168 AutosizeAttribute.prototype.__proto__ = BooleanAttribute.prototype; | |
| 169 | |
| 170 AutosizeAttribute.prototype.handleMutation = | |
| 171 AutosizeDimensionAttribute.prototype.handleMutation; | |
| 172 | |
| 173 // Attribute that sets the guest content's window.name object. | |
| 174 function NameAttribute(webViewImpl) { | |
| 175 WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_NAME, webViewImpl); | |
| 176 } | |
| 177 | |
| 178 NameAttribute.prototype.__proto__ = WebViewAttribute.prototype | |
| 179 | |
| 180 NameAttribute.prototype.handleMutation = function(oldValue, newValue) { | |
| 181 oldValue = oldValue || ''; | |
| 182 newValue = newValue || ''; | |
| 183 if (oldValue === newValue || !this.webViewImpl.guest.getId()) { | |
| 184 return; | |
| 185 } | |
| 186 | |
| 187 WebViewInternal.setName(this.webViewImpl.guest.getId(), newValue); | |
| 188 }; | |
| 189 | |
| 190 NameAttribute.prototype.setValue = function(value) { | |
| 191 value = value || ''; | |
| 192 if (value === '') { | |
| 193 this.webViewImpl.element.removeAttribute(this.name); | |
| 194 } else { | |
| 195 this.webViewImpl.element.setAttribute(this.name, value); | |
| 196 } | |
| 197 }; | |
| 198 | |
| 199 // Attribute representing the state of the storage partition. | |
| 200 function PartitionAttribute(webViewImpl) { | |
| 201 WebViewAttribute.call( | |
| 202 this, WebViewConstants.ATTRIBUTE_PARTITION, webViewImpl); | |
| 203 this.validPartitionId = true; | |
| 204 } | |
| 205 | |
| 206 PartitionAttribute.prototype.__proto__ = WebViewAttribute.prototype; | |
| 207 | |
| 208 PartitionAttribute.prototype.handleMutation = function(oldValue, newValue) { | |
| 209 newValue = newValue || ''; | |
| 210 | |
| 211 // The partition cannot change if the webview has already navigated. | |
| 212 if (!this.webViewImpl.attributes[ | |
| 213 WebViewConstants.ATTRIBUTE_SRC].beforeFirstNavigation) { | |
| 214 window.console.error(WebViewConstants.ERROR_MSG_ALREADY_NAVIGATED); | |
| 215 this.setValueIgnoreMutation(oldValue); | |
| 216 return; | |
| 217 } | |
| 218 if (newValue == 'persist:') { | |
| 219 this.validPartitionId = false; | |
| 220 window.console.error( | |
| 221 WebViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE); | |
| 222 } | |
| 223 }; | |
| 224 | |
| 225 PartitionAttribute.prototype.detach = function() { | |
| 226 this.validPartitionId = true; | |
| 227 }; | |
| 228 | |
| 229 // Attribute that handles the location and navigation of the webview. | |
| 230 function SrcAttribute(webViewImpl) { | |
| 231 WebViewAttribute.call(this, WebViewConstants.ATTRIBUTE_SRC, webViewImpl); | |
| 232 this.setupMutationObserver(); | |
| 233 this.beforeFirstNavigation = true; | |
| 234 this.elementAttached = false; | |
| 235 } | |
| 236 | |
| 237 SrcAttribute.prototype.__proto__ = WebViewAttribute.prototype; | |
| 238 | |
| 239 SrcAttribute.prototype.setValueIgnoreMutation = function(value) { | |
| 240 WebViewAttribute.prototype.setValueIgnoreMutation.call(this, value); | |
| 241 // takeRecords() is needed to clear queued up src mutations. Without it, it is | |
| 242 // possible for this change to get picked up asyncronously by src's mutation | |
| 243 // observer |observer|, and then get handled even though we do not want to | |
| 244 // handle this mutation. | |
| 245 this.observer.takeRecords(); | |
| 246 } | |
| 247 | |
| 248 SrcAttribute.prototype.handleMutation = function(oldValue, newValue) { | |
| 249 // Once we have navigated, we don't allow clearing the src attribute. | |
| 250 // Once <webview> enters a navigated state, it cannot return to a | |
| 251 // placeholder state. | |
| 252 if (!newValue && oldValue) { | |
| 253 // src attribute changes normally initiate a navigation. We suppress | |
| 254 // the next src attribute handler call to avoid reloading the page | |
| 255 // on every guest-initiated navigation. | |
| 256 this.setValueIgnoreMutation(oldValue); | |
| 257 return; | |
| 258 } | |
| 259 this.parse(); | |
| 260 }; | |
| 261 | |
| 262 SrcAttribute.prototype.attach = function() { | |
| 263 this.elementAttached = true; | |
| 264 this.parse(); | |
| 265 }; | |
| 266 | |
| 267 SrcAttribute.prototype.detach = function() { | |
| 268 this.beforeFirstNavigation = true; | |
| 269 this.elementAttached = false; | |
| 270 }; | |
| 271 | |
| 272 // The purpose of this mutation observer is to catch assignment to the src | |
| 273 // attribute without any changes to its value. This is useful in the case | |
| 274 // where the webview guest has crashed and navigating to the same address | |
| 275 // spawns off a new process. | |
| 276 SrcAttribute.prototype.setupMutationObserver = | |
| 277 function() { | |
| 278 this.observer = new MutationObserver(function(mutations) { | |
| 279 $Array.forEach(mutations, function(mutation) { | |
| 280 var oldValue = mutation.oldValue; | |
| 281 var newValue = this.getValue(); | |
| 282 if (oldValue != newValue) { | |
| 283 return; | |
| 284 } | |
| 285 this.handleMutation(oldValue, newValue); | |
| 286 }.bind(this)); | |
| 287 }.bind(this)); | |
| 288 var params = { | |
| 289 attributes: true, | |
| 290 attributeOldValue: true, | |
| 291 attributeFilter: [this.name] | |
| 292 }; | |
| 293 this.observer.observe(this.webViewImpl.element, params); | |
| 294 }; | |
| 295 | |
| 296 SrcAttribute.prototype.parse = function() { | |
| 297 if (!this.elementAttached || | |
| 298 !this.webViewImpl.attributes[ | |
| 299 WebViewConstants.ATTRIBUTE_PARTITION].validPartitionId || | |
| 300 !this.getValue()) { | |
| 301 return; | |
| 302 } | |
| 303 | |
| 304 if (!this.webViewImpl.guest.getId()) { | |
| 305 if (this.beforeFirstNavigation) { | |
| 306 this.beforeFirstNavigation = false; | |
| 307 this.webViewImpl.createGuest(); | |
| 308 } | |
| 309 return; | |
| 310 } | |
| 311 | |
| 312 // Navigate to |src|. | |
| 313 WebViewInternal.navigate(this.webViewImpl.guest.getId(), this.getValue()); | |
| 314 }; | |
| 315 | |
| 316 // ----------------------------------------------------------------------------- | |
| 317 | |
| 318 // Sets up all of the webview attributes. | |
| 319 WebViewImpl.prototype.setupWebViewAttributes = function() { | |
| 320 this.attributes = {}; | |
| 321 | |
| 322 this.attributes[WebViewConstants.ATTRIBUTE_ALLOWTRANSPARENCY] = | |
| 323 new AllowTransparencyAttribute(this); | |
| 324 this.attributes[WebViewConstants.ATTRIBUTE_ALLOWSCALING] = | |
| 325 new AllowScalingAttribute(this); | |
| 326 this.attributes[WebViewConstants.ATTRIBUTE_AUTOSIZE] = | |
| 327 new AutosizeAttribute(this); | |
| 328 this.attributes[WebViewConstants.ATTRIBUTE_NAME] = | |
| 329 new NameAttribute(this); | |
| 330 this.attributes[WebViewConstants.ATTRIBUTE_PARTITION] = | |
| 331 new PartitionAttribute(this); | |
| 332 this.attributes[WebViewConstants.ATTRIBUTE_SRC] = | |
| 333 new SrcAttribute(this); | |
| 334 | |
| 335 var autosizeAttributes = [WebViewConstants.ATTRIBUTE_MAXHEIGHT, | |
| 336 WebViewConstants.ATTRIBUTE_MAXWIDTH, | |
| 337 WebViewConstants.ATTRIBUTE_MINHEIGHT, | |
| 338 WebViewConstants.ATTRIBUTE_MINWIDTH]; | |
| 339 for (var i = 0; autosizeAttributes[i]; ++i) { | |
| 340 this.attributes[autosizeAttributes[i]] = | |
| 341 new AutosizeDimensionAttribute(autosizeAttributes[i], this); | |
| 342 } | |
| 343 }; | |
| OLD | NEW |