Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / renderer / resources / context_menus_handlers.js
blobb3349007651a20d019da96f63e06880b4989bf0e
1 // Copyright 2015 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 // Implementation of custom bindings for the contextMenus API.
6 // This is used to implement the contextMenus API for extensions and for the
7 // <webview> tag (see chrome_web_view_experimental.js).
9 var contextMenuNatives = requireNative('context_menus');
10 var sendRequest = require('sendRequest').sendRequest;
11 var Event = require('event_bindings').Event;
12 var lastError = require('lastError');
14 // Add the bindings to the contextMenus API.
15 function createContextMenusHandlers(isWebview) {
16   var eventName = isWebview ? 'webViewInternal.contextMenus' : 'contextMenus';
17   // Some dummy value for chrome.contextMenus instances.
18   // Webviews use positive integers, and 0 to denote an invalid webview ID.
19   // The following constant is -1 to avoid any conflicts between webview IDs and
20   // extensions.
21   var INSTANCEID_NON_WEBVIEW = -1;
23   // Generates a customCallback for a given method. |handleCallback| will be
24   // invoked with |request.args| as parameters.
25   function createCustomCallback(handleCallback) {
26     return function(name, request, callback) {
27       if (lastError.hasError(chrome)) {
28         if (callback)
29           callback();
30         return;
31       }
32       var args = request.args;
33       if (!isWebview) {
34         // <webview>s have an extra item in front of the parameter list, which
35         // specifies the viewInstanceId of the webview. This is used to hide
36         // context menu events in one webview from another.
37         // The non-webview chrome.contextMenus API is not called with such an
38         // ID, so we prepend an ID to match the function signature.
39         args = $Array.concat([INSTANCEID_NON_WEBVIEW], args);
40       }
41       $Function.apply(handleCallback, null, args);
42       if (callback)
43         callback();
44     };
45   }
47   var contextMenus = {};
48   contextMenus.handlers = {};
49   contextMenus.event = new Event(eventName);
51   contextMenus.getIdFromCreateProperties = function(createProperties) {
52     if (typeof createProperties.id !== 'undefined')
53       return createProperties.id;
54     return createProperties.generatedId;
55   };
57   contextMenus.handlersForId = function(instanceId, id) {
58     if (!contextMenus.handlers[instanceId]) {
59       contextMenus.handlers[instanceId] = {
60         generated: {},
61         string: {}
62       };
63     }
64     if (typeof id === 'number')
65       return contextMenus.handlers[instanceId].generated;
66     return contextMenus.handlers[instanceId].string;
67   };
69   contextMenus.ensureListenerSetup = function() {
70     if (contextMenus.listening) {
71       return;
72     }
73     contextMenus.listening = true;
74     contextMenus.event.addListener(function(info) {
75       var instanceId = INSTANCEID_NON_WEBVIEW;
76       if (isWebview) {
77         instanceId = info.webviewInstanceId;
78         // Don't expose |webviewInstanceId| via the public API.
79         delete info.webviewInstanceId;
80       }
82       var id = info.menuItemId;
83       var onclick = contextMenus.handlersForId(instanceId, id)[id];
84       if (onclick) {
85         $Function.apply(onclick, null, arguments);
86       }
87     });
88   };
90   // To be used with apiFunctions.setHandleRequest
91   var requestHandlers = {};
92   // To be used with apiFunctions.setCustomCallback
93   var callbacks = {};
95   requestHandlers.create = function() {
96     var createProperties = isWebview ? arguments[1] : arguments[0];
97     createProperties.generatedId = contextMenuNatives.GetNextContextMenuId();
98     var optArgs = {
99       customCallback: this.customCallback,
100     };
101     sendRequest(this.name, arguments, this.definition.parameters, optArgs);
102     return contextMenus.getIdFromCreateProperties(createProperties);
103   };
105   callbacks.create =
106       createCustomCallback(function(instanceId, createProperties) {
107     var id = contextMenus.getIdFromCreateProperties(createProperties);
108     var onclick = createProperties.onclick;
109     if (onclick) {
110       contextMenus.ensureListenerSetup();
111       contextMenus.handlersForId(instanceId, id)[id] = onclick;
112     }
113   });
115   callbacks.remove = createCustomCallback(function(instanceId, id) {
116     delete contextMenus.handlersForId(instanceId, id)[id];
117   });
119   callbacks.update =
120       createCustomCallback(function(instanceId, id, updateProperties) {
121     var onclick = updateProperties.onclick;
122     if (onclick) {
123       contextMenus.ensureListenerSetup();
124       contextMenus.handlersForId(instanceId, id)[id] = onclick;
125     } else if (onclick === null) {
126       // When onclick is explicitly set to null, remove the event listener.
127       delete contextMenus.handlersForId(instanceId, id)[id];
128     }
129   });
131   callbacks.removeAll = createCustomCallback(function(instanceId) {
132     delete contextMenus.handlers[instanceId];
133   });
135   return {
136     requestHandlers: requestHandlers,
137     callbacks: callbacks
138   };
141 exports.create = createContextMenusHandlers;