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