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 | 7 |
7 var ChromeWebView = require('chromeWebViewInternal').ChromeWebView; | 8 var ChromeWebView = require('chromeWebViewInternal').ChromeWebView; |
| 9 var ChromeWebViewSchema = |
| 10 requireNative('schema_registry').GetSchema('chromeWebViewInternal'); |
8 var CreateEvent = require('guestViewEvents').CreateEvent; | 11 var CreateEvent = require('guestViewEvents').CreateEvent; |
9 var EventBindings = require('event_bindings'); | 12 var EventBindings = require('event_bindings'); |
| 13 var idGeneratorNatives = requireNative('id_generator'); |
| 14 var Utils = require('utils'); |
10 var WebViewEvents = require('webViewEvents').WebViewEvents; | 15 var WebViewEvents = require('webViewEvents').WebViewEvents; |
| 16 var WebViewImpl = require('webView').WebViewImpl; |
11 | 17 |
12 var CHROME_WEB_VIEW_EVENTS = { | 18 var CHROME_WEB_VIEW_EVENTS = { |
13 'contextmenushown': { | 19 'contextmenushown': { |
14 evt: CreateEvent('chromeWebViewInternal.contextmenu'), | 20 evt: CreateEvent('chromeWebViewInternal.contextmenu'), |
15 cancelable: true, | 21 cancelable: true, |
16 fields: ['items'], | 22 fields: ['items'], |
17 handler: 'handleContextMenu' | 23 handler: 'handleContextMenu' |
18 } | 24 } |
19 }; | 25 }; |
20 | 26 |
21 /** | 27 // This is the only "webViewInternal.onClicked" named event for this renderer. |
22 * Calls to show contextmenu right away instead of dispatching a 'contextmenu' | 28 // |
23 * event. | 29 // Since we need an event per <webview>, we define events with suffix |
24 * This will be overridden in chrome_web_view_experimental.js to implement | 30 // (subEventName) in each of the <webview>. Behind the scenes, this event is |
25 * contextmenu API. | 31 // registered as a ContextMenusEvent, with filter set to the webview's |
26 */ | 32 // |viewInstanceId|. Any time a ContextMenusEvent is dispatched, we re-dispatch |
27 WebViewEvents.prototype.handleContextMenu = function(event) { | 33 // it to the subEvent's listeners. This way |
| 34 // <webview>.contextMenus.onClicked behave as a regular chrome Event type. |
| 35 var ContextMenusEvent = CreateEvent('chromeWebViewInternal.onClicked'); |
| 36 |
| 37 // ----------------------------------------------------------------------------- |
| 38 // ContextMenusOnClickedEvent object. |
| 39 |
| 40 // This event is exposed as <webview>.contextMenus.onClicked. |
| 41 function ContextMenusOnClickedEvent(opt_eventName, |
| 42 opt_argSchemas, |
| 43 opt_eventOptions, |
| 44 opt_webViewInstanceId) { |
| 45 var subEventName = GetUniqueSubEventName(opt_eventName); |
| 46 EventBindings.Event.call(this, |
| 47 subEventName, |
| 48 opt_argSchemas, |
| 49 opt_eventOptions, |
| 50 opt_webViewInstanceId); |
| 51 |
| 52 // TODO(lazyboy): When do we dispose this listener? |
| 53 ContextMenusEvent.addListener(function() { |
| 54 // Re-dispatch to subEvent's listeners. |
| 55 $Function.apply(this.dispatch, this, $Array.slice(arguments)); |
| 56 }.bind(this), {instanceId: opt_webViewInstanceId || 0}); |
| 57 } |
| 58 |
| 59 ContextMenusOnClickedEvent.prototype.__proto__ = EventBindings.Event.prototype; |
| 60 |
| 61 // ----------------------------------------------------------------------------- |
| 62 // WebViewContextMenusImpl object. |
| 63 |
| 64 // An instance of this class is exposed as <webview>.contextMenus. |
| 65 function WebViewContextMenusImpl(viewInstanceId) { |
| 66 this.viewInstanceId_ = viewInstanceId; |
| 67 } |
| 68 |
| 69 WebViewContextMenusImpl.prototype.create = function() { |
| 70 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments)); |
| 71 return $Function.apply(ChromeWebView.contextMenusCreate, null, args); |
| 72 }; |
| 73 |
| 74 WebViewContextMenusImpl.prototype.remove = function() { |
| 75 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments)); |
| 76 return $Function.apply(ChromeWebView.contextMenusRemove, null, args); |
| 77 }; |
| 78 |
| 79 WebViewContextMenusImpl.prototype.removeAll = function() { |
| 80 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments)); |
| 81 return $Function.apply(ChromeWebView.contextMenusRemoveAll, null, args); |
| 82 }; |
| 83 |
| 84 WebViewContextMenusImpl.prototype.update = function() { |
| 85 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments)); |
| 86 return $Function.apply(ChromeWebView.contextMenusUpdate, null, args); |
| 87 }; |
| 88 |
| 89 var WebViewContextMenus = Utils.expose( |
| 90 'WebViewContextMenus', WebViewContextMenusImpl, |
| 91 { functions: ['create', 'remove', 'removeAll', 'update'] }); |
| 92 |
| 93 // ----------------------------------------------------------------------------- |
| 94 |
| 95 WebViewImpl.prototype.maybeSetupContextMenus = function() { |
| 96 var createContextMenus = function() { |
| 97 return function() { |
| 98 if (this.contextMenus_) { |
| 99 return this.contextMenus_; |
| 100 } |
| 101 |
| 102 this.contextMenus_ = new WebViewContextMenus(this.viewInstanceId); |
| 103 |
| 104 // Define 'onClicked' event property on |this.contextMenus_|. |
| 105 var getOnClickedEvent = function() { |
| 106 return function() { |
| 107 if (!this.contextMenusOnClickedEvent_) { |
| 108 var eventName = 'chromeWebViewInternal.onClicked'; |
| 109 // TODO(lazyboy): Find event by name instead of events[0]. |
| 110 var eventSchema = ChromeWebViewSchema.events[0]; |
| 111 var eventOptions = {supportsListeners: true}; |
| 112 var onClickedEvent = new ContextMenusOnClickedEvent( |
| 113 eventName, eventSchema, eventOptions, this.viewInstanceId); |
| 114 this.contextMenusOnClickedEvent_ = onClickedEvent; |
| 115 return onClickedEvent; |
| 116 } |
| 117 return this.contextMenusOnClickedEvent_; |
| 118 }.bind(this); |
| 119 }.bind(this); |
| 120 Object.defineProperty( |
| 121 this.contextMenus_, |
| 122 'onClicked', |
| 123 {get: getOnClickedEvent(), enumerable: true}); |
| 124 |
| 125 return this.contextMenus_; |
| 126 }.bind(this); |
| 127 }.bind(this); |
| 128 |
| 129 // Expose <webview>.contextMenus object. |
| 130 Object.defineProperty( |
| 131 this.element, |
| 132 'contextMenus', |
| 133 { |
| 134 get: createContextMenus(), |
| 135 enumerable: true |
| 136 }); |
| 137 }; |
| 138 |
| 139 WebViewEvents.prototype.handleContextMenu = function(event, eventName) { |
| 140 var webViewEvent = this.makeDomEvent(event, eventName); |
28 var requestId = event.requestId; | 141 var requestId = event.requestId; |
29 // Setting |params| = undefined will show the context menu unmodified, hence | 142 // Construct the event.menu object. |
30 // the 'contextmenu' API is disabled for stable channel. | 143 var actionTaken = false; |
31 var params = undefined; | 144 var validateCall = function() { |
32 ChromeWebView.showContextMenu(this.view.guest.getId(), requestId, params); | 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. |
33 }; | 176 }; |
34 | 177 |
| 178 function GetUniqueSubEventName(eventName) { |
| 179 return eventName + '/' + idGeneratorNatives.GetNextId(); |
| 180 } |
| 181 |
35 // Exposes |CHROME_WEB_VIEW_EVENTS| when the ChromeWebView API is available. | 182 // Exposes |CHROME_WEB_VIEW_EVENTS| when the ChromeWebView API is available. |
36 (function() { | 183 (function() { |
37 for (var eventName in CHROME_WEB_VIEW_EVENTS) { | 184 for (var eventName in CHROME_WEB_VIEW_EVENTS) { |
38 WebViewEvents.EVENTS[eventName] = CHROME_WEB_VIEW_EVENTS[eventName]; | 185 WebViewEvents.EVENTS[eventName] = CHROME_WEB_VIEW_EVENTS[eventName]; |
39 } | 186 } |
40 })(); | 187 })(); |
OLD | NEW |