Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(456)

Side by Side Diff: chrome/renderer/resources/extensions/web_view.js

Issue 333713005: Move event stuff of web_view.js to its own class/file. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: move rest of the events Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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');
11 var GuestViewInternal = 10 var GuestViewInternal =
12 require('binding').Binding.create('guestViewInternal').generate(); 11 require('binding').Binding.create('guestViewInternal').generate();
13 var IdGenerator = requireNative('id_generator'); 12 var IdGenerator = requireNative('id_generator');
14 var MessagingNatives = requireNative('messaging_natives');
15 var WebRequestEvent = require('webRequestInternal').WebRequestEvent;
16 var WebRequestSchema =
17 requireNative('schema_registry').GetSchema('webRequest');
18 var DeclarativeWebRequestSchema =
19 requireNative('schema_registry').GetSchema('declarativeWebRequest');
20 var WebView = require('webview').WebView; 13 var WebView = require('webview').WebView;
14 var WebViewEvents = require('webViewEvents').WebViewEvents;
21 15
22 var WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight'; 16 var WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight';
23 var WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth'; 17 var WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth';
24 var WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight'; 18 var WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight';
25 var WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth'; 19 var WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth';
26 var WEB_VIEW_ATTRIBUTE_PARTITION = 'partition'; 20 var WEB_VIEW_ATTRIBUTE_PARTITION = 'partition';
27 21
28 var ERROR_MSG_ALREADY_NAVIGATED = 22 var ERROR_MSG_ALREADY_NAVIGATED =
29 'The object has already navigated, so its partition cannot be changed.'; 23 'The object has already navigated, so its partition cannot be changed.';
30 var ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.'; 24 var ERROR_MSG_INVALID_PARTITION_ATTRIBUTE = 'Invalid partition attribute.';
31 25
32 /** @type {Array.<string>} */ 26 /** @type {Array.<string>} */
33 var WEB_VIEW_ATTRIBUTES = [ 27 var WEB_VIEW_ATTRIBUTES = [
34 'allowtransparency', 28 'allowtransparency',
35 'autosize', 29 'autosize',
36 WEB_VIEW_ATTRIBUTE_MINHEIGHT, 30 WEB_VIEW_ATTRIBUTE_MINHEIGHT,
37 WEB_VIEW_ATTRIBUTE_MINWIDTH, 31 WEB_VIEW_ATTRIBUTE_MINWIDTH,
38 WEB_VIEW_ATTRIBUTE_MAXHEIGHT, 32 WEB_VIEW_ATTRIBUTE_MAXHEIGHT,
39 WEB_VIEW_ATTRIBUTE_MAXWIDTH 33 WEB_VIEW_ATTRIBUTE_MAXWIDTH
40 ]; 34 ];
41 35
42 /** @class representing state of storage partition. */ 36 /** @class representing state of storage partition. */
43 function Partition() { 37 function Partition() {
44 this.validPartitionId = true; 38 this.validPartitionId = true;
45 this.persist_storage_ = false; 39 this.persistStorage = false;
46 this.storage_partition_id = ''; 40 this.storagePartitionId = '';
47 }; 41 };
48 42
49 Partition.prototype.toAttribute = function() { 43 Partition.prototype.toAttribute = function() {
50 if (!this.validPartitionId) { 44 if (!this.validPartitionId) {
51 return ''; 45 return '';
52 } 46 }
53 return (this.persist_storage_ ? 'persist:' : '') + this.storage_partition_id; 47 return (this.persistStorage ? 'persist:' : '') + this.storagePartitionId;
54 }; 48 };
55 49
56 Partition.prototype.fromAttribute = function(value, hasNavigated) { 50 Partition.prototype.fromAttribute = function(value, hasNavigated) {
57 var result = {}; 51 var result = {};
58 if (hasNavigated) { 52 if (hasNavigated) {
59 result.error = ERROR_MSG_ALREADY_NAVIGATED; 53 result.error = ERROR_MSG_ALREADY_NAVIGATED;
60 return result; 54 return result;
61 } 55 }
62 if (!value) { 56 if (!value) {
63 value = ''; 57 value = '';
64 } 58 }
65 59
66 var LEN = 'persist:'.length; 60 var LEN = 'persist:'.length;
67 if (value.substr(0, LEN) == 'persist:') { 61 if (value.substr(0, LEN) == 'persist:') {
68 value = value.substr(LEN); 62 value = value.substr(LEN);
69 if (!value) { 63 if (!value) {
70 this.validPartitionId = false; 64 this.validPartitionId = false;
71 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; 65 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE;
72 return result; 66 return result;
73 } 67 }
74 this.persist_storage_ = true; 68 this.persistStorage = true;
75 } else { 69 } else {
76 this.persist_storage_ = false; 70 this.persistStorage = false;
77 } 71 }
78 72
79 this.storage_partition_id = value; 73 this.storagePartitionId = value;
80 return result; 74 return result;
81 }; 75 };
82 76
83 var CreateEvent = function(name) {
84 var eventOpts = {supportsListeners: true, supportsFilters: true};
85 return new EventBindings.Event(name, undefined, eventOpts);
86 };
87
88 // WEB_VIEW_EVENTS is a map of stable <webview> DOM event names to their
89 // associated extension event descriptor objects.
90 // An event listener will be attached to the extension event |evt| specified in
91 // the descriptor.
92 // |fields| specifies the public-facing fields in the DOM event that are
93 // accessible to <webview> developers.
94 // |customHandler| allows a handler function to be called each time an extension
95 // event is caught by its event listener. The DOM event should be dispatched
96 // within this handler function. With no handler function, the DOM event
97 // will be dispatched by default each time the extension event is caught.
98 // |cancelable| (default: false) specifies whether the event's default
99 // behavior can be canceled. If the default action associated with the event
100 // is prevented, then its dispatch function will return false in its event
101 // handler. The event must have a custom handler for this to be meaningful.
102
103 var FrameNameChangedEvent = CreateEvent('webview.onFrameNameChanged');
104
105 var WEB_VIEW_EVENTS = {
106 'close': {
107 evt: CreateEvent('webview.onClose'),
108 fields: []
109 },
110 'consolemessage': {
111 evt: CreateEvent('webview.onConsoleMessage'),
112 fields: ['level', 'message', 'line', 'sourceId']
113 },
114 'contentload': {
115 evt: CreateEvent('webview.onContentLoad'),
116 fields: []
117 },
118 'contextmenu': {
119 evt: CreateEvent('webview.contextmenu'),
120 cancelable: true,
121 customHandler: function(webViewInternal, event, webViewEvent) {
122 webViewInternal.maybeHandleContextMenu(event, webViewEvent);
123 },
124 fields: ['items']
125 },
126 'dialog': {
127 cancelable: true,
128 customHandler: function(webViewInternal, event, webViewEvent) {
129 webViewInternal.handleDialogEvent(event, webViewEvent);
130 },
131 evt: CreateEvent('webview.onDialog'),
132 fields: ['defaultPromptText', 'messageText', 'messageType', 'url']
133 },
134 'exit': {
135 evt: CreateEvent('webview.onExit'),
136 fields: ['processId', 'reason']
137 },
138 'loadabort': {
139 cancelable: true,
140 customHandler: function(webViewInternal, event, webViewEvent) {
141 webViewInternal.handleLoadAbortEvent(event, webViewEvent);
142 },
143 evt: CreateEvent('webview.onLoadAbort'),
144 fields: ['url', 'isTopLevel', 'reason']
145 },
146 'loadcommit': {
147 customHandler: function(webViewInternal, event, webViewEvent) {
148 webViewInternal.handleLoadCommitEvent(event, webViewEvent);
149 },
150 evt: CreateEvent('webview.onLoadCommit'),
151 fields: ['url', 'isTopLevel']
152 },
153 'loadprogress': {
154 evt: CreateEvent('webview.onLoadProgress'),
155 fields: ['url', 'progress']
156 },
157 'loadredirect': {
158 evt: CreateEvent('webview.onLoadRedirect'),
159 fields: ['isTopLevel', 'oldUrl', 'newUrl']
160 },
161 'loadstart': {
162 evt: CreateEvent('webview.onLoadStart'),
163 fields: ['url', 'isTopLevel']
164 },
165 'loadstop': {
166 evt: CreateEvent('webview.onLoadStop'),
167 fields: []
168 },
169 'newwindow': {
170 cancelable: true,
171 customHandler: function(webViewInternal, event, webViewEvent) {
172 webViewInternal.handleNewWindowEvent(event, webViewEvent);
173 },
174 evt: CreateEvent('webview.onNewWindow'),
175 fields: [
176 'initialHeight',
177 'initialWidth',
178 'targetUrl',
179 'windowOpenDisposition',
180 'name'
181 ]
182 },
183 'permissionrequest': {
184 cancelable: true,
185 customHandler: function(webViewInternal, event, webViewEvent) {
186 webViewInternal.handlePermissionEvent(event, webViewEvent);
187 },
188 evt: CreateEvent('webview.onPermissionRequest'),
189 fields: [
190 'identifier',
191 'lastUnlockedBySelf',
192 'name',
193 'permission',
194 'requestMethod',
195 'url',
196 'userGesture'
197 ]
198 },
199 'responsive': {
200 evt: CreateEvent('webview.onResponsive'),
201 fields: ['processId']
202 },
203 'sizechanged': {
204 evt: CreateEvent('webview.onSizeChanged'),
205 customHandler: function(webViewInternal, event, webViewEvent) {
206 webViewInternal.handleSizeChangedEvent(event, webViewEvent);
207 },
208 fields: ['oldHeight', 'oldWidth', 'newHeight', 'newWidth']
209 },
210 'unresponsive': {
211 evt: CreateEvent('webview.onUnresponsive'),
212 fields: ['processId']
213 }
214 };
215
216 // Implemented when the experimental API is available. 77 // Implemented when the experimental API is available.
217 WebViewInternal.maybeRegisterExperimentalAPIs = function(proto) {} 78 WebViewInternal.maybeRegisterExperimentalAPIs = function(proto) {}
218 79
219 /** 80 /**
220 * @constructor 81 * @constructor
221 */ 82 */
222 function WebViewInternal(webviewNode) { 83 function WebViewInternal(webviewNode) {
223 privates(webviewNode).internal = this; 84 privates(webviewNode).internal = this;
224 this.webviewNode = webviewNode; 85 this.webviewNode = webviewNode;
225 this.attached = false; 86 this.attached = false;
226 87
227 this.beforeFirstNavigation = true; 88 this.beforeFirstNavigation = true;
228 this.validPartitionId = true; 89 this.validPartitionId = true;
229 90
91 // on* Event handlers.
92 this.on = {};
93
230 this.browserPluginNode = this.createBrowserPluginNode(); 94 this.browserPluginNode = this.createBrowserPluginNode();
231 var shadowRoot = this.webviewNode.createShadowRoot(); 95 var shadowRoot = this.webviewNode.createShadowRoot();
232 shadowRoot.appendChild(this.browserPluginNode); 96 shadowRoot.appendChild(this.browserPluginNode);
233 97
234 this.setupWebviewNodeAttributes(); 98 this.setupWebviewNodeAttributes();
235 this.setupFocusPropagation(); 99 this.setupFocusPropagation();
236 this.setupWebviewNodeProperties(); 100 this.setupWebviewNodeProperties();
237 101
238 this.viewInstanceId = IdGenerator.GetNextId(); 102 this.viewInstanceId = IdGenerator.GetNextId();
239 103
240 this.partition = new Partition(); 104 this.partition = new Partition();
241 this.parseAttributes(); 105 this.parseAttributes();
242 106
243 this.setupWebviewNodeEvents(); 107 new WebViewEvents(this, this.viewInstanceId);
244 } 108 }
245 109
246 /** 110 /**
247 * @private 111 * @private
248 */ 112 */
249 WebViewInternal.prototype.createBrowserPluginNode = function() { 113 WebViewInternal.prototype.createBrowserPluginNode = function() {
250 // We create BrowserPlugin as a custom element in order to observe changes 114 // We create BrowserPlugin as a custom element in order to observe changes
251 // to attributes synchronously. 115 // to attributes synchronously.
252 var browserPluginNode = new WebViewInternal.BrowserPlugin(); 116 var browserPluginNode = new WebViewInternal.BrowserPlugin();
253 privates(browserPluginNode).internal = this; 117 privates(browserPluginNode).internal = this;
(...skipping 10 matching lines...) Expand all
264 // window.DOMContentLoaded event). 128 // window.DOMContentLoaded event).
265 // So copy from property if copying from attribute fails. 129 // So copy from property if copying from attribute fails.
266 browserPluginNode.setAttribute( 130 browserPluginNode.setAttribute(
267 attributeName, this.webviewNode[attributeName]); 131 attributeName, this.webviewNode[attributeName]);
268 } 132 }
269 }, this); 133 }, this);
270 134
271 return browserPluginNode; 135 return browserPluginNode;
272 }; 136 };
273 137
138 WebViewInternal.prototype.getInstanceId = function() {
139 return this.instanceId;
140 };
141
274 /** 142 /**
275 * @private
276 * Resets some state upon reattaching <webview> element to the DOM. 143 * Resets some state upon reattaching <webview> element to the DOM.
277 */ 144 */
278 WebViewInternal.prototype.resetUponReattachment = function() { 145 WebViewInternal.prototype.resetUponReattachment = function() {
279 this.instanceId = undefined; 146 this.instanceId = undefined;
280 this.beforeFirstNavigation = true; 147 this.beforeFirstNavigation = true;
281 this.validPartitionId = true; 148 this.validPartitionId = true;
282 this.partition.validPartitionId = true; 149 this.partition.validPartitionId = true;
283 }; 150 };
284 151
285 /** 152 // Sets <webview>.request property.
286 * @private 153 WebViewInternal.prototype.setRequestPropertyOnWebViewNode = function(request) {
287 */ 154 Object.defineProperty(
155 this.webviewNode,
156 'request',
157 {
158 value: request,
159 enumerable: true
160 }
161 );
162 };
163
288 WebViewInternal.prototype.setupFocusPropagation = function() { 164 WebViewInternal.prototype.setupFocusPropagation = function() {
289 if (!this.webviewNode.hasAttribute('tabIndex')) { 165 if (!this.webviewNode.hasAttribute('tabIndex')) {
290 // <webview> needs a tabIndex in order to be focusable. 166 // <webview> needs a tabIndex in order to be focusable.
291 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute 167 // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
292 // to allow <webview> to be focusable. 168 // to allow <webview> to be focusable.
293 // See http://crbug.com/231664. 169 // See http://crbug.com/231664.
294 this.webviewNode.setAttribute('tabIndex', -1); 170 this.webviewNode.setAttribute('tabIndex', -1);
295 } 171 }
296 var self = this; 172 var self = this;
297 this.webviewNode.addEventListener('focus', function(e) { 173 this.webviewNode.addEventListener('focus', function(e) {
(...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after
633 if (newValue != this.webviewNode.getAttribute(name)) { 509 if (newValue != this.webviewNode.getAttribute(name)) {
634 this.webviewNode.setAttribute(name, newValue); 510 this.webviewNode.setAttribute(name, newValue);
635 } 511 }
636 } else { 512 } else {
637 // If an attribute is removed from the BrowserPlugin, then remove it 513 // If an attribute is removed from the BrowserPlugin, then remove it
638 // from the <webview> as well. 514 // from the <webview> as well.
639 this.webviewNode.removeAttribute(name); 515 this.webviewNode.removeAttribute(name);
640 } 516 }
641 }; 517 };
642 518
643 /** 519 WebViewInternal.prototype.onSizeChanged = function(newWidth, newHeight) {
644 * @private
645 */
646 WebViewInternal.prototype.getEvents = function() {
647 var experimentalEvents = this.maybeGetExperimentalEvents();
648 for (var eventName in experimentalEvents) {
649 WEB_VIEW_EVENTS[eventName] = experimentalEvents[eventName];
650 }
651 return WEB_VIEW_EVENTS;
652 };
653
654 WebViewInternal.prototype.handleSizeChangedEvent =
655 function(event, webViewEvent) {
656 var node = this.webviewNode; 520 var node = this.webviewNode;
657 521
658 var width = node.offsetWidth; 522 var width = node.offsetWidth;
659 var height = node.offsetHeight; 523 var height = node.offsetHeight;
660 524
661 // Check the current bounds to make sure we do not resize <webview> 525 // Check the current bounds to make sure we do not resize <webview>
662 // outside of current constraints. 526 // outside of current constraints.
663 var maxWidth; 527 var maxWidth;
664 if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXWIDTH) && 528 if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXWIDTH) &&
665 node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]) { 529 node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]) {
(...skipping 24 matching lines...) Expand all
690 if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINHEIGHT) && 554 if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINHEIGHT) &&
691 node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]) { 555 node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]) {
692 minHeight = node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]; 556 minHeight = node[WEB_VIEW_ATTRIBUTE_MINHEIGHT];
693 } else { 557 } else {
694 minHeight = height; 558 minHeight = height;
695 } 559 }
696 if (minHeight > maxHeight) { 560 if (minHeight > maxHeight) {
697 minHeight = maxHeight; 561 minHeight = maxHeight;
698 } 562 }
699 563
700 if (webViewEvent.newWidth >= minWidth && 564 if (newWidth >= minWidth &&
701 webViewEvent.newWidth <= maxWidth && 565 newWidth <= maxWidth &&
702 webViewEvent.newHeight >= minHeight && 566 newHeight >= minHeight &&
703 webViewEvent.newHeight <= maxHeight) { 567 newHeight <= maxHeight) {
704 node.style.width = webViewEvent.newWidth + 'px'; 568 node.style.width = newWidth + 'px';
705 node.style.height = webViewEvent.newHeight + 'px'; 569 node.style.height = newHeight + 'px';
706 } 570 }
707 node.dispatchEvent(webViewEvent);
708 }; 571 };
709 572
710 WebViewInternal.prototype.hasNavigated = function() { 573 WebViewInternal.prototype.hasNavigated = function() {
711 return !this.beforeFirstNavigation; 574 return !this.beforeFirstNavigation;
712 }; 575 };
713 576
714 /** @return {boolean} */ 577 /** @return {boolean} */
715 WebViewInternal.prototype.parseSrcAttribute = function(result) { 578 WebViewInternal.prototype.parseSrcAttribute = function(result) {
716 if (!this.partition.validPartitionId) { 579 if (!this.partition.validPartitionId) {
717 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE; 580 result.error = ERROR_MSG_INVALID_PARTITION_ATTRIBUTE;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
753 var self = this; 616 var self = this;
754 GuestViewInternal.allocateInstanceId( 617 GuestViewInternal.allocateInstanceId(
755 function(instanceId) { 618 function(instanceId) {
756 self.instanceId = instanceId; 619 self.instanceId = instanceId;
757 // TODO(lazyboy): Make sure this.autoNavigate_ stuff correctly updated 620 // TODO(lazyboy): Make sure this.autoNavigate_ stuff correctly updated
758 // |self.src| at this point. 621 // |self.src| at this point.
759 self.attachWindowAndSetUpEvents(self.instanceId, self.src); 622 self.attachWindowAndSetUpEvents(self.instanceId, self.src);
760 }); 623 });
761 }; 624 };
762 625
763 /** 626 WebViewInternal.prototype.onFrameNameChanged = function(name) {
764 * @private 627 this.name = name || '';
765 */ 628 if (this.name === '') {
766 WebViewInternal.prototype.setupWebviewNodeEvents = function() { 629 this.webviewNode.removeAttribute('name');
767 this.setupWebRequestEvents(); 630 } else {
768 this.setupExperimentalContextMenus_(); 631 this.webviewNode.setAttribute('name', this.name);
769
770 this.on = {};
771 var events = this.getEvents();
772 for (var eventName in events) {
773 this.setupEventProperty(eventName);
774 } 632 }
775 }; 633 };
776 634
777 /** 635 WebViewInternal.prototype.dispatchEvent = function(webViewEvent) {
778 * @private 636 return this.webviewNode.dispatchEvent(webViewEvent);
779 */
780 WebViewInternal.prototype.setupNameAttribute = function() {
781 var self = this;
782 FrameNameChangedEvent.addListener(function(event) {
783 self.name = event.name || '';
784 if (self.name === '') {
785 self.webviewNode.removeAttribute('name');
786 } else {
787 self.webviewNode.setAttribute('name', self.name);
788 }
789 }, {instanceId: self.instanceId});
790 };
791
792 /**
793 * @private
794 */
795 WebViewInternal.prototype.setupEvent = function(eventName, eventInfo) {
796 var self = this;
797 var webviewNode = this.webviewNode;
798 eventInfo.evt.addListener(function(event) {
799 var details = {bubbles:true};
800 if (eventInfo.cancelable)
801 details.cancelable = true;
802 var webViewEvent = new Event(eventName, details);
803 $Array.forEach(eventInfo.fields, function(field) {
804 if (event[field] !== undefined) {
805 webViewEvent[field] = event[field];
806 }
807 });
808 if (eventInfo.customHandler) {
809 eventInfo.customHandler(self, event, webViewEvent);
810 return;
811 }
812 webviewNode.dispatchEvent(webViewEvent);
813 }, {instanceId: self.instanceId});
814 }; 637 };
815 638
816 /** 639 /**
817 * Adds an 'on<event>' property on the webview, which can be used to set/unset 640 * Adds an 'on<event>' property on the webview, which can be used to set/unset
818 * an event handler. 641 * an event handler.
819 * @private
820 */ 642 */
821 WebViewInternal.prototype.setupEventProperty = function(eventName) { 643 WebViewInternal.prototype.setupEventProperty = function(eventName) {
822 var propertyName = 'on' + eventName.toLowerCase(); 644 var propertyName = 'on' + eventName.toLowerCase();
823 var self = this; 645 var self = this;
824 var webviewNode = this.webviewNode; 646 var webviewNode = this.webviewNode;
825 Object.defineProperty(webviewNode, propertyName, { 647 Object.defineProperty(webviewNode, propertyName, {
826 get: function() { 648 get: function() {
827 return self.on[propertyName]; 649 return self.on[propertyName];
828 }, 650 },
829 set: function(value) { 651 set: function(value) {
830 if (self.on[propertyName]) 652 if (self.on[propertyName])
831 webviewNode.removeEventListener(eventName, self.on[propertyName]); 653 webviewNode.removeEventListener(eventName, self.on[propertyName]);
832 self.on[propertyName] = value; 654 self.on[propertyName] = value;
833 if (value) 655 if (value)
834 webviewNode.addEventListener(eventName, value); 656 webviewNode.addEventListener(eventName, value);
835 }, 657 },
836 enumerable: true 658 enumerable: true
837 }); 659 });
838 }; 660 };
839 661
840 /** 662 // Updates state upon loadcommit.
841 * @private 663 WebViewInternal.prototype.onLoadCommit = function(
842 */ 664 currentEntryIndex, entryCount, processId, url, isTopLevel) {
843 WebViewInternal.prototype.getPermissionTypes = function() { 665 this.currentEntryIndex = currentEntryIndex;
844 var permissions = 666 this.entryCount = entryCount;
845 ['media', 667 this.processId = processId;
846 'geolocation',
847 'pointerLock',
848 'download',
849 'loadplugin',
850 'filesystem'];
851 return permissions.concat(this.maybeGetExperimentalPermissions());
852 };
853
854 /**
855 * @private
856 */
857 WebViewInternal.prototype.handleDialogEvent =
858 function(event, webViewEvent) {
859 var showWarningMessage = function(dialogType) {
860 var VOWELS = ['a', 'e', 'i', 'o', 'u'];
861 var WARNING_MSG_DIALOG_BLOCKED = '<webview>: %1 %2 dialog was blocked.';
862 var article = (VOWELS.indexOf(dialogType.charAt(0)) >= 0) ? 'An' : 'A';
863 var output = WARNING_MSG_DIALOG_BLOCKED.replace('%1', article);
864 output = output.replace('%2', dialogType);
865 window.console.warn(output);
866 };
867
868 var self = this;
869 var webviewNode = this.webviewNode;
870 var requestId = event.requestId;
871 var actionTaken = false;
872
873 var validateCall = function() {
874 var ERROR_MSG_DIALOG_ACTION_ALREADY_TAKEN = '<webview>: ' +
875 'An action has already been taken for this "dialog" event.';
876
877 if (actionTaken) {
878 throw new Error(ERROR_MSG_DIALOG_ACTION_ALREADY_TAKEN);
879 }
880 actionTaken = true;
881 };
882
883 var dialog = {
884 ok: function(user_input) {
885 validateCall();
886 user_input = user_input || '';
887 WebView.setPermission(self.instanceId, requestId, 'allow', user_input);
888 },
889 cancel: function() {
890 validateCall();
891 WebView.setPermission(self.instanceId, requestId, 'deny');
892 }
893 };
894 webViewEvent.dialog = dialog;
895
896 var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent);
897 if (actionTaken) {
898 return;
899 }
900
901 if (defaultPrevented) {
902 // Tell the JavaScript garbage collector to track lifetime of |dialog| and
903 // call back when the dialog object has been collected.
904 MessagingNatives.BindToGC(dialog, function() {
905 // Avoid showing a warning message if the decision has already been made.
906 if (actionTaken) {
907 return;
908 }
909 WebView.setPermission(
910 self.instanceId, requestId, 'default', '', function(allowed) {
911 if (allowed) {
912 return;
913 }
914 showWarningMessage(event.messageType);
915 });
916 });
917 } else {
918 actionTaken = true;
919 // The default action is equivalent to canceling the dialog.
920 WebView.setPermission(
921 self.instanceId, requestId, 'default', '', function(allowed) {
922 if (allowed) {
923 return;
924 }
925 showWarningMessage(event.messageType);
926 });
927 }
928 };
929
930 /**
931 * @private
932 */
933 WebViewInternal.prototype.handleLoadAbortEvent =
934 function(event, webViewEvent) {
935 var showWarningMessage = function(reason) {
936 var WARNING_MSG_LOAD_ABORTED = '<webview>: ' +
937 'The load has aborted with reason "%1".';
938 window.console.warn(WARNING_MSG_LOAD_ABORTED.replace('%1', reason));
939 };
940 if (this.webviewNode.dispatchEvent(webViewEvent)) {
941 showWarningMessage(event.reason);
942 }
943 };
944
945 /**
946 * @private
947 */
948 WebViewInternal.prototype.handleLoadCommitEvent =
949 function(event, webViewEvent) {
950 this.currentEntryIndex = event.currentEntryIndex;
951 this.entryCount = event.entryCount;
952 this.processId = event.processId;
953 var oldValue = this.webviewNode.getAttribute('src'); 668 var oldValue = this.webviewNode.getAttribute('src');
954 var newValue = event.url; 669 var newValue = url;
955 if (event.isTopLevel && (oldValue != newValue)) { 670 if (isTopLevel && (oldValue != newValue)) {
956 // Touching the src attribute triggers a navigation. To avoid 671 // Touching the src attribute triggers a navigation. To avoid
957 // triggering a page reload on every guest-initiated navigation, 672 // triggering a page reload on every guest-initiated navigation,
958 // we use the flag ignoreNextSrcAttributeChange here. 673 // we use the flag ignoreNextSrcAttributeChange here.
959 this.ignoreNextSrcAttributeChange = true; 674 this.ignoreNextSrcAttributeChange = true;
960 this.webviewNode.setAttribute('src', newValue); 675 this.webviewNode.setAttribute('src', newValue);
961 } 676 }
962 this.webviewNode.dispatchEvent(webViewEvent);
963 }
964
965 /**
966 * @private
967 */
968 WebViewInternal.prototype.handleNewWindowEvent =
969 function(event, webViewEvent) {
970 var ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN = '<webview>: ' +
971 'An action has already been taken for this "newwindow" event.';
972
973 var ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH = '<webview>: ' +
974 'Unable to attach the new window to the provided webview.';
975
976 var ERROR_MSG_WEBVIEW_EXPECTED = '<webview> element expected.';
977
978 var showWarningMessage = function() {
979 var WARNING_MSG_NEWWINDOW_BLOCKED = '<webview>: A new window was blocked.';
980 window.console.warn(WARNING_MSG_NEWWINDOW_BLOCKED);
981 };
982
983 var self = this;
984 var webviewNode = this.webviewNode;
985
986 var requestId = event.requestId;
987 var actionTaken = false;
988
989 var validateCall = function () {
990 if (actionTaken) {
991 throw new Error(ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN);
992 }
993 actionTaken = true;
994 };
995
996 var windowObj = {
997 attach: function(webview) {
998 validateCall();
999 if (!webview || !webview.tagName || webview.tagName != 'WEBVIEW')
1000 throw new Error(ERROR_MSG_WEBVIEW_EXPECTED);
1001 // Attach happens asynchronously to give the tagWatcher an opportunity
1002 // to pick up the new webview before attach operates on it, if it hasn't
1003 // been attached to the DOM already.
1004 // Note: Any subsequent errors cannot be exceptions because they happen
1005 // asynchronously.
1006 setTimeout(function() {
1007 var webViewInternal = privates(webview).internal;
1008 if (event.storagePartitionId) {
1009 webViewInternal.webviewNode.setAttribute('partition',
1010 event.storagePartitionId);
1011 var partition = new Partition();
1012 partition.fromAttribute(event.storagePartitionId,
1013 webViewInternal.hasNavigated());
1014 webViewInternal.partition = partition;
1015 }
1016
1017 var attached =
1018 webViewInternal.attachWindowAndSetUpEvents(
1019 event.windowId, undefined, event.storagePartitionId);
1020
1021 if (!attached) {
1022 window.console.error(ERROR_MSG_NEWWINDOW_UNABLE_TO_ATTACH);
1023 }
1024 // If the object being passed into attach is not a valid <webview>
1025 // then we will fail and it will be treated as if the new window
1026 // was rejected. The permission API plumbing is used here to clean
1027 // up the state created for the new window if attaching fails.
1028 WebView.setPermission(
1029 self.instanceId, requestId, attached ? 'allow' : 'deny');
1030 }, 0);
1031 },
1032 discard: function() {
1033 validateCall();
1034 WebView.setPermission(self.instanceId, requestId, 'deny');
1035 }
1036 };
1037 webViewEvent.window = windowObj;
1038
1039 var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent);
1040 if (actionTaken) {
1041 return;
1042 }
1043
1044 if (defaultPrevented) {
1045 // Make browser plugin track lifetime of |windowObj|.
1046 MessagingNatives.BindToGC(windowObj, function() {
1047 // Avoid showing a warning message if the decision has already been made.
1048 if (actionTaken) {
1049 return;
1050 }
1051 WebView.setPermission(
1052 self.instanceId, requestId, 'default', '', function(allowed) {
1053 if (allowed) {
1054 return;
1055 }
1056 showWarningMessage();
1057 });
1058 });
1059 } else {
1060 actionTaken = true;
1061 // The default action is to discard the window.
1062 WebView.setPermission(
1063 self.instanceId, requestId, 'default', '', function(allowed) {
1064 if (allowed) {
1065 return;
1066 }
1067 showWarningMessage();
1068 });
1069 }
1070 }; 677 };
1071 678
1072 WebViewInternal.prototype.handlePermissionEvent = 679 WebViewInternal.prototype.onAttach = function(storagePartitionId) {
1073 function(event, webViewEvent) { 680 this.webviewNode.setAttribute('partition', storagePartitionId);
1074 var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' + 681 this.partition.fromAttribute(storagePartitionId, this.hasNavigated());
1075 'Permission has already been decided for this "permissionrequest" event.';
1076
1077 var showWarningMessage = function(permission) {
1078 var WARNING_MSG_PERMISSION_DENIED = '<webview>: ' +
1079 'The permission request for "%1" has been denied.';
1080 window.console.warn(
1081 WARNING_MSG_PERMISSION_DENIED.replace('%1', permission));
1082 };
1083
1084 var requestId = event.requestId;
1085 var self = this;
1086
1087 if (this.getPermissionTypes().indexOf(event.permission) < 0) {
1088 // The permission type is not allowed. Trigger the default response.
1089 WebView.setPermission(
1090 self.instanceId, requestId, 'default', '', function(allowed) {
1091 if (allowed) {
1092 return;
1093 }
1094 showWarningMessage(event.permission);
1095 });
1096 return;
1097 }
1098
1099 var webviewNode = this.webviewNode;
1100 var decisionMade = false;
1101
1102 var validateCall = function() {
1103 if (decisionMade) {
1104 throw new Error(ERROR_MSG_PERMISSION_ALREADY_DECIDED);
1105 }
1106 decisionMade = true;
1107 };
1108
1109 // Construct the event.request object.
1110 var request = {
1111 allow: function() {
1112 validateCall();
1113 WebView.setPermission(self.instanceId, requestId, 'allow');
1114 },
1115 deny: function() {
1116 validateCall();
1117 WebView.setPermission(self.instanceId, requestId, 'deny');
1118 }
1119 };
1120 webViewEvent.request = request;
1121
1122 var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent);
1123 if (decisionMade) {
1124 return;
1125 }
1126
1127 if (defaultPrevented) {
1128 // Make browser plugin track lifetime of |request|.
1129 MessagingNatives.BindToGC(request, function() {
1130 // Avoid showing a warning message if the decision has already been made.
1131 if (decisionMade) {
1132 return;
1133 }
1134 WebView.setPermission(
1135 self.instanceId, requestId, 'default', '', function(allowed) {
1136 if (allowed) {
1137 return;
1138 }
1139 showWarningMessage(event.permission);
1140 });
1141 });
1142 } else {
1143 decisionMade = true;
1144 WebView.setPermission(
1145 self.instanceId, requestId, 'default', '', function(allowed) {
1146 if (allowed) {
1147 return;
1148 }
1149 showWarningMessage(event.permission);
1150 });
1151 }
1152 }; 682 };
1153 683
1154 var WebRequestMessageEvent = CreateEvent('webview.onMessage');
1155
1156 function DeclarativeWebRequestEvent(opt_eventName,
1157 opt_argSchemas,
1158 opt_eventOptions,
1159 opt_webViewInstanceId) {
1160 var subEventName = opt_eventName + '/' + IdGenerator.GetNextId();
1161 EventBindings.Event.call(this, subEventName, opt_argSchemas, opt_eventOptions,
1162 opt_webViewInstanceId);
1163
1164 var self = this;
1165 // TODO(lazyboy): When do we dispose this listener?
1166 WebRequestMessageEvent.addListener(function() {
1167 // Re-dispatch to subEvent's listeners.
1168 $Function.apply(self.dispatch, self, $Array.slice(arguments));
1169 }, {instanceId: opt_webViewInstanceId || 0});
1170 }
1171
1172 DeclarativeWebRequestEvent.prototype = {
1173 __proto__: EventBindings.Event.prototype
1174 };
1175
1176 /**
1177 * @private
1178 */
1179 WebViewInternal.prototype.setupWebRequestEvents = function() {
1180 var self = this;
1181 var request = {};
1182 var createWebRequestEvent = function(webRequestEvent) {
1183 return function() {
1184 if (!self[webRequestEvent.name]) {
1185 self[webRequestEvent.name] =
1186 new WebRequestEvent(
1187 'webview.' + webRequestEvent.name,
1188 webRequestEvent.parameters,
1189 webRequestEvent.extraParameters, webRequestEvent.options,
1190 self.viewInstanceId);
1191 }
1192 return self[webRequestEvent.name];
1193 };
1194 };
1195
1196 var createDeclarativeWebRequestEvent = function(webRequestEvent) {
1197 return function() {
1198 if (!self[webRequestEvent.name]) {
1199 // The onMessage event gets a special event type because we want
1200 // the listener to fire only for messages targeted for this particular
1201 // <webview>.
1202 var EventClass = webRequestEvent.name === 'onMessage' ?
1203 DeclarativeWebRequestEvent : EventBindings.Event;
1204 self[webRequestEvent.name] =
1205 new EventClass(
1206 'webview.' + webRequestEvent.name,
1207 webRequestEvent.parameters,
1208 webRequestEvent.options,
1209 self.viewInstanceId);
1210 }
1211 return self[webRequestEvent.name];
1212 };
1213 };
1214
1215 for (var i = 0; i < DeclarativeWebRequestSchema.events.length; ++i) {
1216 var eventSchema = DeclarativeWebRequestSchema.events[i];
1217 var webRequestEvent = createDeclarativeWebRequestEvent(eventSchema);
1218 Object.defineProperty(
1219 request,
1220 eventSchema.name,
1221 {
1222 get: webRequestEvent,
1223 enumerable: true
1224 }
1225 );
1226 }
1227
1228 // Populate the WebRequest events from the API definition.
1229 for (var i = 0; i < WebRequestSchema.events.length; ++i) {
1230 var webRequestEvent = createWebRequestEvent(WebRequestSchema.events[i]);
1231 Object.defineProperty(
1232 request,
1233 WebRequestSchema.events[i].name,
1234 {
1235 get: webRequestEvent,
1236 enumerable: true
1237 }
1238 );
1239 }
1240 Object.defineProperty(
1241 this.webviewNode,
1242 'request',
1243 {
1244 value: request,
1245 enumerable: true
1246 }
1247 );
1248 };
1249 684
1250 /** @private */ 685 /** @private */
1251 WebViewInternal.prototype.getUserAgent = function() { 686 WebViewInternal.prototype.getUserAgent = function() {
1252 return this.userAgentOverride || navigator.userAgent; 687 return this.userAgentOverride || navigator.userAgent;
1253 }; 688 };
1254 689
1255 /** @private */ 690 /** @private */
1256 WebViewInternal.prototype.isUserAgentOverridden = function() { 691 WebViewInternal.prototype.isUserAgentOverridden = function() {
1257 return !!this.userAgentOverride && 692 return !!this.userAgentOverride &&
1258 this.userAgentOverride != navigator.userAgent; 693 this.userAgentOverride != navigator.userAgent;
(...skipping 20 matching lines...) Expand all
1279 this.webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) || 714 this.webviewNode.getAttribute(WEB_VIEW_ATTRIBUTE_PARTITION) ||
1280 this.webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION]; 715 this.webviewNode[WEB_VIEW_ATTRIBUTE_PARTITION];
1281 var params = { 716 var params = {
1282 'api': 'webview', 717 'api': 'webview',
1283 'instanceId': this.viewInstanceId, 718 'instanceId': this.viewInstanceId,
1284 'name': this.name, 719 'name': this.name,
1285 'src': opt_src, 720 'src': opt_src,
1286 'storagePartitionId': storagePartitionId, 721 'storagePartitionId': storagePartitionId,
1287 'userAgentOverride': this.userAgentOverride 722 'userAgentOverride': this.userAgentOverride
1288 }; 723 };
1289 this.setupNameAttribute();
1290 var events = this.getEvents();
1291 for (var eventName in events) {
1292 this.setupEvent(eventName, events[eventName]);
1293 }
1294 724
1295 return this.browserPluginNode['-internal-attach'](this.instanceId, params); 725 return this.browserPluginNode['-internal-attach'](this.instanceId, params);
1296 }; 726 };
1297 727
1298 // Registers browser plugin <object> custom element. 728 // Registers browser plugin <object> custom element.
1299 function registerBrowserPluginElement() { 729 function registerBrowserPluginElement() {
1300 var proto = Object.create(HTMLObjectElement.prototype); 730 var proto = Object.create(HTMLObjectElement.prototype);
1301 731
1302 proto.createdCallback = function() { 732 proto.createdCallback = function() {
1303 this.setAttribute('type', 'application/browser-plugin'); 733 this.setAttribute('type', 'application/browser-plugin');
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
1424 */ 854 */
1425 WebViewInternal.prototype.maybeGetExperimentalPermissions = function() { 855 WebViewInternal.prototype.maybeGetExperimentalPermissions = function() {
1426 return []; 856 return [];
1427 }; 857 };
1428 858
1429 /** 859 /**
1430 * Calls to show contextmenu right away instead of dispatching a 'contextmenu' 860 * Calls to show contextmenu right away instead of dispatching a 'contextmenu'
1431 * event. 861 * event.
1432 * This will be overridden in web_view_experimental.js to implement contextmenu 862 * This will be overridden in web_view_experimental.js to implement contextmenu
1433 * API. 863 * API.
1434 * @private
1435 */ 864 */
1436 WebViewInternal.prototype.maybeHandleContextMenu = function(e, webViewEvent) { 865 WebViewInternal.prototype.maybeHandleContextMenu = function(e, webViewEvent) {
1437 var requestId = e.requestId; 866 var requestId = e.requestId;
1438 // Setting |params| = undefined will show the context menu unmodified, hence 867 // Setting |params| = undefined will show the context menu unmodified, hence
1439 // the 'contextmenu' API is disabled for stable channel. 868 // the 'contextmenu' API is disabled for stable channel.
1440 var params = undefined; 869 var params = undefined;
1441 WebView.showContextMenu(this.instanceId, requestId, params); 870 WebView.showContextMenu(this.instanceId, requestId, params);
1442 }; 871 };
1443 872
1444 /** 873 /**
1445 * Implemented when the experimental API is available. 874 * Implemented when the experimental API is available.
1446 * @private 875 * @private
1447 */ 876 */
1448 WebViewInternal.prototype.setupExperimentalContextMenus_ = function() {}; 877 WebViewInternal.prototype.setupExperimentalContextMenus = function() {};
1449 878
1450 exports.WebView = WebView; 879 exports.WebView = WebView;
1451 exports.WebViewInternal = WebViewInternal; 880 exports.WebViewInternal = WebViewInternal;
1452 exports.CreateEvent = CreateEvent;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698