OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 chrome-specific <webview> API. | 5 // This module implements chrome-specific <webview> API. |
6 // See web_view_api_methods.js for details. | 6 // See web_view_api_methods.js for details. |
7 | 7 |
8 var ChromeWebView = require('chromeWebViewInternal').ChromeWebView; | 8 var ChromeWebView = require('chromeWebViewInternal').ChromeWebView; |
9 var ChromeWebViewSchema = | 9 var ChromeWebViewSchema = |
10 requireNative('schema_registry').GetSchema('chromeWebViewInternal'); | 10 requireNative('schema_registry').GetSchema('chromeWebViewInternal'); |
11 var CreateEvent = require('guestViewEvents').CreateEvent; | 11 var CreateEvent = require('guestViewEvents').CreateEvent; |
12 var EventBindings = require('event_bindings'); | 12 var EventBindings = require('event_bindings'); |
13 var idGeneratorNatives = requireNative('id_generator'); | 13 var idGeneratorNatives = requireNative('id_generator'); |
14 var Utils = require('utils'); | 14 var Utils = require('utils'); |
15 var WebViewEvents = require('webViewEvents').WebViewEvents; | |
16 var WebViewImpl = require('webView').WebViewImpl; | 15 var WebViewImpl = require('webView').WebViewImpl; |
17 | 16 |
18 var CHROME_WEB_VIEW_EVENTS = { | |
19 'contextmenushown': { | |
20 evt: CreateEvent('chromeWebViewInternal.contextmenu'), | |
21 cancelable: true, | |
22 fields: ['items'], | |
23 handler: 'handleContextMenu' | |
24 } | |
25 }; | |
26 | |
27 // This is the only "webViewInternal.onClicked" named event for this renderer. | 17 // This is the only "webViewInternal.onClicked" named event for this renderer. |
28 // | 18 // |
29 // Since we need an event per <webview>, we define events with suffix | 19 // Since we need an event per <webview>, we define events with suffix |
30 // (subEventName) in each of the <webview>. Behind the scenes, this event is | 20 // (subEventName) in each of the <webview>. Behind the scenes, this event is |
31 // registered as a ContextMenusEvent, with filter set to the webview's | 21 // registered as a ContextMenusEvent, with filter set to the webview's |
32 // |viewInstanceId|. Any time a ContextMenusEvent is dispatched, we re-dispatch | 22 // |viewInstanceId|. Any time a ContextMenusEvent is dispatched, we re-dispatch |
33 // it to the subEvent's listeners. This way | 23 // it to the subEvent's listeners. This way |
34 // <webview>.contextMenus.onClicked behave as a regular chrome Event type. | 24 // <webview>.contextMenus.onClicked behave as a regular chrome Event type. |
35 var ContextMenusEvent = CreateEvent('chromeWebViewInternal.onClicked'); | 25 var ContextMenusEvent = CreateEvent('chromeWebViewInternal.onClicked'); |
26 // See comment above. | |
27 var ContextMenusHandlerEvent = CreateEvent('chromeWebViewInternal.contextmenu'); | |
36 | 28 |
37 // ----------------------------------------------------------------------------- | 29 // ----------------------------------------------------------------------------- |
38 // ContextMenusOnClickedEvent object. | 30 // ContextMenusOnClickedEvent object. |
39 | 31 |
40 // This event is exposed as <webview>.contextMenus.onClicked. | 32 // This event is exposed as <webview>.contextMenus.onClicked. |
41 function ContextMenusOnClickedEvent(opt_eventName, | 33 function ContextMenusOnClickedEvent(opt_eventName, |
42 opt_argSchemas, | 34 opt_argSchemas, |
43 opt_eventOptions, | 35 opt_eventOptions, |
44 opt_webViewInstanceId) { | 36 opt_webViewInstanceId) { |
45 var subEventName = GetUniqueSubEventName(opt_eventName); | 37 var subEventName = GetUniqueSubEventName(opt_eventName); |
46 EventBindings.Event.call(this, | 38 EventBindings.Event.call(this, |
47 subEventName, | 39 subEventName, |
48 opt_argSchemas, | 40 opt_argSchemas, |
49 opt_eventOptions, | 41 opt_eventOptions, |
50 opt_webViewInstanceId); | 42 opt_webViewInstanceId); |
51 | 43 |
52 // TODO(lazyboy): When do we dispose this listener? | 44 // TODO(lazyboy): When do we dispose this listener? |
53 ContextMenusEvent.addListener(function() { | 45 ContextMenusEvent.addListener(function() { |
54 // Re-dispatch to subEvent's listeners. | 46 // Re-dispatch to subEvent's listeners. |
55 $Function.apply(this.dispatch, this, $Array.slice(arguments)); | 47 $Function.apply(this.dispatch, this, $Array.slice(arguments)); |
56 }.bind(this), {instanceId: opt_webViewInstanceId || 0}); | 48 }.bind(this), {instanceId: opt_webViewInstanceId || 0}); |
57 } | 49 } |
58 | 50 |
59 ContextMenusOnClickedEvent.prototype.__proto__ = EventBindings.Event.prototype; | 51 ContextMenusOnClickedEvent.prototype.__proto__ = EventBindings.Event.prototype; |
60 | 52 |
53 function ContextMenusOnContextMenuEvent(webViewImpl, | |
54 opt_eventName, | |
55 opt_argSchemas, | |
56 opt_eventOptions, | |
57 opt_webViewInstanceId) { | |
58 var subEventName = GetUniqueSubEventName(opt_eventName); | |
59 EventBindings.Event.call(this, | |
60 subEventName, | |
61 opt_argSchemas, | |
62 opt_eventOptions, | |
63 opt_webViewInstanceId); | |
64 var defaultPrevented = false; | |
65 ContextMenusHandlerEvent.addListener(function(e) { | |
66 var defaultPrevented = false; | |
67 var event = { | |
68 'preventDefault': function() { defaultPrevented = true; } | |
69 }; | |
70 | |
71 // Re-dispatch to subEvent's listeners. | |
72 $Function.apply(this.dispatch, this, [event]); | |
73 | |
74 if (!defaultPrevented) { | |
75 // TODO(lazyboy): Remove |items| parameter completely from | |
76 // ChromeWebView.showContextMenu as we don't do anything useful with it | |
77 // currently. | |
78 var items = []; | |
79 ChromeWebView.showContextMenu( | |
80 webViewImpl.guest.getId(), e.requestId, items); | |
81 } | |
82 }.bind(this), {instanceId: opt_webViewInstanceId || 0}); | |
83 } | |
84 | |
85 ContextMenusOnContextMenuEvent.prototype.__proto__ = | |
86 EventBindings.Event.prototype; | |
87 | |
61 // ----------------------------------------------------------------------------- | 88 // ----------------------------------------------------------------------------- |
62 // WebViewContextMenusImpl object. | 89 // WebViewContextMenusImpl object. |
63 | 90 |
64 // An instance of this class is exposed as <webview>.contextMenus. | 91 // An instance of this class is exposed as <webview>.contextMenus. |
65 function WebViewContextMenusImpl(viewInstanceId) { | 92 function WebViewContextMenusImpl(viewInstanceId) { |
66 this.viewInstanceId_ = viewInstanceId; | 93 this.viewInstanceId_ = viewInstanceId; |
67 } | 94 } |
68 | 95 |
69 WebViewContextMenusImpl.prototype.create = function() { | 96 WebViewContextMenusImpl.prototype.create = function() { |
70 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments)); | 97 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments)); |
(...skipping 15 matching lines...) Expand all Loading... | |
86 return $Function.apply(ChromeWebView.contextMenusUpdate, null, args); | 113 return $Function.apply(ChromeWebView.contextMenusUpdate, null, args); |
87 }; | 114 }; |
88 | 115 |
89 var WebViewContextMenus = Utils.expose( | 116 var WebViewContextMenus = Utils.expose( |
90 'WebViewContextMenus', WebViewContextMenusImpl, | 117 'WebViewContextMenus', WebViewContextMenusImpl, |
91 { functions: ['create', 'remove', 'removeAll', 'update'] }); | 118 { functions: ['create', 'remove', 'removeAll', 'update'] }); |
92 | 119 |
93 // ----------------------------------------------------------------------------- | 120 // ----------------------------------------------------------------------------- |
94 | 121 |
95 WebViewImpl.prototype.maybeSetupContextMenus = function() { | 122 WebViewImpl.prototype.maybeSetupContextMenus = function() { |
123 if (!this.contextMenusOnContextMenuEvent_) { | |
124 var eventName = 'chromeWebViewInternal.onContextMenu'; | |
125 // TODO(lazyboy): Find event by name instead of events[1]. | |
126 var eventSchema = ChromeWebViewSchema.events[1]; | |
127 var eventOptions = {supportsListeners: true}; | |
128 this.contextMenusOnContextMenuEvent_ = new ContextMenusOnContextMenuEvent( | |
129 this, eventName, eventSchema, eventOptions, this.viewInstanceId); | |
130 } | |
131 | |
96 var createContextMenus = function() { | 132 var createContextMenus = function() { |
97 return function() { | 133 return function() { |
98 if (this.contextMenus_) { | 134 if (this.contextMenus_) { |
99 return this.contextMenus_; | 135 return this.contextMenus_; |
100 } | 136 } |
101 | 137 |
102 this.contextMenus_ = new WebViewContextMenus(this.viewInstanceId); | 138 this.contextMenus_ = new WebViewContextMenus(this.viewInstanceId); |
103 | 139 |
104 // Define 'onClicked' event property on |this.contextMenus_|. | 140 // Define 'onClicked' event property on |this.contextMenus_|. |
105 var getOnClickedEvent = function() { | 141 var getOnClickedEvent = function() { |
106 return function() { | 142 return function() { |
107 if (!this.contextMenusOnClickedEvent_) { | 143 if (!this.contextMenusOnClickedEvent_) { |
108 var eventName = 'chromeWebViewInternal.onClicked'; | 144 var eventName = 'chromeWebViewInternal.onClicked'; |
109 // TODO(lazyboy): Find event by name instead of events[0]. | 145 // TODO(lazyboy): Find event by name instead of events[0]. |
110 var eventSchema = ChromeWebViewSchema.events[0]; | 146 var eventSchema = ChromeWebViewSchema.events[0]; |
111 var eventOptions = {supportsListeners: true}; | 147 var eventOptions = {supportsListeners: true}; |
112 var onClickedEvent = new ContextMenusOnClickedEvent( | 148 var onClickedEvent = new ContextMenusOnClickedEvent( |
113 eventName, eventSchema, eventOptions, this.viewInstanceId); | 149 eventName, eventSchema, eventOptions, this.viewInstanceId); |
114 this.contextMenusOnClickedEvent_ = onClickedEvent; | 150 this.contextMenusOnClickedEvent_ = onClickedEvent; |
115 return onClickedEvent; | 151 return onClickedEvent; |
116 } | 152 } |
117 return this.contextMenusOnClickedEvent_; | 153 return this.contextMenusOnClickedEvent_; |
118 }.bind(this); | 154 }.bind(this); |
119 }.bind(this); | 155 }.bind(this); |
120 Object.defineProperty( | 156 Object.defineProperty( |
121 this.contextMenus_, | 157 this.contextMenus_, |
122 'onClicked', | 158 'onClicked', |
123 {get: getOnClickedEvent(), enumerable: true}); | 159 {get: getOnClickedEvent(), enumerable: true}); |
124 | 160 Object.defineProperty( |
Fady Samuel
2015/03/26 02:40:28
nit: $Object
lazyboy
2015/03/26 04:16:26
Done.
lazyboy
2015/03/26 19:18:02
Renamed onContextMenu -> onShow.
Renamed internal
| |
161 this.contextMenus_, | |
162 'onContextMenu', | |
163 { | |
164 get: function() { | |
165 return this.contextMenusOnContextMenuEvent_; | |
166 }.bind(this), | |
167 enumerable: true | |
168 }); | |
125 return this.contextMenus_; | 169 return this.contextMenus_; |
126 }.bind(this); | 170 }.bind(this); |
127 }.bind(this); | 171 }.bind(this); |
128 | 172 |
129 // Expose <webview>.contextMenus object. | 173 // Expose <webview>.contextMenus object. |
130 Object.defineProperty( | 174 Object.defineProperty( |
131 this.element, | 175 this.element, |
132 'contextMenus', | 176 'contextMenus', |
133 { | 177 { |
134 get: createContextMenus(), | 178 get: createContextMenus(), |
135 enumerable: true | 179 enumerable: true |
136 }); | 180 }); |
137 }; | 181 }; |
138 | 182 |
139 WebViewEvents.prototype.handleContextMenu = function(event, eventName) { | |
140 var webViewEvent = this.makeDomEvent(event, eventName); | |
141 var requestId = event.requestId; | |
142 // Construct the event.menu object. | |
143 var actionTaken = false; | |
144 var validateCall = function() { | |
145 var ERROR_MSG_CONTEXT_MENU_ACTION_ALREADY_TAKEN = '<webview>: ' + | |
146 'An action has already been taken for this "contextmenu" event.'; | |
147 | |
148 if (actionTaken) { | |
149 throw new Error(ERROR_MSG_CONTEXT_MENU_ACTION_ALREADY_TAKEN); | |
150 } | |
151 actionTaken = true; | |
152 }; | |
153 var menu = { | |
154 show: function(items) { | |
155 validateCall(); | |
156 // TODO(lazyboy): WebViewShowContextFunction doesn't do anything useful | |
157 // with |items|, implement. | |
158 ChromeWebView.showContextMenu(this.view.guest.getId(), requestId, items); | |
159 }.bind(this) | |
160 }; | |
161 webViewEvent.menu = menu; | |
162 var element = this.view.element; | |
163 var defaultPrevented = !element.dispatchEvent(webViewEvent); | |
164 if (actionTaken) { | |
165 return; | |
166 } | |
167 if (!defaultPrevented) { | |
168 actionTaken = true; | |
169 // The default action is equivalent to just showing the context menu as is. | |
170 ChromeWebView.showContextMenu( | |
171 this.view.guest.getId(), requestId, undefined); | |
172 | |
173 // TODO(lazyboy): Figure out a way to show warning message only when | |
174 // listeners are registered for this event. | |
175 } // else we will ignore showing the context menu completely. | |
176 }; | |
177 | |
178 function GetUniqueSubEventName(eventName) { | 183 function GetUniqueSubEventName(eventName) { |
179 return eventName + '/' + idGeneratorNatives.GetNextId(); | 184 return eventName + '/' + idGeneratorNatives.GetNextId(); |
180 } | 185 } |
181 | |
182 // Exposes |CHROME_WEB_VIEW_EVENTS| when the ChromeWebView API is available. | |
183 (function() { | |
184 for (var eventName in CHROME_WEB_VIEW_EVENTS) { | |
185 WebViewEvents.EVENTS[eventName] = CHROME_WEB_VIEW_EVENTS[eventName]; | |
186 } | |
187 })(); | |
OLD | NEW |