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

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

Issue 9965005: Deprecate chrome.extension.sendRequest in favor of sendMessage, with a safer (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: sync Created 8 years, 8 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 script contains unprivileged javascript APIs related to chrome 5 // This script contains unprivileged javascript APIs related to chrome
6 // extensions. It is loaded by any extension-related context, such as content 6 // extensions. It is loaded by any extension-related context, such as content
7 // scripts or background pages. 7 // scripts or background pages.
8 // See user_script_slave.cc for script that is loaded by content scripts only. 8 // See user_script_slave.cc for script that is loaded by content scripts only.
9 9
10 require('json_schema'); 10 require('json_schema');
11 require('event_bindings'); 11 require('event_bindings');
12 var miscNatives = requireNative('miscellaneous_bindings'); 12 var miscNatives = requireNative('miscellaneous_bindings');
13 var CloseChannel = miscNatives.CloseChannel; 13 var CloseChannel = miscNatives.CloseChannel;
14 var PortAddRef = miscNatives.PortAddRef; 14 var PortAddRef = miscNatives.PortAddRef;
15 var PortRelease = miscNatives.PortRelease; 15 var PortRelease = miscNatives.PortRelease;
16 var PostMessage = miscNatives.PostMessage; 16 var PostMessage = miscNatives.PostMessage;
17 var BindToGC = miscNatives.BindToGC; 17 var BindToGC = miscNatives.BindToGC;
18 18
19 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); 19 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
20 var manifestVersion; 20 var manifestVersion;
21 var extensionId; 21 var extensionId;
22 22
23 // The reserved channel name for the sendRequest API. 23 // The reserved channel name for the sendRequest/sendMessage APIs.
24 // Note: sendRequest is deprecated.
24 chromeHidden.kRequestChannel = "chrome.extension.sendRequest"; 25 chromeHidden.kRequestChannel = "chrome.extension.sendRequest";
26 chromeHidden.kMessageChannel = "chrome.extension.sendMessage";
25 27
26 // Map of port IDs to port object. 28 // Map of port IDs to port object.
27 var ports = {}; 29 var ports = {};
28 30
29 // Map of port IDs to chromeHidden.onUnload listeners. Keep track of these 31 // Map of port IDs to chromeHidden.onUnload listeners. Keep track of these
30 // to free the onUnload listeners when ports are closed. 32 // to free the onUnload listeners when ports are closed.
31 var portReleasers = {}; 33 var portReleasers = {};
32 34
33 // Change even to odd and vice versa, to get the other side of a given 35 // Change even to odd and vice versa, to get the other side of a given
34 // channel. 36 // channel.
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
88 } 90 }
89 var port = new PortImpl(portId, opt_name); 91 var port = new PortImpl(portId, opt_name);
90 ports[portId] = port; 92 ports[portId] = port;
91 portReleasers[portId] = PortRelease.bind(this, portId); 93 portReleasers[portId] = PortRelease.bind(this, portId);
92 chromeHidden.onUnload.addListener(portReleasers[portId]); 94 chromeHidden.onUnload.addListener(portReleasers[portId]);
93 95
94 PortAddRef(portId); 96 PortAddRef(portId);
95 return port; 97 return port;
96 }; 98 };
97 99
100 // Helper function for dispatchOnRequest.
101 function handleSendRequestError(isSendMessage, responseCallbackPreserved,
102 sourceExtensionId, targetExtensionId) {
103 var errorMsg;
104 var eventName = (isSendMessage ?
105 "chrome.extension.onMessage" : "chrome.extension.onRequest");
106 if (isSendMessage && !responseCallbackPreserved) {
107 errorMsg =
108 "The " + eventName + " listener must return true if you want to" +
109 " send a response after the listener returns ";
110 } else {
111 errorMsg =
112 "Cannot send a response more than once per " + eventName +
113 " listener per document";
114 }
115 errorMsg += " (message was sent by extension " + sourceExtensionId;
116 if (sourceExtensionId != targetExtensionId)
117 errorMsg += " for extension " + targetExtensionId;
118 errorMsg += ").";
119 chrome.extension.lastError = {"message": errorMsg};
120 console.error("Could not send response: " + errorMsg);
121 }
122
123 // Helper function for dispatchOnConnect
98 function dispatchOnRequest(portId, channelName, sender, 124 function dispatchOnRequest(portId, channelName, sender,
99 sourceExtensionId, targetExtensionId, 125 sourceExtensionId, targetExtensionId,
100 isExternal) { 126 isExternal) {
101 var requestEvent = (isExternal ? 127 var isSendMessage = channelName == chromeHidden.kMessageChannel;
102 chrome.extension.onRequestExternal : chrome.extension.onRequest); 128 var requestEvent = (isSendMessage ?
129 (isExternal ?
130 chrome.extension.onMessageExternal : chrome.extension.onMessage) :
131 (isExternal ?
132 chrome.extension.onRequestExternal : chrome.extension.onRequest));
103 if (requestEvent.hasListeners()) { 133 if (requestEvent.hasListeners()) {
104 var port = chromeHidden.Port.createPort(portId, channelName); 134 var port = chromeHidden.Port.createPort(portId, channelName);
105 port.onMessage.addListener(function(request) { 135 port.onMessage.addListener(function(request) {
136 var responseCallbackPreserved = false;
106 var responseCallback = function(response) { 137 var responseCallback = function(response) {
107 if (port) { 138 if (port) {
108 port.postMessage(response); 139 port.postMessage(response);
109 port.destroy_(); 140 port.destroy_();
110 port = null; 141 port = null;
111 } else { 142 } else {
112 // We nulled out port when sending the response, and now the page 143 // We nulled out port when sending the response, and now the page
113 // is trying to send another response for the same request. 144 // is trying to send another response for the same request.
114 var errorMsg = 145 handleSendRequestError(isSendMessage, responseCallbackPreserved,
115 "Cannot send a response more than once per " + 146 sourceExtensionId, targetExtensionId);
116 "chrome.extension.onRequest listener per document (message " +
117 "was sent by extension " + sourceExtensionId;
118 if (sourceExtensionId != targetExtensionId) {
119 errorMsg += " for extension " + targetExtensionId;
120 }
121 errorMsg += ").";
122 chrome.extension.lastError = {"message": errorMsg};
123 console.error("Could not send response: " + errorMsg);
124 } 147 }
125 }; 148 };
126 // In case the extension never invokes the responseCallback, and also 149 // In case the extension never invokes the responseCallback, and also
127 // doesn't keep a reference to it, we need to clean up the port. Do 150 // doesn't keep a reference to it, we need to clean up the port. Do
128 // so by attaching to the garbage collection of the responseCallback 151 // so by attaching to the garbage collection of the responseCallback
129 // using some native hackery. 152 // using some native hackery.
130 BindToGC(responseCallback, function() { 153 BindToGC(responseCallback, function() {
131 if (port) { 154 if (port) {
132 port.destroy_(); 155 port.destroy_();
133 port = null; 156 port = null;
134 } 157 }
135 }); 158 });
136 requestEvent.dispatch(request, sender, responseCallback); 159 if (!isSendMessage) {
160 requestEvent.dispatch(request, sender, responseCallback);
161 } else {
162 var rv = requestEvent.dispatch(request, sender, responseCallback);
163 responseCallbackPreserved =
164 rv && rv.results && rv.results.indexOf(true) > -1;
165 if (!responseCallbackPreserved) {
166 // If they didn't access the response callback, they're not
167 // going to send a response, so clean up the port immediately.
168 port.destroy_();
169 port = null;
170 }
171 }
137 }); 172 });
138 return true; 173 return true;
139 } 174 }
140 return false; 175 return false;
141 } 176 }
142 177
143 // Called by native code when a channel has been opened to this context. 178 // Called by native code when a channel has been opened to this context.
144 chromeHidden.Port.dispatchOnConnect = function(portId, channelName, tab, 179 chromeHidden.Port.dispatchOnConnect = function(portId, channelName, tab,
145 sourceExtensionId, 180 sourceExtensionId,
146 targetExtensionId) { 181 targetExtensionId) {
147 // Only create a new Port if someone is actually listening for a connection. 182 // Only create a new Port if someone is actually listening for a connection.
148 // In addition to being an optimization, this also fixes a bug where if 2 183 // In addition to being an optimization, this also fixes a bug where if 2
149 // channels were opened to and from the same process, closing one would 184 // channels were opened to and from the same process, closing one would
150 // close both. 185 // close both.
151 if (targetExtensionId != extensionId) 186 if (targetExtensionId != extensionId)
152 return false; // not for us 187 return false; // not for us
153 if (ports[getOppositePortId(portId)]) 188 if (ports[getOppositePortId(portId)])
154 return false; // this channel was opened by us, so ignore it 189 return false; // this channel was opened by us, so ignore it
155 190
156 // Determine whether this is coming from another extension, so we can use 191 // Determine whether this is coming from another extension, so we can use
157 // the right event. 192 // the right event.
158 var isExternal = sourceExtensionId != extensionId; 193 var isExternal = sourceExtensionId != extensionId;
159 194
160 if (tab) 195 if (tab)
161 tab = chromeHidden.JSON.parse(tab); 196 tab = chromeHidden.JSON.parse(tab);
162 var sender = {tab: tab, id: sourceExtensionId}; 197 var sender = {tab: tab, id: sourceExtensionId};
163 198
164 // Special case for sendRequest/onRequest. 199 // Special case for sendRequest/onRequest and sendMessage/onMessage.
165 if (channelName == chromeHidden.kRequestChannel) { 200 if (channelName == chromeHidden.kRequestChannel ||
201 channelName == chromeHidden.kMessageChannel) {
166 return dispatchOnRequest(portId, channelName, sender, 202 return dispatchOnRequest(portId, channelName, sender,
167 sourceExtensionId, targetExtensionId, 203 sourceExtensionId, targetExtensionId,
168 isExternal); 204 isExternal);
169 } 205 }
170 206
171 var connectEvent = (isExternal ? 207 var connectEvent = (isExternal ?
172 chrome.extension.onConnectExternal : chrome.extension.onConnect); 208 chrome.extension.onConnectExternal : chrome.extension.onConnect);
173 if (connectEvent.hasListeners()) { 209 if (connectEvent.hasListeners()) {
174 var port = chromeHidden.Port.createPort(portId, channelName); 210 var port = chromeHidden.Port.createPort(portId, channelName);
175 port.sender = sender; 211 port.sender = sender;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 chromeHidden.Port.dispatchOnMessage = function(msg, portId) { 244 chromeHidden.Port.dispatchOnMessage = function(msg, portId) {
209 var port = ports[portId]; 245 var port = ports[portId];
210 if (port) { 246 if (port) {
211 if (msg) { 247 if (msg) {
212 msg = chromeHidden.JSON.parse(msg); 248 msg = chromeHidden.JSON.parse(msg);
213 } 249 }
214 port.onMessage.dispatch(msg, port); 250 port.onMessage.dispatch(msg, port);
215 } 251 }
216 }; 252 };
217 253
254 // Shared implementation used by tabs.sendMessage and extension.sendMessage.
255 chromeHidden.Port.sendMessageImpl = function(port, request,
256 responseCallback) {
257 port.postMessage(request);
258
259 if (port.name == chromeHidden.kMessageChannel && !responseCallback) {
260 // TODO(mpcomplete): Do this for the old sendRequest API too, after
261 // verifying it doesn't break anything.
262 // Go ahead and disconnect immediately if the sender is not expecting
263 // a response.
264 port.disconnect();
265 return;
266 }
267
268 // Ensure the callback exists for the older sendRequest API.
269 if (!responseCallback)
270 responseCallback = function() {};
271
272 port.onDisconnect.addListener(function() {
273 // For onDisconnects, we only notify the callback if there was an error
274 try {
275 if (chrome.extension.lastError)
276 responseCallback();
277 } finally {
278 port = null;
279 }
280 });
281 port.onMessage.addListener(function(response) {
282 try {
283 responseCallback(response);
284 } finally {
285 port.disconnect();
286 port = null;
287 }
288 });
289 }
290
218 // This function is called on context initialization for both content scripts 291 // This function is called on context initialization for both content scripts
219 // and extension contexts. 292 // and extension contexts.
220 chromeHidden.onLoad.addListener(function(tempExtensionId, 293 chromeHidden.onLoad.addListener(function(tempExtensionId,
221 isExtensionProcess, 294 isExtensionProcess,
222 inIncognitoContext, 295 inIncognitoContext,
223 tempManifestVersion) { 296 tempManifestVersion) {
224 extensionId = tempExtensionId; 297 extensionId = tempExtensionId;
225 manifestVersion = tempManifestVersion; 298 manifestVersion = tempManifestVersion;
226 299
227 chrome.extension = chrome.extension || {}; 300 chrome.extension = chrome.extension || {};
228 301
229 if (manifestVersion < 2) { 302 if (manifestVersion < 2) {
230 chrome.self = chrome.extension; 303 chrome.self = chrome.extension;
231 chrome.extension.inIncognitoTab = inIncognitoContext; 304 chrome.extension.inIncognitoTab = inIncognitoContext;
232 } 305 }
233 306
234 chrome.extension.inIncognitoContext = inIncognitoContext; 307 chrome.extension.inIncognitoContext = inIncognitoContext;
235 }); 308 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698