Chromium Code Reviews| 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 |