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

Side by Side Diff: chrome/renderer/extensions/renderer_extension_bindings.cc

Issue 8540012: Enable extension APIs for content scripts. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 9 years, 1 month 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
(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 #include "chrome/renderer/extensions/renderer_extension_bindings.h"
6
7 #include <map>
8 #include <string>
9
10 #include "base/basictypes.h"
11 #include "base/lazy_instance.h"
12 #include "chrome/common/extensions/extension_message_bundle.h"
13 #include "chrome/common/extensions/extension_messages.h"
14 #include "chrome/common/url_constants.h"
15 #include "chrome/renderer/extensions/chrome_v8_context.h"
16 #include "chrome/renderer/extensions/chrome_v8_context_set.h"
17 #include "chrome/renderer/extensions/chrome_v8_extension.h"
18 #include "chrome/renderer/extensions/event_bindings.h"
19 #include "chrome/renderer/extensions/extension_dispatcher.h"
20 #include "content/public/renderer/render_thread.h"
21 #include "content/public/renderer/render_view.h"
22 #include "grit/renderer_resources.h"
23 #include "v8/include/v8.h"
24
25 // Message passing API example (in a content script):
26 // var extension =
27 // new chrome.Extension('00123456789abcdef0123456789abcdef0123456');
28 // var port = extension.connect();
29 // port.postMessage('Can you hear me now?');
30 // port.onmessage.addListener(function(msg, port) {
31 // alert('response=' + msg);
32 // port.postMessage('I got your reponse');
33 // });
34
35 using content::RenderThread;
36
37 namespace {
38
39 struct ExtensionData {
40 struct PortData {
41 int ref_count; // how many contexts have a handle to this port
42 bool disconnected; // true if this port was forcefully disconnected
43 PortData() : ref_count(0), disconnected(false) {}
44 };
45 std::map<int, PortData> ports; // port ID -> data
46 };
47
48 static base::LazyInstance<ExtensionData> g_extension_data(
49 base::LINKER_INITIALIZED);
50
51 static bool HasPortData(int port_id) {
52 return g_extension_data.Get().ports.find(port_id) !=
53 g_extension_data.Get().ports.end();
54 }
55
56 static ExtensionData::PortData& GetPortData(int port_id) {
57 return g_extension_data.Get().ports[port_id];
58 }
59
60 static void ClearPortData(int port_id) {
61 g_extension_data.Get().ports.erase(port_id);
62 }
63
64 const char kPortClosedError[] = "Attempting to use a disconnected port object";
65 const char* kExtensionDeps[] = { "extensions/event.js" };
66
67 class ExtensionImpl : public ChromeV8Extension {
68 public:
69 explicit ExtensionImpl(ExtensionDispatcher* dispatcher)
70 : ChromeV8Extension("extensions/renderer_extension_bindings.js",
71 IDR_RENDERER_EXTENSION_BINDINGS_JS,
72 arraysize(kExtensionDeps), kExtensionDeps,
73 dispatcher) {
74 }
75 ~ExtensionImpl() {}
76
77 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
78 v8::Handle<v8::String> name) {
79 if (name->Equals(v8::String::New("OpenChannelToExtension"))) {
80 return v8::FunctionTemplate::New(OpenChannelToExtension);
81 } else if (name->Equals(v8::String::New("PostMessage"))) {
82 return v8::FunctionTemplate::New(PostMessage);
83 } else if (name->Equals(v8::String::New("CloseChannel"))) {
84 return v8::FunctionTemplate::New(CloseChannel);
85 } else if (name->Equals(v8::String::New("PortAddRef"))) {
86 return v8::FunctionTemplate::New(PortAddRef);
87 } else if (name->Equals(v8::String::New("PortRelease"))) {
88 return v8::FunctionTemplate::New(PortRelease);
89 } else if (name->Equals(v8::String::New("GetL10nMessage"))) {
90 return v8::FunctionTemplate::New(GetL10nMessage);
91 }
92 return ChromeV8Extension::GetNativeFunction(name);
93 }
94
95 // Creates a new messaging channel to the given extension.
96 static v8::Handle<v8::Value> OpenChannelToExtension(
97 const v8::Arguments& args) {
98 // Get the current RenderView so that we can send a routed IPC message from
99 // the correct source.
100 content::RenderView* renderview = GetCurrentRenderView();
101 if (!renderview)
102 return v8::Undefined();
103
104 if (args.Length() >= 3 && args[0]->IsString() && args[1]->IsString() &&
105 args[2]->IsString()) {
106 std::string source_id = *v8::String::Utf8Value(args[0]->ToString());
107 std::string target_id = *v8::String::Utf8Value(args[1]->ToString());
108 std::string channel_name = *v8::String::Utf8Value(args[2]->ToString());
109 int port_id = -1;
110 renderview->Send(new ExtensionHostMsg_OpenChannelToExtension(
111 renderview->GetRoutingId(), source_id, target_id,
112 channel_name, &port_id));
113 return v8::Integer::New(port_id);
114 }
115 return v8::Undefined();
116 }
117
118 // Sends a message along the given channel.
119 static v8::Handle<v8::Value> PostMessage(const v8::Arguments& args) {
120 content::RenderView* renderview = GetCurrentRenderView();
121 if (!renderview)
122 return v8::Undefined();
123
124 if (args.Length() >= 2 && args[0]->IsInt32() && args[1]->IsString()) {
125 int port_id = args[0]->Int32Value();
126 if (!HasPortData(port_id)) {
127 return v8::ThrowException(v8::Exception::Error(
128 v8::String::New(kPortClosedError)));
129 }
130 std::string message = *v8::String::Utf8Value(args[1]->ToString());
131 renderview->Send(new ExtensionHostMsg_PostMessage(
132 renderview->GetRoutingId(), port_id, message));
133 }
134 return v8::Undefined();
135 }
136
137 // Forcefully disconnects a port.
138 static v8::Handle<v8::Value> CloseChannel(const v8::Arguments& args) {
139 if (args.Length() >= 2 && args[0]->IsInt32() && args[1]->IsBoolean()) {
140 int port_id = args[0]->Int32Value();
141 if (!HasPortData(port_id)) {
142 return v8::Undefined();
143 }
144 // Send via the RenderThread because the RenderView might be closing.
145 bool notify_browser = args[1]->BooleanValue();
146 if (notify_browser)
147 content::RenderThread::Get()->Send(
148 new ExtensionHostMsg_CloseChannel(port_id));
149 ClearPortData(port_id);
150 }
151 return v8::Undefined();
152 }
153
154 // A new port has been created for a context. This occurs both when script
155 // opens a connection, and when a connection is opened to this script.
156 static v8::Handle<v8::Value> PortAddRef(const v8::Arguments& args) {
157 if (args.Length() >= 1 && args[0]->IsInt32()) {
158 int port_id = args[0]->Int32Value();
159 ++GetPortData(port_id).ref_count;
160 }
161 return v8::Undefined();
162 }
163
164 // The frame a port lived in has been destroyed. When there are no more
165 // frames with a reference to a given port, we will disconnect it and notify
166 // the other end of the channel.
167 static v8::Handle<v8::Value> PortRelease(const v8::Arguments& args) {
168 if (args.Length() >= 1 && args[0]->IsInt32()) {
169 int port_id = args[0]->Int32Value();
170 if (HasPortData(port_id) && --GetPortData(port_id).ref_count == 0) {
171 // Send via the RenderThread because the RenderView might be closing.
172 content::RenderThread::Get()->Send(
173 new ExtensionHostMsg_CloseChannel(port_id));
174 ClearPortData(port_id);
175 }
176 }
177 return v8::Undefined();
178 }
179
180 static v8::Handle<v8::Value> GetL10nMessage(const v8::Arguments& args) {
181 if (args.Length() != 3 || !args[0]->IsString()) {
182 NOTREACHED() << "Bad arguments";
183 return v8::Undefined();
184 }
185
186 std::string extension_id;
187 if (args[2]->IsNull() || !args[2]->IsString()) {
188 return v8::Undefined();
189 } else {
190 extension_id = *v8::String::Utf8Value(args[2]->ToString());
191 if (extension_id.empty())
192 return v8::Undefined();
193 }
194
195 L10nMessagesMap* l10n_messages = GetL10nMessagesMap(extension_id);
196 if (!l10n_messages) {
197 // Get the current RenderView so that we can send a routed IPC message
198 // from the correct source.
199 content::RenderView* renderview = GetCurrentRenderView();
200 if (!renderview)
201 return v8::Undefined();
202
203 L10nMessagesMap messages;
204 // A sync call to load message catalogs for current extension.
205 renderview->Send(new ExtensionHostMsg_GetMessageBundle(
206 extension_id, &messages));
207
208 // Save messages we got.
209 ExtensionToL10nMessagesMap& l10n_messages_map =
210 *GetExtensionToL10nMessagesMap();
211 l10n_messages_map[extension_id] = messages;
212
213 l10n_messages = GetL10nMessagesMap(extension_id);
214 }
215
216 std::string message_name = *v8::String::AsciiValue(args[0]);
217 std::string message =
218 ExtensionMessageBundle::GetL10nMessage(message_name, *l10n_messages);
219
220 std::vector<std::string> substitutions;
221 if (args[1]->IsNull() || args[1]->IsUndefined()) {
222 // chrome.i18n.getMessage("message_name");
223 // chrome.i18n.getMessage("message_name", null);
224 return v8::String::New(message.c_str());
225 } else if (args[1]->IsString()) {
226 // chrome.i18n.getMessage("message_name", "one param");
227 std::string substitute = *v8::String::Utf8Value(args[1]->ToString());
228 substitutions.push_back(substitute);
229 } else if (args[1]->IsArray()) {
230 // chrome.i18n.getMessage("message_name", ["more", "params"]);
231 v8::Local<v8::Array> placeholders = v8::Local<v8::Array>::Cast(args[1]);
232 uint32_t count = placeholders->Length();
233 if (count <= 0 || count > 9)
234 return v8::Undefined();
235 for (uint32_t i = 0; i < count; ++i) {
236 std::string substitute =
237 *v8::String::Utf8Value(
238 placeholders->Get(v8::Integer::New(i))->ToString());
239 substitutions.push_back(substitute);
240 }
241 } else {
242 NOTREACHED() << "Couldn't parse second parameter.";
243 return v8::Undefined();
244 }
245
246 return v8::String::New(ReplaceStringPlaceholders(
247 message, substitutions, NULL).c_str());
248 }
249 };
250
251 } // namespace
252
253 v8::Extension* RendererExtensionBindings::Get(ExtensionDispatcher* dispatcher) {
254 static v8::Extension* extension = new ExtensionImpl(dispatcher);
255 return extension;
256 }
257
258 void RendererExtensionBindings::DeliverMessage(
259 const ChromeV8ContextSet::ContextSet& contexts,
260 int target_port_id,
261 const std::string& message,
262 content::RenderView* restrict_to_render_view) {
263 v8::HandleScope handle_scope;
264
265 for (ChromeV8ContextSet::ContextSet::const_iterator it = contexts.begin();
266 it != contexts.end(); ++it) {
267
268 if (restrict_to_render_view &&
269 restrict_to_render_view != (*it)->GetRenderView()) {
270 continue;
271 }
272
273 // Check to see whether the context has this port before bothering to create
274 // the message.
275 v8::Handle<v8::Value> port_id_handle = v8::Integer::New(target_port_id);
276 v8::Handle<v8::Value> has_port;
277 if (!(*it)->CallChromeHiddenMethod("Port.hasPort", 1, &port_id_handle,
278 &has_port)) {
279 continue;
280 }
281
282 CHECK(!has_port.IsEmpty());
283 if (!has_port->BooleanValue())
284 continue;
285
286 std::vector<v8::Handle<v8::Value> > arguments;
287 arguments.push_back(v8::String::New(message.c_str(), message.size()));
288 arguments.push_back(port_id_handle);
289 CHECK((*it)->CallChromeHiddenMethod("Port.dispatchOnMessage",
290 arguments.size(),
291 &arguments[0],
292 NULL));
293 }
294 }
OLDNEW
« no previous file with comments | « chrome/renderer/extensions/renderer_extension_bindings.h ('k') | chrome/renderer/extensions/schema_generated_bindings.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698