Chromium Code Reviews| 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 = |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 43 } | 43 } |
| 44 | 44 |
| 45 WebViewImpl.prototype.createBrowserPluginNode = function() { | 45 WebViewImpl.prototype.createBrowserPluginNode = function() { |
| 46 // We create BrowserPlugin as a custom element in order to observe changes | 46 // We create BrowserPlugin as a custom element in order to observe changes |
| 47 // to attributes synchronously. | 47 // to attributes synchronously. |
| 48 var browserPluginNode = new WebViewImpl.BrowserPlugin(); | 48 var browserPluginNode = new WebViewImpl.BrowserPlugin(); |
| 49 privates(browserPluginNode).internal = this; | 49 privates(browserPluginNode).internal = this; |
| 50 return browserPluginNode; | 50 return browserPluginNode; |
| 51 }; | 51 }; |
| 52 | 52 |
| 53 WebViewImpl.prototype.getGuestInstanceId = function() { | |
| 54 return this.guestInstanceId; | |
| 55 }; | |
| 56 | |
| 57 // Resets some state upon reattaching <webview> element to the DOM. | 53 // Resets some state upon reattaching <webview> element to the DOM. |
| 58 WebViewImpl.prototype.reset = function() { | 54 WebViewImpl.prototype.reset = function() { |
| 59 // If guestInstanceId is defined then the <webview> has navigated and has | 55 // If guestInstanceId is defined then the <webview> has navigated and has |
| 60 // already picked up a partition ID. Thus, we need to reset the initialization | 56 // already picked up a partition ID. Thus, we need to reset the initialization |
| 61 // state. However, it may be the case that beforeFirstNavigation is false BUT | 57 // state. However, it may be the case that beforeFirstNavigation is false BUT |
| 62 // guestInstanceId has yet to be initialized. This means that we have not | 58 // guestInstanceId has yet to be initialized. This means that we have not |
| 63 // heard back from createGuest yet. We will not reset the flag in this case so | 59 // heard back from createGuest yet. We will not reset the flag in this case so |
| 64 // that we don't end up allocating a second guest. | 60 // that we don't end up allocating a second guest. |
| 65 if (this.guestInstanceId) { | 61 if (this.guestInstanceId) { |
| 66 GuestViewInternal.destroyGuest(this.guestInstanceId); | 62 GuestViewInternal.destroyGuest(this.guestInstanceId); |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 96 this.webviewNode.addEventListener('focus', function(e) { | 92 this.webviewNode.addEventListener('focus', function(e) { |
| 97 // Focus the BrowserPlugin when the <webview> takes focus. | 93 // Focus the BrowserPlugin when the <webview> takes focus. |
| 98 this.browserPluginNode.focus(); | 94 this.browserPluginNode.focus(); |
| 99 }.bind(this)); | 95 }.bind(this)); |
| 100 this.webviewNode.addEventListener('blur', function(e) { | 96 this.webviewNode.addEventListener('blur', function(e) { |
| 101 // Blur the BrowserPlugin when the <webview> loses focus. | 97 // Blur the BrowserPlugin when the <webview> loses focus. |
| 102 this.browserPluginNode.blur(); | 98 this.browserPluginNode.blur(); |
| 103 }.bind(this)); | 99 }.bind(this)); |
| 104 }; | 100 }; |
| 105 | 101 |
| 106 // Validation helper function for executeScript() and insertCSS(). | |
| 107 WebViewImpl.prototype.validateExecuteCodeCall = function() { | |
| 108 if (!this.guestInstanceId) { | |
| 109 throw new Error(WebViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT); | |
| 110 } | |
| 111 }; | |
| 112 | |
| 113 WebViewImpl.prototype.setupWebviewNodeProperties = function() { | 102 WebViewImpl.prototype.setupWebviewNodeProperties = function() { |
| 114 // We cannot use {writable: true} property descriptor because we want a | 103 // We cannot use {writable: true} property descriptor because we want a |
| 115 // dynamic getter value. | 104 // dynamic getter value. |
| 116 Object.defineProperty(this.webviewNode, 'contentWindow', { | 105 Object.defineProperty(this.webviewNode, 'contentWindow', { |
| 117 get: function() { | 106 get: function() { |
| 118 if (this.contentWindow) { | 107 if (this.contentWindow) { |
| 119 return this.contentWindow; | 108 return this.contentWindow; |
| 120 } | 109 } |
| 121 window.console.error( | 110 window.console.error( |
| 122 WebViewConstants.ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE); | 111 WebViewConstants.ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 168 var newWidth = webViewEvent.newWidth; | 157 var newWidth = webViewEvent.newWidth; |
| 169 var newHeight = webViewEvent.newHeight; | 158 var newHeight = webViewEvent.newHeight; |
| 170 | 159 |
| 171 var node = this.webviewNode; | 160 var node = this.webviewNode; |
| 172 | 161 |
| 173 var width = node.offsetWidth; | 162 var width = node.offsetWidth; |
| 174 var height = node.offsetHeight; | 163 var height = node.offsetHeight; |
| 175 | 164 |
| 176 // Check the current bounds to make sure we do not resize <webview> | 165 // Check the current bounds to make sure we do not resize <webview> |
| 177 // outside of current constraints. | 166 // outside of current constraints. |
| 178 var maxWidth; | 167 var maxWidth = this.attributes[ |
| 179 if (node.hasAttribute(WebViewConstants.ATTRIBUTE_MAXWIDTH) && | 168 WebViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || width; |
| 180 node[WebViewConstants.ATTRIBUTE_MAXWIDTH]) { | 169 var minWidth = this.attributes[ |
| 181 maxWidth = node[WebViewConstants.ATTRIBUTE_MAXWIDTH]; | 170 WebViewConstants.ATTRIBUTE_MINWIDTH].getValue() || width; |
| 182 } else { | 171 var maxHeight = this.attributes[ |
| 183 maxWidth = width; | 172 WebViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || height; |
| 184 } | 173 var minHeight = this.attributes[ |
| 174 WebViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || height; | |
| 185 | 175 |
| 186 var minWidth; | |
| 187 if (node.hasAttribute(WebViewConstants.ATTRIBUTE_MINWIDTH) && | |
| 188 node[WebViewConstants.ATTRIBUTE_MINWIDTH]) { | |
| 189 minWidth = node[WebViewConstants.ATTRIBUTE_MINWIDTH]; | |
| 190 } else { | |
| 191 minWidth = width; | |
| 192 } | |
| 193 if (minWidth > maxWidth) { | 176 if (minWidth > maxWidth) { |
| 194 minWidth = maxWidth; | 177 minWidth = maxWidth; |
| 195 } | 178 } |
|
Fady Samuel
2014/11/11 00:04:21
minWidth = Math.min(minWidth, maxWidth);
https://
| |
| 196 | |
| 197 var maxHeight; | |
| 198 if (node.hasAttribute(WebViewConstants.ATTRIBUTE_MAXHEIGHT) && | |
| 199 node[WebViewConstants.ATTRIBUTE_MAXHEIGHT]) { | |
| 200 maxHeight = node[WebViewConstants.ATTRIBUTE_MAXHEIGHT]; | |
| 201 } else { | |
| 202 maxHeight = height; | |
| 203 } | |
| 204 | |
| 205 var minHeight; | |
| 206 if (node.hasAttribute(WebViewConstants.ATTRIBUTE_MINHEIGHT) && | |
| 207 node[WebViewConstants.ATTRIBUTE_MINHEIGHT]) { | |
| 208 minHeight = node[WebViewConstants.ATTRIBUTE_MINHEIGHT]; | |
| 209 } else { | |
| 210 minHeight = height; | |
| 211 } | |
| 212 if (minHeight > maxHeight) { | 179 if (minHeight > maxHeight) { |
| 213 minHeight = maxHeight; | 180 minHeight = maxHeight; |
| 214 } | 181 } |
|
Fady Samuel
2014/11/11 00:04:21
minHeight = Math.min(minHeight, maxHeight);
| |
| 215 | 182 |
| 216 if (!this.attributes[WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue() || | 183 if (!this.attributes[WebViewConstants.ATTRIBUTE_AUTOSIZE].getValue() || |
| 217 (newWidth >= minWidth && | 184 (newWidth >= minWidth && |
| 218 newWidth <= maxWidth && | 185 newWidth <= maxWidth && |
| 219 newHeight >= minHeight && | 186 newHeight >= minHeight && |
| 220 newHeight <= maxHeight)) { | 187 newHeight <= maxHeight)) { |
| 221 node.style.width = newWidth + 'px'; | 188 node.style.width = newWidth + 'px'; |
| 222 node.style.height = newHeight + 'px'; | 189 node.style.height = newHeight + 'px'; |
| 223 // Only fire the DOM event if the size of the <webview> has actually | 190 // Only fire the DOM event if the size of the <webview> has actually |
| 224 // changed. | 191 // changed. |
| 225 this.dispatchEvent(webViewEvent); | 192 this.dispatchEvent(webViewEvent); |
| 226 } | 193 } |
| 227 }; | 194 }; |
| 228 | 195 |
| 229 // Returns if <object> is in the render tree. | |
| 230 WebViewImpl.prototype.isPluginInRenderTree = function() { | |
| 231 return !!this.internalInstanceId && this.internalInstanceId != 0; | |
| 232 }; | |
| 233 | |
| 234 WebViewImpl.prototype.hasNavigated = function() { | |
| 235 return !this.beforeFirstNavigation; | |
| 236 }; | |
| 237 | |
| 238 WebViewImpl.prototype.parseSrcAttribute = function() { | 196 WebViewImpl.prototype.parseSrcAttribute = function() { |
| 239 if (!this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].validPartitionId || | 197 if (!this.attributes[WebViewConstants.ATTRIBUTE_PARTITION].validPartitionId || |
| 240 !this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue()) { | 198 !this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue()) { |
| 241 return; | 199 return; |
| 242 } | 200 } |
| 243 | 201 |
| 244 if (this.guestInstanceId == undefined) { | 202 if (this.guestInstanceId == undefined) { |
| 245 if (this.beforeFirstNavigation) { | 203 if (this.beforeFirstNavigation) { |
| 246 this.beforeFirstNavigation = false; | 204 this.beforeFirstNavigation = false; |
| 247 this.createGuest(); | 205 this.createGuest(); |
| 248 } | 206 } |
| 249 return; | 207 return; |
| 250 } | 208 } |
| 251 | 209 |
| 252 // Navigate to |this.src|. | 210 // Navigate to |this.src|. |
| 253 WebViewInternal.navigate( | 211 WebViewInternal.navigate( |
| 254 this.guestInstanceId, | 212 this.guestInstanceId, |
| 255 this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue()); | 213 this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue()); |
| 256 }; | 214 }; |
| 257 | 215 |
| 258 WebViewImpl.prototype.parseAttributes = function() { | |
| 259 if (!this.elementAttached) { | |
| 260 return; | |
| 261 } | |
| 262 this.parseSrcAttribute(); | |
| 263 }; | |
| 264 | |
| 265 WebViewImpl.prototype.createGuest = function() { | 216 WebViewImpl.prototype.createGuest = function() { |
| 266 if (this.pendingGuestCreation) { | 217 if (this.pendingGuestCreation) { |
| 267 return; | 218 return; |
| 268 } | 219 } |
| 269 var params = { | 220 var params = { |
| 270 'storagePartitionId': this.attributes[ | 221 'storagePartitionId': this.attributes[ |
| 271 WebViewConstants.ATTRIBUTE_PARTITION].getValue() | 222 WebViewConstants.ATTRIBUTE_PARTITION].getValue() |
| 272 }; | 223 }; |
| 273 GuestViewInternal.createGuest( | 224 GuestViewInternal.createGuest( |
| 274 'webview', | 225 'webview', |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 349 for (var i in this.attributes) { | 300 for (var i in this.attributes) { |
| 350 params[i] = this.attributes[i].getValue(); | 301 params[i] = this.attributes[i].getValue(); |
| 351 } | 302 } |
| 352 return params; | 303 return params; |
| 353 }; | 304 }; |
| 354 | 305 |
| 355 WebViewImpl.prototype.attachWindow = function(guestInstanceId) { | 306 WebViewImpl.prototype.attachWindow = function(guestInstanceId) { |
| 356 this.guestInstanceId = guestInstanceId; | 307 this.guestInstanceId = guestInstanceId; |
| 357 var params = this.buildAttachParams(); | 308 var params = this.buildAttachParams(); |
| 358 | 309 |
| 359 if (!this.isPluginInRenderTree()) { | 310 if (!this.internalInstanceId) { |
| 360 return true; | 311 return true; |
| 361 } | 312 } |
| 362 | 313 |
| 363 return guestViewInternalNatives.AttachGuest( | 314 return guestViewInternalNatives.AttachGuest( |
| 364 this.internalInstanceId, | 315 this.internalInstanceId, |
| 365 this.guestInstanceId, | 316 this.guestInstanceId, |
| 366 params, function(w) { | 317 params, function(w) { |
| 367 this.contentWindow = w; | 318 this.contentWindow = w; |
| 368 }.bind(this) | 319 }.bind(this) |
| 369 ); | 320 ); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 391 | 342 |
| 392 // Clears browsing data for the WebView partition. | 343 // Clears browsing data for the WebView partition. |
| 393 WebViewImpl.prototype.clearData = function() { | 344 WebViewImpl.prototype.clearData = function() { |
| 394 if (!this.guestInstanceId) { | 345 if (!this.guestInstanceId) { |
| 395 return; | 346 return; |
| 396 } | 347 } |
| 397 var args = $Array.concat([this.guestInstanceId], $Array.slice(arguments)); | 348 var args = $Array.concat([this.guestInstanceId], $Array.slice(arguments)); |
| 398 $Function.apply(WebViewInternal.clearData, null, args); | 349 $Function.apply(WebViewInternal.clearData, null, args); |
| 399 }; | 350 }; |
| 400 | 351 |
| 401 // Injects JavaScript code into the guest page. | 352 // Shared implementation of executeScript() and insertCSS(). |
| 402 WebViewImpl.prototype.executeScript = function(var_args) { | 353 WebViewImpl.prototype.executeCode = function(func, args) { |
| 403 this.validateExecuteCodeCall(); | 354 if (!this.guestInstanceId) { |
| 355 window.console.error(WebViewConstants.ERROR_MSG_CANNOT_INJECT_SCRIPT); | |
| 356 return; | |
| 357 } | |
| 358 | |
| 404 var webviewSrc = this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(); | 359 var webviewSrc = this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(); |
| 405 if (this.baseUrlForDataUrl != '') { | 360 if (this.baseUrlForDataUrl != '') { |
| 406 webviewSrc = this.baseUrlForDataUrl; | 361 webviewSrc = this.baseUrlForDataUrl; |
| 407 } | 362 } |
| 408 var args = $Array.concat([this.guestInstanceId, webviewSrc], | 363 |
| 409 $Array.slice(arguments)); | 364 args = $Array.concat([this.guestInstanceId, webviewSrc], |
| 410 $Function.apply(WebViewInternal.executeScript, null, args); | 365 $Array.slice(args)); |
| 366 $Function.apply(func, null, args); | |
| 367 } | |
| 368 | |
| 369 // Injects JavaScript code into the guest page. | |
| 370 WebViewImpl.prototype.executeScript = function(var_args) { | |
| 371 this.executeCode(WebViewInternal.executeScript, $Array.slice(arguments)); | |
| 411 }; | 372 }; |
| 412 | 373 |
| 413 // Initiates a find-in-page request. | 374 // Initiates a find-in-page request. |
| 414 WebViewImpl.prototype.find = function(search_text, options, callback) { | 375 WebViewImpl.prototype.find = function(search_text, options, callback) { |
| 415 if (!this.guestInstanceId) { | 376 if (!this.guestInstanceId) { |
| 416 return; | 377 return; |
| 417 } | 378 } |
| 418 WebViewInternal.find(this.guestInstanceId, search_text, options, callback); | 379 WebViewInternal.find(this.guestInstanceId, search_text, options, callback); |
| 419 }; | 380 }; |
| 420 | 381 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 446 // navigation. | 407 // navigation. |
| 447 WebViewImpl.prototype.go = function(relativeIndex, callback) { | 408 WebViewImpl.prototype.go = function(relativeIndex, callback) { |
| 448 if (!this.guestInstanceId) { | 409 if (!this.guestInstanceId) { |
| 449 return; | 410 return; |
| 450 } | 411 } |
| 451 WebViewInternal.go(this.guestInstanceId, relativeIndex, callback); | 412 WebViewInternal.go(this.guestInstanceId, relativeIndex, callback); |
| 452 }; | 413 }; |
| 453 | 414 |
| 454 // Injects CSS into the guest page. | 415 // Injects CSS into the guest page. |
| 455 WebViewImpl.prototype.insertCSS = function(var_args) { | 416 WebViewImpl.prototype.insertCSS = function(var_args) { |
| 456 this.validateExecuteCodeCall(); | 417 this.executeCode(WebViewInternal.insertCSS, $Array.slice(arguments)); |
| 457 var webviewSrc = this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue(); | |
| 458 if (this.baseUrlForDataUrl != '') { | |
| 459 webviewSrc = this.baseUrlForDataUrl; | |
| 460 } | |
| 461 var args = $Array.concat([this.guestInstanceId, webviewSrc], | |
| 462 $Array.slice(arguments)); | |
| 463 $Function.apply(WebViewInternal.insertCSS, null, args); | |
| 464 }; | 418 }; |
| 465 | 419 |
| 466 // Indicates whether or not the webview's user agent string has been overridden. | 420 // Indicates whether or not the webview's user agent string has been overridden. |
| 467 WebViewImpl.prototype.isUserAgentOverridden = function() { | 421 WebViewImpl.prototype.isUserAgentOverridden = function() { |
| 468 return !!this.userAgentOverride && | 422 return !!this.userAgentOverride && |
| 469 this.userAgentOverride != navigator.userAgent; | 423 this.userAgentOverride != navigator.userAgent; |
| 470 }; | 424 }; |
| 471 | 425 |
| 472 // Prints the contents of the webview. | 426 // Prints the contents of the webview. |
| 473 WebViewImpl.prototype.print = function() { | 427 WebViewImpl.prototype.print = function() { |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 587 internal.reset(); | 541 internal.reset(); |
| 588 }; | 542 }; |
| 589 | 543 |
| 590 proto.attachedCallback = function() { | 544 proto.attachedCallback = function() { |
| 591 var internal = privates(this).internal; | 545 var internal = privates(this).internal; |
| 592 if (!internal) { | 546 if (!internal) { |
| 593 return; | 547 return; |
| 594 } | 548 } |
| 595 if (!internal.elementAttached) { | 549 if (!internal.elementAttached) { |
| 596 internal.elementAttached = true; | 550 internal.elementAttached = true; |
| 597 internal.parseAttributes(); | 551 internal.parseSrcAttribute(); |
| 598 } | 552 } |
| 599 }; | 553 }; |
| 600 | 554 |
| 601 // Public-facing API methods. | 555 // Public-facing API methods. |
| 602 var methods = [ | 556 var methods = [ |
| 603 'back', | 557 'back', |
| 604 'canGoBack', | 558 'canGoBack', |
| 605 'canGoForward', | 559 'canGoForward', |
| 606 'clearData', | 560 'clearData', |
| 607 'executeScript', | 561 'executeScript', |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 657 registerBrowserPluginElement(); | 611 registerBrowserPluginElement(); |
| 658 registerWebViewElement(); | 612 registerWebViewElement(); |
| 659 window.removeEventListener(event.type, listener, useCapture); | 613 window.removeEventListener(event.type, listener, useCapture); |
| 660 }, useCapture); | 614 }, useCapture); |
| 661 | 615 |
| 662 // Implemented when the ChromeWebView API is available. | 616 // Implemented when the ChromeWebView API is available. |
| 663 WebViewImpl.prototype.maybeGetChromeWebViewEvents = function() {}; | 617 WebViewImpl.prototype.maybeGetChromeWebViewEvents = function() {}; |
| 664 | 618 |
| 665 // Implemented when the experimental WebView API is available. | 619 // Implemented when the experimental WebView API is available. |
| 666 WebViewImpl.maybeGetExperimentalAPIs = function() {}; | 620 WebViewImpl.maybeGetExperimentalAPIs = function() {}; |
| 667 WebViewImpl.prototype.maybeGetExperimentalEvents = function() {}; | |
| 668 WebViewImpl.prototype.setupExperimentalContextMenus = function() {}; | |
| 669 | 621 |
| 670 // Exports. | 622 // Exports. |
| 671 exports.WebViewImpl = WebViewImpl; | 623 exports.WebViewImpl = WebViewImpl; |
| 672 exports.WebViewInternal = WebViewInternal; | 624 exports.WebViewInternal = WebViewInternal; |
| OLD | NEW |