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

Side by Side Diff: chrome/renderer/resources/renderer_extension_bindings.js

Issue 7980055: Move bindings javascript into its own directory. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 3 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
« no previous file with comments | « chrome/renderer/resources/json_schema.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 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.
4
5 // This script contains unprivileged javascript APIs related to chrome
6 // extensions. It is loaded by any extension-related context, such as content
7 // scripts or toolstrips.
8 // See user_script_slave.cc for script that is loaded by content scripts only.
9 // TODO(mpcomplete): we also load this in regular web pages, but don't need to.
10
11 var chrome = chrome || {};
12 (function () {
13 native function OpenChannelToExtension(sourceId, targetId, name);
14 native function CloseChannel(portId, notifyBrowser);
15 native function PortAddRef(portId);
16 native function PortRelease(portId);
17 native function PostMessage(portId, msg);
18 native function GetChromeHidden();
19 native function GetL10nMessage();
20 native function Print();
21
22 var chromeHidden = GetChromeHidden();
23
24 // The reserved channel name for the sendRequest API.
25 chromeHidden.kRequestChannel = "chrome.extension.sendRequest";
26
27 // Map of port IDs to port object.
28 var ports = {};
29
30 // Map of port IDs to chromeHidden.onUnload listeners. Keep track of these
31 // to free the onUnload listeners when ports are closed.
32 var portReleasers = {};
33
34 // Change even to odd and vice versa, to get the other side of a given
35 // channel.
36 function getOppositePortId(portId) { return portId ^ 1; }
37
38 // Port object. Represents a connection to another script context through
39 // which messages can be passed.
40 chrome.Port = function(portId, opt_name) {
41 this.portId_ = portId;
42 this.name = opt_name;
43 this.onDisconnect = new chrome.Event();
44 this.onMessage = new chrome.Event();
45 };
46
47 chromeHidden.Port = {};
48
49 // Returns true if the specified port id is in this context. This is used by
50 // the C++ to avoid creating the javascript message for all the contexts that
51 // don't care about a particular message.
52 chromeHidden.Port.hasPort = function(portId) {
53 return portId in ports;
54 };
55
56 // Hidden port creation function. We don't want to expose an API that lets
57 // people add arbitrary port IDs to the port list.
58 chromeHidden.Port.createPort = function(portId, opt_name) {
59 if (ports[portId]) {
60 throw new Error("Port '" + portId + "' already exists.");
61 }
62 var port = new chrome.Port(portId, opt_name);
63 ports[portId] = port;
64 portReleasers[portId] = PortRelease.bind(this, portId);
65 chromeHidden.onUnload.addListener(portReleasers[portId]);
66
67 PortAddRef(portId);
68 return port;
69 };
70
71 // Called by native code when a channel has been opened to this context.
72 chromeHidden.Port.dispatchOnConnect = function(portId, channelName, tab,
73 sourceExtensionId,
74 targetExtensionId) {
75 // Only create a new Port if someone is actually listening for a connection.
76 // In addition to being an optimization, this also fixes a bug where if 2
77 // channels were opened to and from the same process, closing one would
78 // close both.
79 if (targetExtensionId != chromeHidden.extensionId)
80 return; // not for us
81 if (ports[getOppositePortId(portId)])
82 return; // this channel was opened by us, so ignore it
83
84 // Determine whether this is coming from another extension, so we can use
85 // the right event.
86 var isExternal = sourceExtensionId != chromeHidden.extensionId;
87
88 if (tab)
89 tab = chromeHidden.JSON.parse(tab);
90 var sender = {tab: tab, id: sourceExtensionId};
91
92 // Special case for sendRequest/onRequest.
93 if (channelName == chromeHidden.kRequestChannel) {
94 var requestEvent = (isExternal ?
95 chrome.extension.onRequestExternal : chrome.extension.onRequest);
96 if (requestEvent.hasListeners()) {
97 var port = chromeHidden.Port.createPort(portId, channelName);
98 port.onMessage.addListener(function(request) {
99 requestEvent.dispatch(request, sender, function(response) {
100 port.postMessage(response);
101 port = null;
102 });
103 });
104 }
105 return;
106 }
107
108 var connectEvent = (isExternal ?
109 chrome.extension.onConnectExternal : chrome.extension.onConnect);
110 if (connectEvent.hasListeners()) {
111 var port = chromeHidden.Port.createPort(portId, channelName);
112 port.sender = sender;
113 // TODO(EXTENSIONS_DEPRECATED): port.tab is obsolete.
114 port.tab = port.sender.tab;
115
116 connectEvent.dispatch(port);
117 }
118 };
119
120 // Called by native code when a channel has been closed.
121 chromeHidden.Port.dispatchOnDisconnect = function(
122 portId, connectionInvalid) {
123 var port = ports[portId];
124 if (port) {
125 // Update the renderer's port bookkeeping, without notifying the browser.
126 CloseChannel(portId, false);
127 if (connectionInvalid) {
128 var errorMsg =
129 "Could not establish connection. Receiving end does not exist.";
130 chrome.extension.lastError = {"message": errorMsg};
131 console.error("Port error: " + errorMsg);
132 }
133 try {
134 port.onDisconnect.dispatch(port);
135 } finally {
136 port.destroy_();
137 delete chrome.extension.lastError;
138 }
139 }
140 };
141
142 // Called by native code when a message has been sent to the given port.
143 chromeHidden.Port.dispatchOnMessage = function(msg, portId) {
144 var port = ports[portId];
145 if (port) {
146 if (msg) {
147 msg = chromeHidden.JSON.parse(msg);
148 }
149 port.onMessage.dispatch(msg, port);
150 }
151 };
152
153 // Sends a message asynchronously to the context on the other end of this
154 // port.
155 chrome.Port.prototype.postMessage = function(msg) {
156 // JSON.stringify doesn't support a root object which is undefined.
157 if (msg === undefined)
158 msg = null;
159 PostMessage(this.portId_, chromeHidden.JSON.stringify(msg));
160 };
161
162 // Disconnects the port from the other end.
163 chrome.Port.prototype.disconnect = function() {
164 CloseChannel(this.portId_, true);
165 this.destroy_();
166 };
167
168 chrome.Port.prototype.destroy_ = function() {
169 var portId = this.portId_;
170
171 this.onDisconnect.destroy_();
172 this.onMessage.destroy_();
173
174 PortRelease(portId);
175 chromeHidden.onUnload.removeListener(portReleasers[portId]);
176
177 delete ports[portId];
178 delete portReleasers[portId];
179 };
180
181 // This function is called on context initialization for both content scripts
182 // and extension contexts.
183 chromeHidden.onLoad.addListener(function(extensionId, isExtensionProcess,
184 inIncognitoContext) {
185 chromeHidden.extensionId = extensionId;
186
187 chrome.extension = chrome.extension || {};
188 chrome.self = chrome.extension;
189
190 chrome.extension.inIncognitoTab = inIncognitoContext; // deprecated
191 chrome.extension.inIncognitoContext = inIncognitoContext;
192
193 // Events for when a message channel is opened to our extension.
194 chrome.extension.onConnect = new chrome.Event();
195 chrome.extension.onConnectExternal = new chrome.Event();
196 chrome.extension.onRequest = new chrome.Event();
197 chrome.extension.onRequestExternal = new chrome.Event();
198
199 // Opens a message channel to the given target extension, or the current one
200 // if unspecified. Returns a Port for message passing.
201 chrome.extension.connect = function(targetId_opt, connectInfo_opt) {
202 var name = "";
203 var targetId = extensionId;
204 var nextArg = 0;
205 if (typeof(arguments[nextArg]) == "string")
206 targetId = arguments[nextArg++];
207 if (typeof(arguments[nextArg]) == "object")
208 name = arguments[nextArg++].name || name;
209 if (nextArg != arguments.length)
210 throw new Error("Invalid arguments to connect.");
211
212 var portId = OpenChannelToExtension(extensionId, targetId, name);
213 if (portId >= 0)
214 return chromeHidden.Port.createPort(portId, name);
215 throw new Error("Error connecting to extension '" + targetId + "'");
216 };
217
218 chrome.extension.sendRequest =
219 function(targetId_opt, request, responseCallback_opt) {
220 var targetId = extensionId;
221 var responseCallback = null;
222 var lastArg = arguments.length - 1;
223 if (typeof(arguments[lastArg]) == "function")
224 responseCallback = arguments[lastArg--];
225 request = arguments[lastArg--];
226 if (lastArg >= 0 && typeof(arguments[lastArg]) == "string")
227 targetId = arguments[lastArg--];
228 if (lastArg != -1)
229 throw new Error("Invalid arguments to sendRequest.");
230
231 var port = chrome.extension.connect(targetId,
232 {name: chromeHidden.kRequestChannel});
233 port.postMessage(request);
234 port.onDisconnect.addListener(function() {
235 // For onDisconnects, we only notify the callback if there was an error
236 try {
237 if (chrome.extension.lastError && responseCallback)
238 responseCallback();
239 } finally {
240 port = null;
241 }
242 });
243 port.onMessage.addListener(function(response) {
244 try {
245 if (responseCallback)
246 responseCallback(response);
247 } finally {
248 port.disconnect();
249 port = null;
250 }
251 });
252 };
253
254 // Returns a resource URL that can be used to fetch a resource from this
255 // extension.
256 chrome.extension.getURL = function(path) {
257 path = String(path);
258 if (!path.length || path[0] != "/")
259 path = "/" + path;
260 return "chrome-extension://" + extensionId + path;
261 };
262
263 chrome.i18n = chrome.i18n || {};
264 chrome.i18n.getMessage = function(message_name, placeholders) {
265 return GetL10nMessage(message_name, placeholders, extensionId);
266 };
267
268 if (!isExtensionProcess)
269 setupApiStubs();
270 });
271
272 var notSupportedSuffix = " can only be used in extension processes. " +
273 "See the content scripts documentation for more details.";
274
275 // Setup to throw an error message when trying to access |name| on the chrome
276 // object. The |name| can be a dot-separated path.
277 function createStub(name) {
278 var module = chrome;
279 var parts = name.split(".");
280 for (var i = 0; i < parts.length - 1; i++) {
281 var nextPart = parts[i];
282 // Make sure an object at the path so far is defined.
283 module[nextPart] = module[nextPart] || {};
284 module = module[nextPart];
285 }
286 var finalPart = parts[parts.length-1];
287 module.__defineGetter__(finalPart, function() {
288 throw new Error("chrome." + name + notSupportedSuffix);
289 });
290 }
291
292 // Sets up stubs to throw a better error message for the common case of
293 // developers trying to call extension API's that aren't allowed to be
294 // called from content scripts.
295 function setupApiStubs() {
296 // TODO(asargent) It would be nice to eventually generate this
297 // programmatically from extension_api.json (there is already a browser test
298 // that should prevent it from getting stale).
299 var privileged = [
300 // Entire namespaces.
301 "bookmarks",
302 "browserAction",
303 "chromeAuthPrivate",
304 "chromePrivate",
305 "chromeosInfoPrivate",
306 "contextMenus",
307 "cookies",
308 "devtools",
309 "experimental.accessibility",
310 "experimental.app",
311 "experimental.bookmarkManager",
312 "experimental.clear",
313 "experimental.contentSettings",
314 "experimental.debugger",
315 "experimental.downloads",
316 "experimental.extension",
317 "experimental.infobars",
318 "experimental.input",
319 "experimental.inputUI",
320 "experimental.metrics",
321 "experimental.permissions",
322 "experimental.settings",
323 "experimental.popup",
324 "experimental.processes",
325 "experimental.privacy",
326 "experimental.rlz",
327 "experimental.sidebar",
328 "experimental.webNavigation",
329 "experimental.webRequest",
330 "fileBrowserHandler",
331 "fileBrowserPrivate",
332 "fileSystem",
333 "history",
334 "idle",
335 "inputMethodPrivate",
336 "management",
337 "mediaPlayerPrivate",
338 "omnibox",
339 "pageAction",
340 "pageActions",
341 "proxy",
342 "tabs",
343 "test",
344 "toolstrip",
345 "tts",
346 "ttsEngine",
347 "types",
348 "webSocketProxyPrivate",
349 "webstorePrivate",
350 "windows",
351
352 // Functions/events/properties within the extension namespace.
353 "extension.getBackgroundPage",
354 "extension.getExtensionTabs",
355 "extension.getToolstrips",
356 "extension.getViews",
357 "extension.isAllowedIncognitoAccess",
358 "extension.isAllowedFileSchemeAccess",
359 "extension.onConnectExternal",
360 "extension.onRequestExternal",
361 "extension.setUpdateUrlData",
362 "i18n.getAcceptLanguages"
363 ];
364 for (var i = 0; i < privileged.length; i++) {
365 createStub(privileged[i]);
366 }
367 }
368
369 })();
OLDNEW
« no previous file with comments | « chrome/renderer/resources/json_schema.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698