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

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

Issue 1026383003: <webview>: make context menus cancellable using JS API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address comments from yoz@ Created 5 years, 9 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 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
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 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698