| 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 |