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'); | 10 var EventBindings = require('event_bindings'); |
| 11 var GuestView = require('binding').Binding.create('guestview').generate(); |
11 var IdGenerator = requireNative('id_generator'); | 12 var IdGenerator = requireNative('id_generator'); |
12 var MessagingNatives = requireNative('messaging_natives'); | 13 var MessagingNatives = requireNative('messaging_natives'); |
13 var WebRequestEvent = require('webRequestInternal').WebRequestEvent; | 14 var WebRequestEvent = require('webRequestInternal').WebRequestEvent; |
14 var WebRequestSchema = | 15 var WebRequestSchema = |
15 requireNative('schema_registry').GetSchema('webRequest'); | 16 requireNative('schema_registry').GetSchema('webRequest'); |
16 var DeclarativeWebRequestSchema = | 17 var DeclarativeWebRequestSchema = |
17 requireNative('schema_registry').GetSchema('declarativeWebRequest'); | 18 requireNative('schema_registry').GetSchema('declarativeWebRequest'); |
18 var WebView = require('webview').WebView; | 19 var WebView = require('webview').WebView; |
19 | 20 |
20 var WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight'; | 21 var WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight'; |
21 var WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth'; | 22 var WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth'; |
22 var WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight'; | 23 var WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight'; |
23 var WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth'; | 24 var WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth'; |
| 25 var WEB_VIEW_ATTRIBUTE_PARTITION = 'partition'; |
| 26 |
| 27 var ERROR_MSG_ALREADY_NAVIGATED = |
| 28 'The object has already navigated, so its partition cannot be changed.'; |
| 29 var ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.'; |
24 | 30 |
25 /** @type {Array.<string>} */ | 31 /** @type {Array.<string>} */ |
26 var WEB_VIEW_ATTRIBUTES = [ | 32 var WEB_VIEW_ATTRIBUTES = [ |
27 'allowtransparency', | 33 'allowtransparency', |
28 'autosize', | 34 'autosize', |
29 'partition', | |
30 WEB_VIEW_ATTRIBUTE_MINHEIGHT, | 35 WEB_VIEW_ATTRIBUTE_MINHEIGHT, |
31 WEB_VIEW_ATTRIBUTE_MINWIDTH, | 36 WEB_VIEW_ATTRIBUTE_MINWIDTH, |
32 WEB_VIEW_ATTRIBUTE_MAXHEIGHT, | 37 WEB_VIEW_ATTRIBUTE_MAXHEIGHT, |
33 WEB_VIEW_ATTRIBUTE_MAXWIDTH | 38 WEB_VIEW_ATTRIBUTE_MAXWIDTH |
34 ]; | 39 ]; |
35 | 40 |
| 41 /** @class representing state of storage partition. */ |
| 42 function Partition() { |
| 43 this.validPartitionId = true; |
| 44 this.persist_storage_ = false; |
| 45 this.storage_partition_id = ''; |
| 46 }; |
| 47 |
| 48 Partition.prototype.toAttribute = function() { |
| 49 if (!this.validPartitionId) { |
| 50 return ''; |
| 51 } |
| 52 return (this.persist_storage_ ? 'persist:' : '') + this.storage_partition_id; |
| 53 }; |
| 54 |
| 55 Partition.prototype.fromAttribute = function(value, hasNavigated) { |
| 56 var result = {}; |
| 57 if (hasNavigated) { |
| 58 result.error = ERROR_MSG_ALREADY_NAVIGATED; |
| 59 return result; |
| 60 } |
| 61 if (!value) { |
| 62 value = ''; |
| 63 } |
| 64 |
| 65 var LEN = 'persist:'.length; |
| 66 if (value.substr(0, LEN) == 'persist:') { |
| 67 value = value.substr(LEN); |
| 68 if (!value) { |
| 69 this.validPartitionId = false; |
| 70 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; |
| 71 return result; |
| 72 } |
| 73 this.persist_storage_ = true; |
| 74 } else { |
| 75 this.persist_storage_ = false; |
| 76 } |
| 77 |
| 78 this.storage_partition_id = value; |
| 79 return result; |
| 80 }; |
| 81 |
36 var CreateEvent = function(name) { | 82 var CreateEvent = function(name) { |
37 var eventOpts = {supportsListeners: true, supportsFilters: true}; | 83 var eventOpts = {supportsListeners: true, supportsFilters: true}; |
38 return new EventBindings.Event(name, undefined, eventOpts); | 84 return new EventBindings.Event(name, undefined, eventOpts); |
39 }; | 85 }; |
40 | 86 |
41 // WEB_VIEW_EVENTS is a map of stable <webview> DOM event names to their | 87 // WEB_VIEW_EVENTS is a map of stable <webview> DOM event names to their |
42 // associated extension event descriptor objects. | 88 // associated extension event descriptor objects. |
43 // An event listener will be attached to the extension event |evt| specified in | 89 // An event listener will be attached to the extension event |evt| specified in |
44 // the descriptor. | 90 // the descriptor. |
45 // |fields| specifies the public-facing fields in the DOM event that are | 91 // |fields| specifies the public-facing fields in the DOM event that are |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 // Implemented when the experimental API is available. | 215 // Implemented when the experimental API is available. |
170 WebViewInternal.maybeRegisterExperimentalAPIs = function(proto) {} | 216 WebViewInternal.maybeRegisterExperimentalAPIs = function(proto) {} |
171 | 217 |
172 /** | 218 /** |
173 * @constructor | 219 * @constructor |
174 */ | 220 */ |
175 function WebViewInternal(webviewNode) { | 221 function WebViewInternal(webviewNode) { |
176 privates(webviewNode).internal = this; | 222 privates(webviewNode).internal = this; |
177 this.webviewNode = webviewNode; | 223 this.webviewNode = webviewNode; |
178 this.attached = false; | 224 this.attached = false; |
| 225 |
| 226 this.beforeFirstNavigation = true; |
| 227 this.validPartitionId = true; |
| 228 |
179 this.browserPluginNode = this.createBrowserPluginNode(); | 229 this.browserPluginNode = this.createBrowserPluginNode(); |
180 var shadowRoot = this.webviewNode.createShadowRoot(); | 230 var shadowRoot = this.webviewNode.createShadowRoot(); |
181 shadowRoot.appendChild(this.browserPluginNode); | 231 shadowRoot.appendChild(this.browserPluginNode); |
182 | 232 |
183 this.setupWebviewNodeAttributes(); | 233 this.setupWebviewNodeAttributes(); |
184 this.setupFocusPropagation(); | 234 this.setupFocusPropagation(); |
185 this.setupWebviewNodeProperties(); | 235 this.setupWebviewNodeProperties(); |
| 236 |
| 237 this.viewInstanceId = IdGenerator.GetNextId(); |
| 238 |
| 239 this.partition = new Partition(); |
| 240 this.parseAttributes(); |
| 241 |
186 this.setupWebviewNodeEvents(); | 242 this.setupWebviewNodeEvents(); |
187 } | 243 } |
188 | 244 |
189 /** | 245 /** |
190 * @private | 246 * @private |
191 */ | 247 */ |
192 WebViewInternal.prototype.createBrowserPluginNode = function() { | 248 WebViewInternal.prototype.createBrowserPluginNode = function() { |
193 // We create BrowserPlugin as a custom element in order to observe changes | 249 // We create BrowserPlugin as a custom element in order to observe changes |
194 // to attributes synchronously. | 250 // to attributes synchronously. |
195 var browserPluginNode = new WebViewInternal.BrowserPlugin(); | 251 var browserPluginNode = new WebViewInternal.BrowserPlugin(); |
196 privates(browserPluginNode).internal = this; | 252 privates(browserPluginNode).internal = this; |
197 | 253 |
198 var ALL_ATTRIBUTES = WEB_VIEW_ATTRIBUTES.concat(['src']); | 254 $Array.forEach(WEB_VIEW_ATTRIBUTES, function(attributeName) { |
199 $Array.forEach(ALL_ATTRIBUTES, function(attributeName) { | |
200 // Only copy attributes that have been assigned values, rather than copying | 255 // Only copy attributes that have been assigned values, rather than copying |
201 // a series of undefined attributes to BrowserPlugin. | 256 // a series of undefined attributes to BrowserPlugin. |
202 if (this.webviewNode.hasAttribute(attributeName)) { | 257 if (this.webviewNode.hasAttribute(attributeName)) { |
203 browserPluginNode.setAttribute( | 258 browserPluginNode.setAttribute( |
204 attributeName, this.webviewNode.getAttribute(attributeName)); | 259 attributeName, this.webviewNode.getAttribute(attributeName)); |
205 } else if (this.webviewNode[attributeName]){ | 260 } else if (this.webviewNode[attributeName]){ |
206 // Reading property using has/getAttribute does not work on | 261 // Reading property using has/getAttribute does not work on |
207 // document.DOMContentLoaded event (but works on | 262 // document.DOMContentLoaded event (but works on |
208 // window.DOMContentLoaded event). | 263 // window.DOMContentLoaded event). |
209 // So copy from property if copying from attribute fails. | 264 // So copy from property if copying from attribute fails. |
210 browserPluginNode.setAttribute( | 265 browserPluginNode.setAttribute( |
211 attributeName, this.webviewNode[attributeName]); | 266 attributeName, this.webviewNode[attributeName]); |
212 } | 267 } |
213 }, this); | 268 }, this); |
214 | 269 |
215 return browserPluginNode; | 270 return browserPluginNode; |
216 }; | 271 }; |
217 | 272 |
218 /** | 273 /** |
219 * @private | 274 * @private |
| 275 * Resets some state upon re-attaching <webview> element to the DOM. |
| 276 */ |
| 277 WebViewInternal.prototype.resetUponReattachment = function() { |
| 278 this.instanceId = undefined; |
| 279 this.beforeFirstNavigation = true; |
| 280 this.validPartitionId = true; |
| 281 this.partition.validPartitionId = true; |
| 282 }; |
| 283 |
| 284 /** |
| 285 * @private |
220 */ | 286 */ |
221 WebViewInternal.prototype.setupFocusPropagation = function() { | 287 WebViewInternal.prototype.setupFocusPropagation = function() { |
222 if (!this.webviewNode.hasAttribute('tabIndex')) { | 288 if (!this.webviewNode.hasAttribute('tabIndex')) { |
223 // <webview> needs a tabIndex in order to be focusable. | 289 // <webview> needs a tabIndex in order to be focusable. |
224 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute | 290 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute |
225 // to allow <webview> to be focusable. | 291 // to allow <webview> to be focusable. |
226 // See http://crbug.com/231664. | 292 // See http://crbug.com/231664. |
227 this.webviewNode.setAttribute('tabIndex', -1); | 293 this.webviewNode.setAttribute('tabIndex', -1); |
228 } | 294 } |
229 var self = this; | 295 var self = this; |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 Object.defineProperty(this.webviewNode, 'name', { | 458 Object.defineProperty(this.webviewNode, 'name', { |
393 get: function() { | 459 get: function() { |
394 return self.name; | 460 return self.name; |
395 }, | 461 }, |
396 set: function(value) { | 462 set: function(value) { |
397 self.webviewNode.setAttribute('name', value); | 463 self.webviewNode.setAttribute('name', value); |
398 }, | 464 }, |
399 enumerable: true | 465 enumerable: true |
400 }); | 466 }); |
401 | 467 |
| 468 Object.defineProperty(this.webviewNode, 'partition', { |
| 469 get: function() { |
| 470 return self.partition.toAttribute(); |
| 471 }, |
| 472 set: function(value) { |
| 473 var result = self.partition.fromAttribute(value, self.hasNavigated()); |
| 474 if (result.error) { |
| 475 throw result.error; |
| 476 } |
| 477 self.webviewNode.setAttribute('partition', value); |
| 478 }, |
| 479 enumerable: true |
| 480 }); |
| 481 |
402 // We cannot use {writable: true} property descriptor because we want a | 482 // We cannot use {writable: true} property descriptor because we want a |
403 // dynamic getter value. | 483 // dynamic getter value. |
404 Object.defineProperty(this.webviewNode, 'contentWindow', { | 484 Object.defineProperty(this.webviewNode, 'contentWindow', { |
405 get: function() { | 485 get: function() { |
406 if (browserPluginNode.contentWindow) | 486 if (browserPluginNode.contentWindow) |
407 return browserPluginNode.contentWindow; | 487 return browserPluginNode.contentWindow; |
408 window.console.error(ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE); | 488 window.console.error(ERROR_MSG_CONTENTWINDOW_NOT_AVAILABLE); |
409 }, | 489 }, |
410 // No setter. | 490 // No setter. |
411 enumerable: true | 491 enumerable: true |
(...skipping 10 matching lines...) Expand all Loading... |
422 /** | 502 /** |
423 * @private | 503 * @private |
424 */ | 504 */ |
425 WebViewInternal.prototype.setupWebViewSrcAttributeMutationObserver = | 505 WebViewInternal.prototype.setupWebViewSrcAttributeMutationObserver = |
426 function() { | 506 function() { |
427 // The purpose of this mutation observer is to catch assignment to the src | 507 // The purpose of this mutation observer is to catch assignment to the src |
428 // attribute without any changes to its value. This is useful in the case | 508 // attribute without any changes to its value. This is useful in the case |
429 // where the webview guest has crashed and navigating to the same address | 509 // where the webview guest has crashed and navigating to the same address |
430 // spawns off a new process. | 510 // spawns off a new process. |
431 var self = this; | 511 var self = this; |
432 this.srcObserver = new MutationObserver(function(mutations) { | 512 this.srcAndPartitionObserver = new MutationObserver(function(mutations) { |
433 $Array.forEach(mutations, function(mutation) { | 513 $Array.forEach(mutations, function(mutation) { |
434 var oldValue = mutation.oldValue; | 514 var oldValue = mutation.oldValue; |
435 var newValue = self.webviewNode.getAttribute(mutation.attributeName); | 515 var newValue = self.webviewNode.getAttribute(mutation.attributeName); |
436 if (oldValue != newValue) { | 516 if (oldValue != newValue) { |
437 return; | 517 return; |
438 } | 518 } |
439 self.handleWebviewAttributeMutation( | 519 self.handleWebviewAttributeMutation( |
440 mutation.attributeName, oldValue, newValue); | 520 mutation.attributeName, oldValue, newValue); |
441 }); | 521 }); |
442 }); | 522 }); |
443 var params = { | 523 var params = { |
444 attributes: true, | 524 attributes: true, |
445 attributeOldValue: true, | 525 attributeOldValue: true, |
446 attributeFilter: ['src'] | 526 attributeFilter: ['src', 'partition'] |
447 }; | 527 }; |
448 this.srcObserver.observe(this.webviewNode, params); | 528 this.srcAndPartitionObserver.observe(this.webviewNode, params); |
449 }; | 529 }; |
450 | 530 |
451 /** | 531 /** |
452 * @private | 532 * @private |
453 */ | 533 */ |
454 WebViewInternal.prototype.handleWebviewAttributeMutation = | 534 WebViewInternal.prototype.handleWebviewAttributeMutation = |
455 function(name, oldValue, newValue) { | 535 function(name, oldValue, newValue) { |
456 // This observer monitors mutations to attributes of the <webview> and | 536 // This observer monitors mutations to attributes of the <webview> and |
457 // updates the BrowserPlugin properties accordingly. In turn, updating | 537 // updates the BrowserPlugin properties accordingly. In turn, updating |
458 // a BrowserPlugin property will update the corresponding BrowserPlugin | 538 // a BrowserPlugin property will update the corresponding BrowserPlugin |
(...skipping 26 matching lines...) Expand all Loading... |
485 // src attribute changes normally initiate a navigation. We suppress | 565 // src attribute changes normally initiate a navigation. We suppress |
486 // the next src attribute handler call to avoid reloading the page | 566 // the next src attribute handler call to avoid reloading the page |
487 // on every guest-initiated navigation. | 567 // on every guest-initiated navigation. |
488 this.ignoreNextSrcAttributeChange = true; | 568 this.ignoreNextSrcAttributeChange = true; |
489 this.webviewNode.setAttribute('src', oldValue); | 569 this.webviewNode.setAttribute('src', oldValue); |
490 return; | 570 return; |
491 } | 571 } |
492 this.src = newValue; | 572 this.src = newValue; |
493 if (this.ignoreNextSrcAttributeChange) { | 573 if (this.ignoreNextSrcAttributeChange) { |
494 // Don't allow the src mutation observer to see this change. | 574 // Don't allow the src mutation observer to see this change. |
495 this.srcObserver.takeRecords(); | 575 this.srcAndPartitionObserver.takeRecords(); |
496 this.ignoreNextSrcAttributeChange = false; | 576 this.ignoreNextSrcAttributeChange = false; |
497 return; | 577 return; |
498 } | 578 } |
| 579 var result = {}; |
| 580 this.parseSrcAttribute(result); |
| 581 |
| 582 if (result.error) { |
| 583 throw result.error; |
| 584 } |
| 585 } else if (name == 'partition') { |
| 586 // Note that throwing error here won't synchronously propagate. |
| 587 this.partition.fromAttribute(newValue, this.hasNavigated()); |
499 } | 588 } |
| 589 |
| 590 // No <webview> -> <object> mutation propagation for these attributes. |
| 591 if (name == 'src' || name == 'partition') { |
| 592 return; |
| 593 } |
| 594 |
500 if (this.browserPluginNode.hasOwnProperty(name)) { | 595 if (this.browserPluginNode.hasOwnProperty(name)) { |
501 this.browserPluginNode[name] = newValue; | 596 this.browserPluginNode[name] = newValue; |
502 } else { | 597 } else { |
503 this.browserPluginNode.setAttribute(name, newValue); | 598 this.browserPluginNode.setAttribute(name, newValue); |
504 } | 599 } |
505 }; | 600 }; |
506 | 601 |
507 /** | 602 /** |
508 * @private | 603 * @private |
509 */ | 604 */ |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
590 if (webViewEvent.newWidth >= minWidth && | 685 if (webViewEvent.newWidth >= minWidth && |
591 webViewEvent.newWidth <= maxWidth && | 686 webViewEvent.newWidth <= maxWidth && |
592 webViewEvent.newHeight >= minHeight && | 687 webViewEvent.newHeight >= minHeight && |
593 webViewEvent.newHeight <= maxHeight) { | 688 webViewEvent.newHeight <= maxHeight) { |
594 node.style.width = webViewEvent.newWidth + 'px'; | 689 node.style.width = webViewEvent.newWidth + 'px'; |
595 node.style.height = webViewEvent.newHeight + 'px'; | 690 node.style.height = webViewEvent.newHeight + 'px'; |
596 } | 691 } |
597 node.dispatchEvent(webViewEvent); | 692 node.dispatchEvent(webViewEvent); |
598 }; | 693 }; |
599 | 694 |
| 695 WebViewInternal.prototype.hasNavigated = function() { |
| 696 return !this.beforeFirstNavigation; |
| 697 }; |
| 698 |
| 699 /** @return {boolean} */ |
| 700 WebViewInternal.prototype.parseSrcAttribute = function(result) { |
| 701 if (!this.partition.validPartitionId) { |
| 702 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; |
| 703 return false; |
| 704 } |
| 705 this.src = this.webviewNode.getAttribute('src'); |
| 706 |
| 707 if (!this.src) { |
| 708 return true; |
| 709 } |
| 710 |
| 711 if (!this.hasGuestInstanceID()) { |
| 712 if (this.beforeFirstNavigation) { |
| 713 this.beforeFirstNavigation = false; |
| 714 this.allocateInstanceId(); |
| 715 } |
| 716 return true; |
| 717 } |
| 718 |
| 719 // Navigate to this.src. |
| 720 WebView.navigate(this.instanceId, this.src); |
| 721 return true; |
| 722 }; |
| 723 |
| 724 /** @return {boolean} */ |
| 725 WebViewInternal.prototype.parseAttributes = function() { |
| 726 var hasNavigated = this.hasNavigated(); |
| 727 var attributeValue = this.webviewNode.getAttribute('partition'); |
| 728 var result = this.partition.fromAttribute(attributeValue, hasNavigated); |
| 729 return this.parseSrcAttribute(result); |
| 730 }; |
| 731 |
| 732 WebViewInternal.prototype.hasGuestInstanceID = function() { |
| 733 return this.instanceId != undefined; |
| 734 }; |
| 735 |
| 736 WebViewInternal.prototype.allocateInstanceId = function() { |
| 737 // Parse .src and .partition. |
| 738 var self = this; |
| 739 GuestView.allocateInstanceId( |
| 740 function(instanceId) { |
| 741 self.instanceId = instanceId; |
| 742 // TODO(lazyboy): Make sure this.autoNavigate_ stuff correctly updated |
| 743 // |self.src| at this point. |
| 744 self.attachWindowAndSetUpEvents(self.instanceId, self.src); |
| 745 }); |
| 746 }; |
| 747 |
600 /** | 748 /** |
601 * @private | 749 * @private |
602 */ | 750 */ |
603 WebViewInternal.prototype.setupWebviewNodeEvents = function() { | 751 WebViewInternal.prototype.setupWebviewNodeEvents = function() { |
604 var self = this; | |
605 this.viewInstanceId = IdGenerator.GetNextId(); | |
606 var onInstanceIdAllocated = function(e) { | |
607 var detail = e.detail ? JSON.parse(e.detail) : {}; | |
608 self.attachWindowAndSetUpEvents(detail.windowId); | |
609 }; | |
610 this.browserPluginNode.addEventListener('-internal-instanceid-allocated', | |
611 onInstanceIdAllocated); | |
612 this.setupWebRequestEvents(); | 752 this.setupWebRequestEvents(); |
613 this.setupExperimentalContextMenus_(); | 753 this.setupExperimentalContextMenus_(); |
614 | 754 |
615 this.on = {}; | 755 this.on = {}; |
616 var events = self.getEvents(); | 756 var events = this.getEvents(); |
617 for (var eventName in events) { | 757 for (var eventName in events) { |
618 this.setupEventProperty(eventName); | 758 this.setupEventProperty(eventName); |
619 } | 759 } |
620 }; | 760 }; |
621 | 761 |
622 /** | 762 /** |
623 * @private | 763 * @private |
624 */ | 764 */ |
625 WebViewInternal.prototype.setupNameAttribute = function() { | 765 WebViewInternal.prototype.setupNameAttribute = function() { |
626 var self = this; | 766 var self = this; |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
838 validateCall(); | 978 validateCall(); |
839 if (!webview || !webview.tagName || webview.tagName != 'WEBVIEW') | 979 if (!webview || !webview.tagName || webview.tagName != 'WEBVIEW') |
840 throw new Error(ERROR_MSG_WEBVIEW_EXPECTED); | 980 throw new Error(ERROR_MSG_WEBVIEW_EXPECTED); |
841 // Attach happens asynchronously to give the tagWatcher an opportunity | 981 // Attach happens asynchronously to give the tagWatcher an opportunity |
842 // to pick up the new webview before attach operates on it, if it hasn't | 982 // to pick up the new webview before attach operates on it, if it hasn't |
843 // been attached to the DOM already. | 983 // been attached to the DOM already. |
844 // Note: Any subsequent errors cannot be exceptions because they happen | 984 // Note: Any subsequent errors cannot be exceptions because they happen |
845 // asynchronously. | 985 // asynchronously. |
846 setTimeout(function() { | 986 setTimeout(function() { |
847 var webViewInternal = privates(webview).internal; | 987 var webViewInternal = privates(webview).internal; |
| 988 if (event.storagePartitionId) { |
| 989 webViewInternal.webviewNode.setAttribute('partition', |
| 990 event.storagePartitionId); |
| 991 var partition = new Partition(); |
| 992 partition.fromAttribute(event.storagePartitionId, |
| 993 webViewInternal.hasNavigated()); |
| 994 webViewInternal.partition = partition; |
| 995 } |
| 996 |
848 var attached = | 997 var attached = |
849 webViewInternal.attachWindowAndSetUpEvents(event.windowId); | 998 webViewInternal.attachWindowAndSetUpEvents( |
| 999 event.windowId, undefined, event.storagePartitionId); |
850 | 1000 |
851 if (!attached) { | 1001 if (!attached) { |
852 window.console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH); | 1002 window.console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH); |
853 } | 1003 } |
854 // If the object being passed into attach is not a valid <webview> | 1004 // If the object being passed into attach is not a valid <webview> |
855 // then we will fail and it will be treated as if the new window | 1005 // then we will fail and it will be treated as if the new window |
856 // was rejected. The permission API plumbing is used here to clean | 1006 // was rejected. The permission API plumbing is used here to clean |
857 // up the state created for the new window if attaching fails. | 1007 // up the state created for the new window if attaching fails. |
858 WebView.setPermission( | 1008 WebView.setPermission( |
859 self.instanceId, requestId, attached ? 'allow' : 'deny'); | 1009 self.instanceId, requestId, attached ? 'allow' : 'deny'); |
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1093 this.userAgentOverride = userAgentOverride; | 1243 this.userAgentOverride = userAgentOverride; |
1094 if (!this.instanceId) { | 1244 if (!this.instanceId) { |
1095 // If we are not attached yet, then we will pick up the user agent on | 1245 // If we are not attached yet, then we will pick up the user agent on |
1096 // attachment. | 1246 // attachment. |
1097 return; | 1247 return; |
1098 } | 1248 } |
1099 WebView.overrideUserAgent(this.instanceId, userAgentOverride); | 1249 WebView.overrideUserAgent(this.instanceId, userAgentOverride); |
1100 }; | 1250 }; |
1101 | 1251 |
1102 /** @private */ | 1252 /** @private */ |
1103 WebViewInternal.prototype.attachWindowAndSetUpEvents = function(instanceId) { | 1253 WebViewInternal.prototype.attachWindowAndSetUpEvents = function( |
| 1254 instanceId, opt_src, opt_partitionId) { |
1104 this.instanceId = instanceId; | 1255 this.instanceId = instanceId; |
| 1256 // If we have a partition from the opener, use that instead. |
| 1257 var storagePartitionId = |
| 1258 opt_partitionId || |
| 1259 this.webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) || |
| 1260 this.webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION]; |
1105 var params = { | 1261 var params = { |
1106 'api': 'webview', | 1262 'api': 'webview', |
1107 'instanceId': this.viewInstanceId, | 1263 'instanceId': this.viewInstanceId, |
1108 'name': this.name | 1264 'name': this.name, |
| 1265 'src': opt_src, |
| 1266 'storagePartitionId': storagePartitionId, |
| 1267 'userAgentOverride': this.userAgentOverride |
1109 }; | 1268 }; |
1110 if (this.userAgentOverride) { | |
1111 params['userAgentOverride'] = this.userAgentOverride; | |
1112 } | |
1113 this.setupNameAttribute(); | 1269 this.setupNameAttribute(); |
1114 var events = this.getEvents(); | 1270 var events = this.getEvents(); |
1115 for (var eventName in events) { | 1271 for (var eventName in events) { |
1116 this.setupEvent(eventName, events[eventName]); | 1272 this.setupEvent(eventName, events[eventName]); |
1117 } | 1273 } |
1118 | 1274 |
1119 return this.browserPluginNode['-internal-attach'](this.instanceId, params); | 1275 return this.browserPluginNode['-internal-attach'](this.instanceId, params); |
1120 }; | 1276 }; |
1121 | 1277 |
1122 // Registers browser plugin <object> custom element. | 1278 // Registers browser plugin <object> custom element. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1154 } | 1310 } |
1155 | 1311 |
1156 // Registers <webview> custom element. | 1312 // Registers <webview> custom element. |
1157 function registerWebViewElement() { | 1313 function registerWebViewElement() { |
1158 var proto = Object.create(HTMLElement.prototype); | 1314 var proto = Object.create(HTMLElement.prototype); |
1159 | 1315 |
1160 proto.createdCallback = function() { | 1316 proto.createdCallback = function() { |
1161 new WebViewInternal(this); | 1317 new WebViewInternal(this); |
1162 }; | 1318 }; |
1163 | 1319 |
| 1320 proto.customElementDetached = false; |
| 1321 |
1164 proto.attributeChangedCallback = function(name, oldValue, newValue) { | 1322 proto.attributeChangedCallback = function(name, oldValue, newValue) { |
1165 var internal = privates(this).internal; | 1323 var internal = privates(this).internal; |
1166 if (!internal) { | 1324 if (!internal) { |
1167 return; | 1325 return; |
1168 } | 1326 } |
1169 internal.handleWebviewAttributeMutation(name, oldValue, newValue); | 1327 internal.handleWebviewAttributeMutation(name, oldValue, newValue); |
1170 }; | 1328 }; |
1171 | 1329 |
| 1330 proto.detachedCallback = function() { |
| 1331 this.customElementDetached = true; |
| 1332 }; |
| 1333 |
| 1334 proto.attachedCallback = function() { |
| 1335 if (this.customElementDetached) { |
| 1336 var webViewInternal = privates(this).internal; |
| 1337 webViewInternal.resetUponReattachment(); |
| 1338 webViewInternal.allocateInstanceId(); |
| 1339 } |
| 1340 this.customElementDetached = false; |
| 1341 }; |
| 1342 |
1172 proto.back = function() { | 1343 proto.back = function() { |
1173 this.go(-1); | 1344 this.go(-1); |
1174 }; | 1345 }; |
1175 | 1346 |
1176 proto.forward = function() { | 1347 proto.forward = function() { |
1177 this.go(1); | 1348 this.go(1); |
1178 }; | 1349 }; |
1179 | 1350 |
1180 proto.canGoBack = function() { | 1351 proto.canGoBack = function() { |
1181 return privates(this).internal.canGoBack(); | 1352 return privates(this).internal.canGoBack(); |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1285 | 1456 |
1286 /** | 1457 /** |
1287 * Implemented when the experimental API is available. | 1458 * Implemented when the experimental API is available. |
1288 * @private | 1459 * @private |
1289 */ | 1460 */ |
1290 WebViewInternal.prototype.setupExperimentalContextMenus_ = function() {}; | 1461 WebViewInternal.prototype.setupExperimentalContextMenus_ = function() {}; |
1291 | 1462 |
1292 exports.WebView = WebView; | 1463 exports.WebView = WebView; |
1293 exports.WebViewInternal = WebViewInternal; | 1464 exports.WebViewInternal = WebViewInternal; |
1294 exports.CreateEvent = CreateEvent; | 1465 exports.CreateEvent = CreateEvent; |
OLD | NEW |