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.
5 // This module implements experimental API for <webview>.
6 // See web_view.js for details.
8 // <webview> Experimental API is only available on canary and dev channels of
11 var ContextMenusSchema =
12 requireNative('schema_registry').GetSchema('contextMenus');
13 var CreateEvent = require('webView').CreateEvent;
14 var EventBindings = require('event_bindings');
15 var MessagingNatives = requireNative('messaging_natives');
16 var WebView = require('webView').WebView;
17 var WebViewInternal = require('webView').WebViewInternal;
18 var WebViewSchema = requireNative('schema_registry').GetSchema('webview');
19 var idGeneratorNatives = requireNative('id_generator');
20 var utils = require('utils');
22 // WEB_VIEW_EXPERIMENTAL_EVENTS is a map of experimental <webview> DOM event
23 // names to their associated extension event descriptor objects.
24 // An event listener will be attached to the extension event |evt| specified in
26 // |fields| specifies the public-facing fields in the DOM event that are
27 // accessible to <webview> developers.
28 // |customHandler| allows a handler function to be called each time an extension
29 // event is caught by its event listener. The DOM event should be dispatched
30 // within this handler function. With no handler function, the DOM event
31 // will be dispatched by default each time the extension event is caught.
32 // |cancelable| (default: false) specifies whether the event's default
33 // behavior can be canceled. If the default action associated with the event
34 // is prevented, then its dispatch function will return false in its event
35 // handler. The event must have a custom handler for this to be meaningful.
36 var WEB_VIEW_EXPERIMENTAL_EVENTS = {
38 evt: CreateEvent('webview.contextmenu'),
40 customHandler: function(webViewInternal, event, webViewEvent) {
41 webViewInternal.handleContextMenu(event, webViewEvent);
46 evt: CreateEvent('webview.onFindReply'),
57 evt: CreateEvent('webview.onZoomChange'),
58 fields: ['oldZoomFactor', 'newZoomFactor']
62 function GetUniqueSubEventName(eventName) {
63 return eventName + "/" + idGeneratorNatives.GetNextId();
66 // This is the only "webview.onClicked" named event for this renderer.
68 // Since we need an event per <webview>, we define events with suffix
69 // (subEventName) in each of the <webview>. Behind the scenes, this event is
70 // registered as a ContextMenusEvent, with filter set to the webview's
71 // |viewInstanceId|. Any time a ContextMenusEvent is dispatched, we re-dispatch
72 // it to the subEvent's listeners. This way
73 // <webview>.contextMenus.onClicked behave as a regular chrome Event type.
74 var ContextMenusEvent = CreateEvent('webview.onClicked');
77 * This event is exposed as <webview>.contextMenus.onClicked.
81 function ContextMenusOnClickedEvent(opt_eventName,
84 opt_webViewInstanceId) {
85 var subEventName = GetUniqueSubEventName(opt_eventName);
86 EventBindings.Event.call(this, subEventName, opt_argSchemas, opt_eventOptions,
87 opt_webViewInstanceId);
90 // TODO(lazyboy): When do we dispose this listener?
91 ContextMenusEvent.addListener(function() {
92 // Re-dispatch to subEvent's listeners.
93 $Function.apply(self.dispatch, self, $Array.slice(arguments));
94 }, {instanceId: opt_webViewInstanceId || 0});
97 ContextMenusOnClickedEvent.prototype = {
98 __proto__: EventBindings.Event.prototype
102 * An instance of this class is exposed as <webview>.contextMenus.
105 function WebViewContextMenusImpl(viewInstanceId) {
106 this.viewInstanceId_ = viewInstanceId;
109 WebViewContextMenusImpl.prototype.create = function() {
110 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
111 return $Function.apply(WebView.contextMenusCreate, null, args);
114 WebViewContextMenusImpl.prototype.remove = function() {
115 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
116 return $Function.apply(WebView.contextMenusRemove, null, args);
119 WebViewContextMenusImpl.prototype.removeAll = function() {
120 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
121 return $Function.apply(WebView.contextMenusRemoveAll, null, args);
124 WebViewContextMenusImpl.prototype.update = function() {
125 var args = $Array.concat([this.viewInstanceId_], $Array.slice(arguments));
126 return $Function.apply(WebView.contextMenusUpdate, null, args);
129 var WebViewContextMenus = utils.expose(
130 'WebViewContextMenus', WebViewContextMenusImpl,
131 { functions: ['create', 'remove', 'removeAll', 'update'] });
136 WebViewInternal.prototype.maybeAttachWebRequestEventToObject =
137 function(obj, eventName, webRequestEvent) {
138 Object.defineProperty(
142 get: webRequestEvent,
149 WebViewInternal.prototype.handleContextMenu = function(e, webViewEvent) {
150 var requestId = e.requestId;
152 // Construct the event.menu object.
154 show: function(items) {
155 // TODO(lazyboy): Implement.
156 window.console.log('menu.show() Not implemented yet');
159 webViewEvent.menu = menu;
160 var webviewNode = this.webviewNode;
161 webviewNode.dispatchEvent(webViewEvent);
167 WebViewInternal.prototype.setZoom = function(zoomFactor) {
168 if (!this.instanceId) {
171 WebView.setZoom(this.instanceId, zoomFactor);
174 WebViewInternal.prototype.maybeGetExperimentalEvents = function() {
175 return WEB_VIEW_EXPERIMENTAL_EVENTS;
179 WebViewInternal.prototype.maybeGetExperimentalPermissions = function() {
184 WebViewInternal.prototype.maybeSetCurrentZoomFactor =
185 function(zoomFactor) {
186 this.currentZoomFactor = zoomFactor;
190 WebViewInternal.prototype.setZoom = function(zoomFactor, callback) {
191 if (!this.instanceId) {
194 WebView.setZoom(this.instanceId, zoomFactor, callback);
197 WebViewInternal.prototype.getZoom = function(callback) {
198 if (!this.instanceId) {
201 WebView.getZoom(this.instanceId, callback);
205 WebViewInternal.prototype.captureVisibleRegion = function(spec, callback) {
206 WebView.captureVisibleRegion(this.instanceId, spec, callback);
210 WebViewInternal.prototype.find = function(search_text, options, callback) {
211 if (!this.instanceId) {
214 WebView.find(this.instanceId, search_text, options, callback);
218 WebViewInternal.prototype.stopFinding = function(action) {
219 if (!this.instanceId) {
222 WebView.stopFinding(this.instanceId, action);
225 WebViewInternal.maybeRegisterExperimentalAPIs = function(proto) {
226 proto.setZoom = function(zoomFactor, callback) {
227 privates(this).internal.setZoom(zoomFactor, callback);
230 proto.getZoom = function(callback) {
231 return privates(this).internal.getZoom(callback);
234 proto.captureVisibleRegion = function(spec, callback) {
235 privates(this).internal.captureVisibleRegion(spec, callback);
238 proto.find = function(search_text, options, callback) {
239 privates(this).internal.find(search_text, options, callback);
242 proto.stopFinding = function(action) {
243 privates(this).internal.stopFinding(action);
248 WebViewInternal.prototype.setupExperimentalContextMenus_ = function() {
250 var createContextMenus = function() {
252 if (self.contextMenus_) {
253 return self.contextMenus_;
256 self.contextMenus_ = new WebViewContextMenus(self.viewInstanceId);
258 // Define 'onClicked' event property on |self.contextMenus_|.
259 var getOnClickedEvent = function() {
261 if (!self.contextMenusOnClickedEvent_) {
262 var eventName = 'webview.onClicked';
263 // TODO(lazyboy): Find event by name instead of events[0].
264 var eventSchema = WebViewSchema.events[0];
265 var eventOptions = {supportsListeners: true};
266 var onClickedEvent = new ContextMenusOnClickedEvent(
267 eventName, eventSchema, eventOptions, self.viewInstanceId);
268 self.contextMenusOnClickedEvent_ = onClickedEvent;
269 return onClickedEvent;
271 return self.contextMenusOnClickedEvent_;
274 Object.defineProperty(
277 {get: getOnClickedEvent(), enumerable: true});
279 return self.contextMenus_;
283 // Expose <webview>.contextMenus object.
284 Object.defineProperty(
288 get: createContextMenus(),