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 = |
| 28 CreateEvent('chromeWebViewInternal.onContextMenuShow'); |
36 | 29 |
37 // ----------------------------------------------------------------------------- | 30 // ----------------------------------------------------------------------------- |
38 // ContextMenusOnClickedEvent object. | 31 // ContextMenusOnClickedEvent object. |
39 | 32 |
40 // This event is exposed as <webview>.contextMenus.onClicked. | 33 // This event is exposed as <webview>.contextMenus.onClicked. |
41 function ContextMenusOnClickedEvent(opt_eventName, | 34 function ContextMenusOnClickedEvent(opt_eventName, |
42 opt_argSchemas, | 35 opt_argSchemas, |
43 opt_eventOptions, | 36 opt_eventOptions, |
44 opt_webViewInstanceId) { | 37 opt_webViewInstanceId) { |
45 var subEventName = GetUniqueSubEventName(opt_eventName); | 38 var subEventName = GetUniqueSubEventName(opt_eventName); |
46 EventBindings.Event.call(this, | 39 EventBindings.Event.call(this, |
47 subEventName, | 40 subEventName, |
48 opt_argSchemas, | 41 opt_argSchemas, |
49 opt_eventOptions, | 42 opt_eventOptions, |
50 opt_webViewInstanceId); | 43 opt_webViewInstanceId); |
51 | 44 |
52 // TODO(lazyboy): When do we dispose this listener? | 45 // TODO(lazyboy): When do we dispose this listener? |
53 ContextMenusEvent.addListener(function() { | 46 ContextMenusEvent.addListener(function() { |
54 // Re-dispatch to subEvent's listeners. | 47 // Re-dispatch to subEvent's listeners. |
55 $Function.apply(this.dispatch, this, $Array.slice(arguments)); | 48 $Function.apply(this.dispatch, this, $Array.slice(arguments)); |
56 }.bind(this), {instanceId: opt_webViewInstanceId || 0}); | 49 }.bind(this), {instanceId: opt_webViewInstanceId || 0}); |
57 } | 50 } |
58 | 51 |
59 ContextMenusOnClickedEvent.prototype.__proto__ = EventBindings.Event.prototype; | 52 ContextMenusOnClickedEvent.prototype.__proto__ = EventBindings.Event.prototype; |
60 | 53 |
| 54 function ContextMenusOnContextMenuEvent(webViewImpl, |
| 55 opt_eventName, |
| 56 opt_argSchemas, |
| 57 opt_eventOptions, |
| 58 opt_webViewInstanceId) { |
| 59 var subEventName = GetUniqueSubEventName(opt_eventName); |
| 60 EventBindings.Event.call(this, |
| 61 subEventName, |
| 62 opt_argSchemas, |
| 63 opt_eventOptions, |
| 64 opt_webViewInstanceId); |
| 65 var defaultPrevented = false; |
| 66 ContextMenusHandlerEvent.addListener(function(e) { |
| 67 var defaultPrevented = false; |
| 68 var event = { |
| 69 'preventDefault': function() { defaultPrevented = true; } |
| 70 }; |
| 71 |
| 72 // Re-dispatch to subEvent's listeners. |
| 73 $Function.apply(this.dispatch, this, [event]); |
| 74 |
| 75 if (!defaultPrevented) { |
| 76 // TODO(lazyboy): Remove |items| parameter completely from |
| 77 // ChromeWebView.showContextMenu as we don't do anything useful with it |
| 78 // currently. |
| 79 var items = []; |
| 80 ChromeWebView.showContextMenu( |
| 81 webViewImpl.guest.getId(), e.requestId, items); |
| 82 } |
| 83 }.bind(this), {instanceId: opt_webViewInstanceId || 0}); |
| 84 } |
| 85 |
| 86 ContextMenusOnContextMenuEvent.prototype.__proto__ = |
| 87 EventBindings.Event.prototype; |
| 88 |
61 // ----------------------------------------------------------------------------- | 89 // ----------------------------------------------------------------------------- |
62 // WebViewContextMenusImpl object. | 90 // WebViewContextMenusImpl object. |
63 | 91 |
64 // An instance of this class is exposed as <webview>.contextMenus. | 92 // An instance of this class is exposed as <webview>.contextMenus. |
65 function WebViewContextMenusImpl(viewInstanceId) { | 93 function WebViewContextMenusImpl(viewInstanceId) { |
66 this.viewInstanceId_ = viewInstanceId; | 94 this.viewInstanceId_ = viewInstanceId; |
67 } | 95 } |
68 | 96 |
69 WebViewContextMenusImpl.prototype.create = function() { | 97 WebViewContextMenusImpl.prototype.create = function() { |
70 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments)); | 98 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments)); |
(...skipping 15 matching lines...) Expand all Loading... |
86 return $Function.apply(ChromeWebView.contextMenusUpdate, null, args); | 114 return $Function.apply(ChromeWebView.contextMenusUpdate, null, args); |
87 }; | 115 }; |
88 | 116 |
89 var WebViewContextMenus = Utils.expose( | 117 var WebViewContextMenus = Utils.expose( |
90 'WebViewContextMenus', WebViewContextMenusImpl, | 118 'WebViewContextMenus', WebViewContextMenusImpl, |
91 { functions: ['create', 'remove', 'removeAll', 'update'] }); | 119 { functions: ['create', 'remove', 'removeAll', 'update'] }); |
92 | 120 |
93 // ----------------------------------------------------------------------------- | 121 // ----------------------------------------------------------------------------- |
94 | 122 |
95 WebViewImpl.prototype.maybeSetupContextMenus = function() { | 123 WebViewImpl.prototype.maybeSetupContextMenus = function() { |
| 124 if (!this.contextMenusOnContextMenuEvent_) { |
| 125 var eventName = 'chromeWebViewInternal.onContextMenuShow'; |
| 126 // TODO(lazyboy): Find event by name instead of events[1]. |
| 127 var eventSchema = ChromeWebViewSchema.events[1]; |
| 128 var eventOptions = {supportsListeners: true}; |
| 129 this.contextMenusOnContextMenuEvent_ = new ContextMenusOnContextMenuEvent( |
| 130 this, eventName, eventSchema, eventOptions, this.viewInstanceId); |
| 131 } |
| 132 |
96 var createContextMenus = function() { | 133 var createContextMenus = function() { |
97 return function() { | 134 return function() { |
98 if (this.contextMenus_) { | 135 if (this.contextMenus_) { |
99 return this.contextMenus_; | 136 return this.contextMenus_; |
100 } | 137 } |
101 | 138 |
102 this.contextMenus_ = new WebViewContextMenus(this.viewInstanceId); | 139 this.contextMenus_ = new WebViewContextMenus(this.viewInstanceId); |
103 | 140 |
104 // Define 'onClicked' event property on |this.contextMenus_|. | 141 // Define 'onClicked' event property on |this.contextMenus_|. |
105 var getOnClickedEvent = function() { | 142 var getOnClickedEvent = function() { |
106 return function() { | 143 return function() { |
107 if (!this.contextMenusOnClickedEvent_) { | 144 if (!this.contextMenusOnClickedEvent_) { |
108 var eventName = 'chromeWebViewInternal.onClicked'; | 145 var eventName = 'chromeWebViewInternal.onClicked'; |
109 // TODO(lazyboy): Find event by name instead of events[0]. | 146 // TODO(lazyboy): Find event by name instead of events[0]. |
110 var eventSchema = ChromeWebViewSchema.events[0]; | 147 var eventSchema = ChromeWebViewSchema.events[0]; |
111 var eventOptions = {supportsListeners: true}; | 148 var eventOptions = {supportsListeners: true}; |
112 var onClickedEvent = new ContextMenusOnClickedEvent( | 149 var onClickedEvent = new ContextMenusOnClickedEvent( |
113 eventName, eventSchema, eventOptions, this.viewInstanceId); | 150 eventName, eventSchema, eventOptions, this.viewInstanceId); |
114 this.contextMenusOnClickedEvent_ = onClickedEvent; | 151 this.contextMenusOnClickedEvent_ = onClickedEvent; |
115 return onClickedEvent; | 152 return onClickedEvent; |
116 } | 153 } |
117 return this.contextMenusOnClickedEvent_; | 154 return this.contextMenusOnClickedEvent_; |
118 }.bind(this); | 155 }.bind(this); |
119 }.bind(this); | 156 }.bind(this); |
120 Object.defineProperty( | 157 $Object.defineProperty( |
121 this.contextMenus_, | 158 this.contextMenus_, |
122 'onClicked', | 159 'onClicked', |
123 {get: getOnClickedEvent(), enumerable: true}); | 160 {get: getOnClickedEvent(), enumerable: true}); |
124 | 161 $Object.defineProperty( |
| 162 this.contextMenus_, |
| 163 'onShow', |
| 164 { |
| 165 get: function() { |
| 166 return this.contextMenusOnContextMenuEvent_; |
| 167 }.bind(this), |
| 168 enumerable: true |
| 169 }); |
125 return this.contextMenus_; | 170 return this.contextMenus_; |
126 }.bind(this); | 171 }.bind(this); |
127 }.bind(this); | 172 }.bind(this); |
128 | 173 |
129 // Expose <webview>.contextMenus object. | 174 // Expose <webview>.contextMenus object. |
130 Object.defineProperty( | 175 // TODO(lazyboy): Add documentation for contextMenus: |
| 176 // http://crbug.com/470979. |
| 177 $Object.defineProperty( |
131 this.element, | 178 this.element, |
132 'contextMenus', | 179 'contextMenus', |
133 { | 180 { |
134 get: createContextMenus(), | 181 get: createContextMenus(), |
135 enumerable: true | 182 enumerable: true |
136 }); | 183 }); |
137 }; | 184 }; |
138 | 185 |
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) { | 186 function GetUniqueSubEventName(eventName) { |
179 return eventName + '/' + idGeneratorNatives.GetNextId(); | 187 return eventName + '/' + idGeneratorNatives.GetNextId(); |
180 } | 188 } |
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 |