Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(407)

Side by Side Diff: chrome/renderer/resources/extensions/miscellaneous_bindings.js

Issue 15841013: Make miscellaneous_bindings and event_bindings Required as needed. Previously (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 contains unprivileged javascript APIs for extensions and apps. It 5 // This contains unprivileged javascript APIs for extensions and apps. It
6 // can be loaded by any extension-related context, such as content scripts or 6 // can be loaded by any extension-related context, such as content scripts or
7 // background pages. See user_script_slave.cc for script that is loaded by 7 // background pages. See user_script_slave.cc for script that is loaded by
8 // content scripts only. 8 // content scripts only.
9 9
10 require('json_schema'); 10 var chrome = requireNative('chrome').GetChrome();
11 var json = require('json'); 11 var Event = require('event_bindings').Event;
12 var lastError = require('lastError'); 12 var lastError = require('lastError');
13 var miscNatives = requireNative('miscellaneous_bindings'); 13 var logActivity = requireNative('activityLogger');
14 var chrome = requireNative('chrome').GetChrome(); 14 var miscNatives = requireNative('miscellaneous_bindings_natives');
15 var CloseChannel = miscNatives.CloseChannel; 15 var onUnload = require('on_unload');
16 var PortAddRef = miscNatives.PortAddRef; 16 var processNatives = requireNative('process');
17 var PortRelease = miscNatives.PortRelease;
18 var PostMessage = miscNatives.PostMessage;
19 var BindToGC = miscNatives.BindToGC;
20 17
21 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); 18 // The reserved channel name for the sendRequest/send(Native)Message APIs.
22
23 var processNatives = requireNative('process');
24 var manifestVersion = processNatives.GetManifestVersion();
25 var extensionId = processNatives.GetExtensionId();
26
27 var logActivity = requireNative('activityLogger');
28
29 // The reserved channel name for the sendRequest/sendMessage APIs.
30 // Note: sendRequest is deprecated. 19 // Note: sendRequest is deprecated.
31 chromeHidden.kRequestChannel = "chrome.extension.sendRequest"; 20 var kRequestChannel = "chrome.extension.sendRequest";
32 chromeHidden.kMessageChannel = "chrome.runtime.sendMessage"; 21 var kMessageChannel = "chrome.runtime.sendMessage";
33 chromeHidden.kNativeMessageChannel = "chrome.runtime.sendNativeMessage"; 22 var kNativeMessageChannel = "chrome.runtime.sendNativeMessage";
34 23
35 // Map of port IDs to port object. 24 // Map of port IDs to port object.
36 var ports = {}; 25 var ports = {};
37 26
38 // Map of port IDs to chromeHidden.onUnload listeners. Keep track of these 27 // Map of port IDs to onUnload listeners. Keep track of these to free the
39 // to free the onUnload listeners when ports are closed. 28 // onUnload listeners when ports are closed.
40 var portReleasers = {}; 29 var portReleasers = {};
41 30
42 // Change even to odd and vice versa, to get the other side of a given 31 // Change even to odd and vice versa, to get the other side of a given
43 // channel. 32 // channel.
44 function getOppositePortId(portId) { return portId ^ 1; } 33 function getOppositePortId(portId) { return portId ^ 1; }
45 34
46 // Port object. Represents a connection to another script context through 35 // Port object. Represents a connection to another script context through
47 // which messages can be passed. 36 // which messages can be passed.
48 function PortImpl(portId, opt_name) { 37 function Port(portId, opt_name) {
49 this.portId_ = portId; 38 this.portId_ = portId;
50 this.name = opt_name; 39 this.name = opt_name;
51 this.onDisconnect = new chrome.Event(); 40 this.onDisconnect = new Event();
52 this.onMessage = new chrome.Event(); 41 this.onMessage = new Event();
53 } 42 }
54 43
55 // Sends a message asynchronously to the context on the other end of this 44 // Sends a message asynchronously to the context on the other end of this
56 // port. 45 // port.
57 PortImpl.prototype.postMessage = function(msg) { 46 Port.prototype.postMessage = function(msg) {
58 // json.stringify doesn't support a root object which is undefined. 47 miscNatives.PostMessage(this.portId_, msg);
59 if (msg === undefined)
60 msg = null;
61 PostMessage(this.portId_, json.stringify(msg));
62 }; 48 };
63 49
64 // Disconnects the port from the other end. 50 // Disconnects the port from the other end.
65 PortImpl.prototype.disconnect = function() { 51 Port.prototype.disconnect = function() {
66 CloseChannel(this.portId_, true); 52 miscNatives.CloseChannel(this.portId_, true);
67 this.destroy_(); 53 this.destroy_();
68 }; 54 };
69 55
70 PortImpl.prototype.destroy_ = function() { 56 Port.prototype.destroy_ = function() {
71 var portId = this.portId_; 57 var portId = this.portId_;
72 58
73 this.onDisconnect.destroy_(); 59 this.onDisconnect.destroy_();
74 this.onMessage.destroy_(); 60 this.onMessage.destroy_();
75 61
76 PortRelease(portId); 62 miscNatives.PortRelease(portId);
77 chromeHidden.onUnload.removeListener(portReleasers[portId]); 63 onUnload.removeListener(portReleasers[portId]);
78 64
79 delete ports[portId]; 65 delete ports[portId];
80 delete portReleasers[portId]; 66 delete portReleasers[portId];
81 }; 67 };
82 68
83 chromeHidden.Port = {};
84
85 // Returns true if the specified port id is in this context. This is used by 69 // Returns true if the specified port id is in this context. This is used by
86 // the C++ to avoid creating the javascript message for all the contexts that 70 // the C++ to avoid creating the javascript message for all the contexts that
87 // don't care about a particular message. 71 // don't care about a particular message.
88 chromeHidden.Port.hasPort = function(portId) { 72 function hasPort(portId) {
89 return portId in ports; 73 return portId in ports;
90 }; 74 };
91 75
92 // Hidden port creation function. We don't want to expose an API that lets 76 // Hidden port creation function. We don't want to expose an API that lets
93 // people add arbitrary port IDs to the port list. 77 // people add arbitrary port IDs to the port list.
94 chromeHidden.Port.createPort = function(portId, opt_name) { 78 function createPort(portId, opt_name) {
95 if (ports[portId]) { 79 if (ports[portId])
96 throw new Error("Port '" + portId + "' already exists."); 80 throw new Error("Port '" + portId + "' already exists.");
97 } 81 var port = new Port(portId, opt_name);
98 var port = new PortImpl(portId, opt_name);
99 ports[portId] = port; 82 ports[portId] = port;
100 portReleasers[portId] = PortRelease.bind(this, portId); 83 portReleasers[portId] = miscNatives.PortRelease.bind(this, portId);
101 chromeHidden.onUnload.addListener(portReleasers[portId]); 84 onUnload.addListener(portReleasers[portId]);
102 85 miscNatives.PortAddRef(portId);
103 PortAddRef(portId);
104 return port; 86 return port;
105 }; 87 };
106 88
107 // Helper function for dispatchOnRequest. 89 // Helper function for dispatchOnRequest.
108 function handleSendRequestError(isSendMessage, 90 function handleSendRequestError(isSendMessage,
109 responseCallbackPreserved, 91 responseCallbackPreserved,
110 sourceExtensionId, 92 sourceExtensionId,
111 targetExtensionId, 93 targetExtensionId,
112 sourceUrl) { 94 sourceUrl) {
113 var errorMsg = []; 95 var errorMsg = [];
(...skipping 12 matching lines...) Expand all
126 errorMsg.push("for extension " + targetExtensionId); 108 errorMsg.push("for extension " + targetExtensionId);
127 if (sourceUrl != "") 109 if (sourceUrl != "")
128 errorMsg.push("for URL " + sourceUrl); 110 errorMsg.push("for URL " + sourceUrl);
129 lastError.set(eventName, errorMsg.join(" ") + ").", null, chrome); 111 lastError.set(eventName, errorMsg.join(" ") + ").", null, chrome);
130 } 112 }
131 113
132 // Helper function for dispatchOnConnect 114 // Helper function for dispatchOnConnect
133 function dispatchOnRequest(portId, channelName, sender, 115 function dispatchOnRequest(portId, channelName, sender,
134 sourceExtensionId, targetExtensionId, sourceUrl, 116 sourceExtensionId, targetExtensionId, sourceUrl,
135 isExternal) { 117 isExternal) {
136 var isSendMessage = channelName == chromeHidden.kMessageChannel; 118 var isSendMessage = channelName == kMessageChannel;
137 var requestEvent = (isSendMessage ? 119 var requestEvent = (isSendMessage ?
138 (isExternal ? 120 (isExternal ?
139 chrome.runtime.onMessageExternal : chrome.runtime.onMessage) : 121 chrome.runtime.onMessageExternal : chrome.runtime.onMessage) :
140 (isExternal ? 122 (isExternal ?
141 chrome.extension.onRequestExternal : chrome.extension.onRequest)); 123 chrome.extension.onRequestExternal : chrome.extension.onRequest));
142 if (requestEvent.hasListeners()) { 124 if (!requestEvent.hasListeners())
143 var port = chromeHidden.Port.createPort(portId, channelName); 125 return false;
144 port.onMessage.addListener(function(request) { 126 var port = createPort(portId, channelName);
145 var responseCallbackPreserved = false; 127 port.onMessage.addListener(function(request) {
146 var responseCallback = function(response) { 128 var responseCallbackPreserved = false;
147 if (port) { 129 var responseCallback = function(response) {
148 port.postMessage(response); 130 if (port) {
149 port.destroy_(); 131 port.postMessage(response);
150 port = null; 132 port.destroy_();
151 } else { 133 port = null;
152 // We nulled out port when sending the response, and now the page
153 // is trying to send another response for the same request.
154 handleSendRequestError(isSendMessage, responseCallbackPreserved,
155 sourceExtensionId, targetExtensionId);
156 }
157 };
158 // In case the extension never invokes the responseCallback, and also
159 // doesn't keep a reference to it, we need to clean up the port. Do
160 // so by attaching to the garbage collection of the responseCallback
161 // using some native hackery.
162 BindToGC(responseCallback, function() {
163 if (port) {
164 port.destroy_();
165 port = null;
166 }
167 });
168 if (!isSendMessage) {
169 requestEvent.dispatch(request, sender, responseCallback);
170 } else { 134 } else {
171 var rv = requestEvent.dispatch(request, sender, responseCallback); 135 // We nulled out port when sending the response, and now the page
172 responseCallbackPreserved = 136 // is trying to send another response for the same request.
173 rv && rv.results && rv.results.indexOf(true) > -1; 137 handleSendRequestError(isSendMessage, responseCallbackPreserved,
174 if (!responseCallbackPreserved && port) { 138 sourceExtensionId, targetExtensionId);
175 // If they didn't access the response callback, they're not 139 }
176 // going to send a response, so clean up the port immediately. 140 };
177 port.destroy_(); 141 // In case the extension never invokes the responseCallback, and also
178 port = null; 142 // doesn't keep a reference to it, we need to clean up the port. Do
179 } 143 // so by attaching to the garbage collection of the responseCallback
144 // using some native hackery.
145 miscNatives.BindToGC(responseCallback, function() {
146 if (port) {
147 port.destroy_();
148 port = null;
180 } 149 }
181 }); 150 });
182 var eventName = (isSendMessage ? 151 if (!isSendMessage) {
183 (isExternal ? 152 requestEvent.dispatch(request, sender, responseCallback);
184 "runtime.onMessageExternal" : "runtime.onMessage") : 153 } else {
185 (isExternal ? 154 var rv = requestEvent.dispatch(request, sender, responseCallback);
186 "extension.onRequestExternal" : "extension.onRequest")); 155 responseCallbackPreserved =
187 logActivity.LogEvent(targetExtensionId, 156 rv && rv.results && rv.results.indexOf(true) > -1;
188 eventName, 157 if (!responseCallbackPreserved && port) {
189 [sourceExtensionId, sourceUrl]); 158 // If they didn't access the response callback, they're not
190 return true; 159 // going to send a response, so clean up the port immediately.
191 } 160 port.destroy_();
192 return false; 161 port = null;
162 }
163 }
164 });
165 var eventName = (isSendMessage ?
166 (isExternal ?
167 "runtime.onMessageExternal" : "runtime.onMessage") :
168 (isExternal ?
169 "extension.onRequestExternal" : "extension.onRequest"));
170 logActivity.LogEvent(targetExtensionId,
171 eventName,
172 [sourceExtensionId, sourceUrl]);
173 return true;
193 } 174 }
194 175
195 // Called by native code when a channel has been opened to this context. 176 // Called by native code when a channel has been opened to this context.
196 chromeHidden.Port.dispatchOnConnect = function(portId, 177 function dispatchOnConnect(portId,
197 channelName, 178 channelName,
198 sourceTab, 179 sourceTab,
199 sourceExtensionId, 180 sourceExtensionId,
200 targetExtensionId, 181 targetExtensionId,
201 sourceUrl) { 182 sourceUrl) {
202 // Only create a new Port if someone is actually listening for a connection. 183 // Only create a new Port if someone is actually listening for a connection.
203 // In addition to being an optimization, this also fixes a bug where if 2 184 // In addition to being an optimization, this also fixes a bug where if 2
204 // channels were opened to and from the same process, closing one would 185 // channels were opened to and from the same process, closing one would
205 // close both. 186 // close both.
187 var extensionId = processNatives.GetExtensionId();
206 if (targetExtensionId != extensionId) 188 if (targetExtensionId != extensionId)
207 return false; // not for us 189 return false; // not for us
190
208 if (ports[getOppositePortId(portId)]) 191 if (ports[getOppositePortId(portId)])
209 return false; // this channel was opened by us, so ignore it 192 return false; // this channel was opened by us, so ignore it
210 193
211 // Determine whether this is coming from another extension, so we can use 194 // Determine whether this is coming from another extension, so we can use
212 // the right event. 195 // the right event.
213 var isExternal = sourceExtensionId != extensionId; 196 var isExternal = sourceExtensionId != extensionId;
214 197
215 var sender = {id: sourceExtensionId}; 198 var sender = {id: sourceExtensionId};
216 if (sourceUrl) 199 if (sourceUrl)
217 sender.url = sourceUrl; 200 sender.url = sourceUrl;
218 if (sourceTab) 201 if (sourceTab)
219 sender.tab = sourceTab; 202 sender.tab = sourceTab;
220 203
221 // Special case for sendRequest/onRequest and sendMessage/onMessage. 204 // Special case for sendRequest/onRequest and sendMessage/onMessage.
222 if (channelName == chromeHidden.kRequestChannel || 205 if (channelName == kRequestChannel || channelName == kMessageChannel) {
223 channelName == chromeHidden.kMessageChannel) {
224 return dispatchOnRequest(portId, channelName, sender, 206 return dispatchOnRequest(portId, channelName, sender,
225 sourceExtensionId, targetExtensionId, sourceUrl, 207 sourceExtensionId, targetExtensionId, sourceUrl,
226 isExternal); 208 isExternal);
227 } 209 }
228 210
229 var connectEvent = (isExternal ? 211 var connectEvent = (isExternal ?
230 chrome.runtime.onConnectExternal : chrome.runtime.onConnect); 212 chrome.runtime.onConnectExternal : chrome.runtime.onConnect);
231 if (connectEvent.hasListeners()) { 213 if (!connectEvent)
232 var port = chromeHidden.Port.createPort(portId, channelName); 214 return false;
233 port.sender = sender; 215 if (!connectEvent.hasListeners())
234 if (manifestVersion < 2) 216 return false;
235 port.tab = port.sender.tab;
236 217
237 var eventName = (isExternal ? 218 var port = createPort(portId, channelName);
238 "runtime.onConnectExternal" : "runtime.onConnect"); 219 port.sender = sender;
239 connectEvent.dispatch(port); 220 if (processNatives.manifestVersion < 2)
240 logActivity.LogEvent(targetExtensionId, 221 port.tab = port.sender.tab;
241 eventName, 222
242 [sourceExtensionId]); 223 var eventName = (isExternal ?
243 return true; 224 "runtime.onConnectExternal" : "runtime.onConnect");
244 } 225 connectEvent.dispatch(port);
245 return false; 226 logActivity.LogEvent(targetExtensionId,
227 eventName,
228 [sourceExtensionId]);
229 return true;
246 }; 230 };
247 231
248 // Called by native code when a channel has been closed. 232 // Called by native code when a channel has been closed.
249 chromeHidden.Port.dispatchOnDisconnect = function( 233 function dispatchOnDisconnect(portId, errorMessage) {
250 portId, errorMessage) {
251 var port = ports[portId]; 234 var port = ports[portId];
252 if (port) { 235 if (port) {
253 // Update the renderer's port bookkeeping, without notifying the browser. 236 // Update the renderer's port bookkeeping, without notifying the browser.
254 CloseChannel(portId, false); 237 miscNatives.CloseChannel(portId, false);
255 if (errorMessage) 238 if (errorMessage)
256 lastError.set('Port', errorMessage, null, chrome); 239 lastError.set('Port', errorMessage, null, chrome);
257 try { 240 try {
258 port.onDisconnect.dispatch(port); 241 port.onDisconnect.dispatch(port);
259 } finally { 242 } finally {
260 port.destroy_(); 243 port.destroy_();
261 lastError.clear(chrome); 244 lastError.clear(chrome);
262 } 245 }
263 } 246 }
264 }; 247 };
265 248
266 // Called by native code when a message has been sent to the given port. 249 // Called by native code when a message has been sent to the given port.
267 chromeHidden.Port.dispatchOnMessage = function(msg, portId) { 250 function dispatchOnMessage(msg, portId) {
268 var port = ports[portId]; 251 var port = ports[portId];
269 if (port) { 252 if (port)
270 if (msg) {
271 msg = json.parse(msg);
272 }
273 port.onMessage.dispatch(msg, port); 253 port.onMessage.dispatch(msg, port);
274 }
275 }; 254 };
276 255
277 // Shared implementation used by tabs.sendMessage and runtime.sendMessage. 256 // Shared implementation used by tabs.sendMessage and runtime.sendMessage.
278 chromeHidden.Port.sendMessageImpl = function(port, request, 257 function sendMessageImpl(port, request, responseCallback) {
279 responseCallback) { 258 if (port.name != kNativeMessageChannel)
280 if (port.name != chromeHidden.kNativeMessageChannel)
281 port.postMessage(request); 259 port.postMessage(request);
282 260
283 if (port.name == chromeHidden.kMessageChannel && !responseCallback) { 261 if (port.name == kMessageChannel && !responseCallback) {
284 // TODO(mpcomplete): Do this for the old sendRequest API too, after 262 // TODO(mpcomplete): Do this for the old sendRequest API too, after
285 // verifying it doesn't break anything. 263 // verifying it doesn't break anything.
286 // Go ahead and disconnect immediately if the sender is not expecting 264 // Go ahead and disconnect immediately if the sender is not expecting
287 // a response. 265 // a response.
288 port.disconnect(); 266 port.disconnect();
289 return; 267 return;
290 } 268 }
291 269
292 // Ensure the callback exists for the older sendRequest API. 270 // Ensure the callback exists for the older sendRequest API.
293 if (!responseCallback) 271 if (!responseCallback)
294 responseCallback = function() {}; 272 responseCallback = function() {};
295 273
296 port.onDisconnect.addListener(function() { 274 port.onDisconnect.addListener(function() {
297 // For onDisconnects, we only notify the callback if there was an error 275 // For onDisconnects, we only notify the callback if there was an error.
298 try { 276 try {
299 if (chrome.runtime.lastError) 277 if (chrome.runtime.lastError)
300 responseCallback(); 278 responseCallback();
301 } finally { 279 } finally {
302 port = null; 280 port = null;
303 } 281 }
304 }); 282 });
305 port.onMessage.addListener(function(response) { 283 port.onMessage.addListener(function(response) {
306 try { 284 try {
307 responseCallback(response); 285 responseCallback(response);
(...skipping 23 matching lines...) Expand all
331 // targetId (first argument, extensionId in the manfiest) is optional. 309 // targetId (first argument, extensionId in the manfiest) is optional.
332 var targetId = null; 310 var targetId = null;
333 if (lastArg >= 0) 311 if (lastArg >= 0)
334 targetId = args[lastArg--]; 312 targetId = args[lastArg--];
335 313
336 if (lastArg != -1) 314 if (lastArg != -1)
337 throw new Error('Invalid arguments to ' + functionName + '.'); 315 throw new Error('Invalid arguments to ' + functionName + '.');
338 return [targetId, request, responseCallback]; 316 return [targetId, request, responseCallback];
339 } 317 }
340 318
319 exports.kRequestChannel = kRequestChannel;
320 exports.kMessageChannel = kMessageChannel;
321 exports.kNativeMessageChannel = kNativeMessageChannel;
322 exports.Port = Port;
323 exports.createPort = createPort;
324 exports.sendMessageImpl = sendMessageImpl;
341 exports.sendMessageUpdateArguments = sendMessageUpdateArguments; 325 exports.sendMessageUpdateArguments = sendMessageUpdateArguments;
326
327 // For C++ code to call.
328 exports.hasPort = hasPort;
329 exports.dispatchOnConnect = dispatchOnConnect;
330 exports.dispatchOnDisconnect = dispatchOnDisconnect;
331 exports.dispatchOnMessage = dispatchOnMessage;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698