1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This script contains unprivileged javascript APIs related to chrome
// extensions. It is loaded by any extension-related context, such as content
// scripts or toolstrips.
// See user_script_slave.cc for script that is loaded by content scripts only.
// TODO(mpcomplete): we also load this in regular web pages, but don't need
// to.
var chrome = chrome || {};
(function () {
native function OpenChannelToExtension(sourceId, targetId, name);
native function CloseChannel(portId);
native function PortAddRef(portId);
native function PortRelease(portId);
native function PostMessage(portId, msg);
native function GetChromeHidden();
var chromeHidden = GetChromeHidden();
// The reserved channel name for the sendRequest API.
chromeHidden.kRequestChannel = "chrome.extension.sendRequest";
// Map of port IDs to port object.
var ports = {};
// Change even to odd and vice versa, to get the other side of a given
// channel.
function getOppositePortId(portId) { return portId ^ 1; }
// Port object. Represents a connection to another script context through
// which messages can be passed.
chrome.Port = function(portId, opt_name) {
this.portId_ = portId;
this.name = opt_name;
this.onDisconnect = new chrome.Event();
this.onMessage = new chrome.Event();
};
chromeHidden.Port = {};
// Hidden port creation function. We don't want to expose an API that lets
// people add arbitrary port IDs to the port list.
chromeHidden.Port.createPort = function(portId, opt_name) {
if (ports[portId]) {
throw new Error("Port '" + portId + "' already exists.");
}
var port = new chrome.Port(portId, opt_name);
ports[portId] = port;
chromeHidden.onUnload.addListener(function() {
PortRelease(portId);
});
PortAddRef(portId);
return port;
}
// Called by native code when a channel has been opened to this context.
chromeHidden.Port.dispatchOnConnect = function(portId, channelName, tab,
sourceExtensionId,
targetExtensionId) {
// Only create a new Port if someone is actually listening for a connection.
// In addition to being an optimization, this also fixes a bug where if 2
// channels were opened to and from the same process, closing one would
// close both.
if (targetExtensionId != chromeHidden.extensionId)
return; // not for us
if (ports[getOppositePortId(portId)])
return; // this channel was opened by us, so ignore it
// Determine whether this is coming from another extension, so we can use
// the right event.
var isExternal = sourceExtensionId != chromeHidden.extensionId;
// Special case for sendRequest/onRequest.
if (channelName == chromeHidden.kRequestChannel) {
var requestEvent = (isExternal ?
chrome.extension.onRequestExternal : chrome.extension.onRequest);
if (requestEvent.hasListeners()) {
var port = chromeHidden.Port.createPort(portId, channelName);
port.onMessage.addListener(function(request) {
requestEvent.dispatch(request, function(response) {
port.postMessage(response);
});
});
}
return;
}
var connectEvent = (isExternal ?
chrome.extension.onConnectExternal : chrome.extension.onConnect);
if (connectEvent.hasListeners()) {
var port = chromeHidden.Port.createPort(portId, channelName);
if (tab) {
tab = JSON.parse(tab);
}
port.sender = {tab: tab, id: sourceExtensionId};
// TODO(EXTENSIONS_DEPRECATED): port.tab is obsolete.
port.tab = port.sender.tab;
connectEvent.dispatch(port);
}
};
// Called by native code when a channel has been closed.
chromeHidden.Port.dispatchOnDisconnect = function(portId) {
var port = ports[portId];
if (port) {
port.onDisconnect.dispatch(port);
delete ports[portId];
}
};
// Called by native code when a message has been sent to the given port.
chromeHidden.Port.dispatchOnMessage = function(msg, portId) {
var port = ports[portId];
if (port) {
if (msg) {
msg = JSON.parse(msg);
}
port.onMessage.dispatch(msg, port);
}
};
// Sends a message asynchronously to the context on the other end of this
// port.
chrome.Port.prototype.postMessage = function(msg) {
// JSON.stringify doesn't support a root object which is undefined.
if (msg === undefined)
msg = null;
PostMessage(this.portId_, JSON.stringify(msg));
};
// Disconnects the port from the other end.
chrome.Port.prototype.disconnect = function() {
delete ports[this.portId_];
CloseChannel(this.portId_);
}
// This function is called on context initialization for both content scripts
// and extension contexts.
chrome.initExtension = function(extensionId) {
delete chrome.initExtension;
chromeHidden.extensionId = extensionId;
chrome.extension = chrome.extension || {};
// TODO(EXTENSIONS_DEPRECATED): chrome.self is obsolete.
// http://code.google.com/p/chromium/issues/detail?id=16356
chrome.self = chrome.extension;
// Events for when a message channel is opened to our extension.
chrome.extension.onConnect = new chrome.Event();
chrome.extension.onConnectExternal = new chrome.Event();
chrome.extension.onRequest = new chrome.Event();
chrome.extension.onRequestExternal = new chrome.Event();
// Opens a message channel to the given target extension, or the current one
// if unspecified. Returns a Port for message passing.
chrome.extension.connect = function(targetId_opt, connectInfo_opt) {
var name = "";
var targetId = extensionId;
var nextArg = 0;
if (typeof(arguments[nextArg]) == "string")
targetId = arguments[nextArg++];
if (typeof(arguments[nextArg]) == "object")
name = arguments[nextArg++].name || name;
var portId = OpenChannelToExtension(extensionId, targetId, name);
if (portId >= 0)
return chromeHidden.Port.createPort(portId, name);
throw new Error("Error connecting to extension '" + targetId + "'");
};
chrome.extension.sendRequest =
function(targetId_opt, request, responseCallback_opt) {
var targetId = extensionId;
var responseCallback = null;
var lastArg = arguments.length - 1;
if (typeof(arguments[lastArg]) == "function")
responseCallback = arguments[lastArg--];
request = arguments[lastArg--];
if (lastArg >= 0)
targetId = arguments[lastArg--];
var port = chrome.extension.connect(targetId,
{name: chromeHidden.kRequestChannel});
port.postMessage(request);
port.onMessage.addListener(function(response) {
if (responseCallback)
responseCallback(response);
port.disconnect();
});
};
// Returns a resource URL that can be used to fetch a resource from this
// extension.
chrome.extension.getURL = function(path) {
return "chrome-extension://" + extensionId + "/" + path;
};
}
})();
|