blob: be760806f57d673e78ec48535e291fc3bcbf3f55 [file] [log] [blame]
[email protected]b1cf3372011-04-20 21:28:101// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]0aa477bd2009-03-23 22:21:432// 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
[email protected]a672cb92009-12-29 00:28:437#include <map>
8#include <string>
9
[email protected]75e5a872009-04-02 23:56:1110#include "base/basictypes.h"
[email protected]625332e02010-12-14 07:48:4911#include "base/lazy_instance.h"
[email protected]a672cb92009-12-29 00:28:4312#include "chrome/common/extensions/extension_message_bundle.h"
[email protected]44c49c92011-03-28 16:17:2313#include "chrome/common/extensions/extension_messages.h"
[email protected]a672cb92009-12-29 00:28:4314#include "chrome/common/url_constants.h"
[email protected]a40caa972009-04-08 18:35:3415#include "chrome/renderer/extensions/event_bindings.h"
[email protected]e354e882011-08-30 01:00:0416#include "chrome/renderer/extensions/extension_base.h"
[email protected]ae5281b2011-09-06 08:11:5017#include "chrome/renderer/extensions/extension_renderer_context.h"
[email protected]10e6ab572011-04-14 23:42:0018#include "content/renderer/render_thread.h"
[email protected]60916042011-03-19 00:43:3619#include "content/renderer/render_view.h"
[email protected]75e5a872009-04-02 23:56:1120#include "grit/renderer_resources.h"
[email protected]82fc0f52011-09-06 23:39:2221#include "v8/include/v8.h"
[email protected]0aa477bd2009-03-23 22:21:4322
[email protected]0aa477bd2009-03-23 22:21:4323// Message passing API example (in a content script):
24// var extension =
[email protected]28c47ee92009-05-05 18:25:0125// new chrome.Extension('00123456789abcdef0123456789abcdef0123456');
[email protected]a40caa972009-04-08 18:35:3426// var port = extension.connect();
27// port.postMessage('Can you hear me now?');
28// port.onmessage.addListener(function(msg, port) {
[email protected]75e5a872009-04-02 23:56:1129// alert('response=' + msg);
30// port.postMessage('I got your reponse');
[email protected]a40caa972009-04-08 18:35:3431// });
[email protected]75e5a872009-04-02 23:56:1132
33namespace {
34
[email protected]cc661e52009-07-27 19:18:4135struct ExtensionData {
36 struct PortData {
37 int ref_count; // how many contexts have a handle to this port
38 bool disconnected; // true if this port was forcefully disconnected
39 PortData() : ref_count(0), disconnected(false) {}
40 };
41 std::map<int, PortData> ports; // port ID -> data
42};
[email protected]a672cb92009-12-29 00:28:4343
[email protected]625332e02010-12-14 07:48:4944static base::LazyInstance<ExtensionData> g_extension_data(
45 base::LINKER_INITIALIZED);
46
[email protected]a672cb92009-12-29 00:28:4347static bool HasPortData(int port_id) {
[email protected]625332e02010-12-14 07:48:4948 return g_extension_data.Get().ports.find(port_id) !=
49 g_extension_data.Get().ports.end();
[email protected]4ae8a452009-11-03 23:21:2750}
[email protected]a672cb92009-12-29 00:28:4351
52static ExtensionData::PortData& GetPortData(int port_id) {
[email protected]625332e02010-12-14 07:48:4953 return g_extension_data.Get().ports[port_id];
[email protected]cc661e52009-07-27 19:18:4154}
[email protected]a672cb92009-12-29 00:28:4355
56static void ClearPortData(int port_id) {
[email protected]625332e02010-12-14 07:48:4957 g_extension_data.Get().ports.erase(port_id);
[email protected]4ae8a452009-11-03 23:21:2758}
[email protected]cc661e52009-07-27 19:18:4159
[email protected]4ae8a452009-11-03 23:21:2760const char kPortClosedError[] = "Attempting to use a disconnected port object";
[email protected]a40caa972009-04-08 18:35:3461const char* kExtensionDeps[] = { EventBindings::kName };
[email protected]0aa477bd2009-03-23 22:21:4362
[email protected]4083f052009-06-30 19:52:0963class ExtensionImpl : public ExtensionBase {
[email protected]0aa477bd2009-03-23 22:21:4364 public:
[email protected]ae5281b2011-09-06 08:11:5065 explicit ExtensionImpl(ExtensionRendererContext* context)
[email protected]4083f052009-06-30 19:52:0966 : ExtensionBase(RendererExtensionBindings::kName,
[email protected]687b9602010-12-08 10:43:0867 GetStringResource(IDR_RENDERER_EXTENSION_BINDINGS_JS),
[email protected]ae5281b2011-09-06 08:11:5068 arraysize(kExtensionDeps), kExtensionDeps, context) {
[email protected]a40caa972009-04-08 18:35:3469 }
[email protected]0aa477bd2009-03-23 22:21:4370 ~ExtensionImpl() {}
71
72 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
73 v8::Handle<v8::String> name) {
74 if (name->Equals(v8::String::New("OpenChannelToExtension"))) {
75 return v8::FunctionTemplate::New(OpenChannelToExtension);
76 } else if (name->Equals(v8::String::New("PostMessage"))) {
77 return v8::FunctionTemplate::New(PostMessage);
[email protected]3ca29e22009-07-01 00:01:0378 } else if (name->Equals(v8::String::New("CloseChannel"))) {
79 return v8::FunctionTemplate::New(CloseChannel);
[email protected]cc661e52009-07-27 19:18:4180 } else if (name->Equals(v8::String::New("PortAddRef"))) {
81 return v8::FunctionTemplate::New(PortAddRef);
82 } else if (name->Equals(v8::String::New("PortRelease"))) {
83 return v8::FunctionTemplate::New(PortRelease);
[email protected]a672cb92009-12-29 00:28:4384 } else if (name->Equals(v8::String::New("GetL10nMessage"))) {
85 return v8::FunctionTemplate::New(GetL10nMessage);
[email protected]0aa477bd2009-03-23 22:21:4386 }
[email protected]4083f052009-06-30 19:52:0987 return ExtensionBase::GetNativeFunction(name);
[email protected]0aa477bd2009-03-23 22:21:4388 }
[email protected]75e5a872009-04-02 23:56:1189
90 // Creates a new messaging channel to the given extension.
[email protected]0aa477bd2009-03-23 22:21:4391 static v8::Handle<v8::Value> OpenChannelToExtension(
92 const v8::Arguments& args) {
[email protected]9a7b22e2009-05-12 00:48:5893 // Get the current RenderView so that we can send a routed IPC message from
94 // the correct source.
[email protected]37acaf2a82011-09-03 03:38:4795 RenderView* renderview = GetCurrentRenderView();
[email protected]4b5d64ff2009-05-01 21:17:4996 if (!renderview)
97 return v8::Undefined();
98
[email protected]ea4d0790d2009-10-09 18:13:2799 if (args.Length() >= 3 && args[0]->IsString() && args[1]->IsString() &&
100 args[2]->IsString()) {
101 std::string source_id = *v8::String::Utf8Value(args[0]->ToString());
102 std::string target_id = *v8::String::Utf8Value(args[1]->ToString());
103 std::string channel_name = *v8::String::Utf8Value(args[2]->ToString());
[email protected]75e5a872009-04-02 23:56:11104 int port_id = -1;
[email protected]44c49c92011-03-28 16:17:23105 renderview->Send(new ExtensionHostMsg_OpenChannelToExtension(
[email protected]ea4d0790d2009-10-09 18:13:27106 renderview->routing_id(), source_id, target_id,
107 channel_name, &port_id));
[email protected]75e5a872009-04-02 23:56:11108 return v8::Integer::New(port_id);
[email protected]0aa477bd2009-03-23 22:21:43109 }
110 return v8::Undefined();
111 }
[email protected]75e5a872009-04-02 23:56:11112
113 // Sends a message along the given channel.
[email protected]0aa477bd2009-03-23 22:21:43114 static v8::Handle<v8::Value> PostMessage(const v8::Arguments& args) {
[email protected]37acaf2a82011-09-03 03:38:47115 RenderView* renderview = GetCurrentRenderView();
[email protected]4b5d64ff2009-05-01 21:17:49116 if (!renderview)
117 return v8::Undefined();
118
[email protected]0aa477bd2009-03-23 22:21:43119 if (args.Length() >= 2 && args[0]->IsInt32() && args[1]->IsString()) {
[email protected]75e5a872009-04-02 23:56:11120 int port_id = args[0]->Int32Value();
[email protected]4ae8a452009-11-03 23:21:27121 if (!HasPortData(port_id)) {
122 return v8::ThrowException(v8::Exception::Error(
123 v8::String::New(kPortClosedError)));
124 }
[email protected]0aa477bd2009-03-23 22:21:43125 std::string message = *v8::String::Utf8Value(args[1]->ToString());
[email protected]44c49c92011-03-28 16:17:23126 renderview->Send(new ExtensionHostMsg_PostMessage(
[email protected]4b5d64ff2009-05-01 21:17:49127 renderview->routing_id(), port_id, message));
[email protected]0aa477bd2009-03-23 22:21:43128 }
129 return v8::Undefined();
130 }
[email protected]3ca29e22009-07-01 00:01:03131
[email protected]cc661e52009-07-27 19:18:41132 // Forcefully disconnects a port.
[email protected]3ca29e22009-07-01 00:01:03133 static v8::Handle<v8::Value> CloseChannel(const v8::Arguments& args) {
[email protected]85e55fa2010-10-28 18:13:58134 if (args.Length() >= 2 && args[0]->IsInt32() && args[1]->IsBoolean()) {
[email protected]3ca29e22009-07-01 00:01:03135 int port_id = args[0]->Int32Value();
[email protected]4ae8a452009-11-03 23:21:27136 if (!HasPortData(port_id)) {
137 return v8::Undefined();
138 }
[email protected]3ca29e22009-07-01 00:01:03139 // Send via the RenderThread because the RenderView might be closing.
[email protected]85e55fa2010-10-28 18:13:58140 bool notify_browser = args[1]->BooleanValue();
141 if (notify_browser)
142 EventBindings::GetRenderThread()->Send(
[email protected]44c49c92011-03-28 16:17:23143 new ExtensionHostMsg_CloseChannel(port_id));
[email protected]4ae8a452009-11-03 23:21:27144 ClearPortData(port_id);
[email protected]cc661e52009-07-27 19:18:41145 }
146 return v8::Undefined();
147 }
148
149 // A new port has been created for a context. This occurs both when script
150 // opens a connection, and when a connection is opened to this script.
151 static v8::Handle<v8::Value> PortAddRef(const v8::Arguments& args) {
152 if (args.Length() >= 1 && args[0]->IsInt32()) {
153 int port_id = args[0]->Int32Value();
154 ++GetPortData(port_id).ref_count;
155 }
156 return v8::Undefined();
157 }
158
159 // The frame a port lived in has been destroyed. When there are no more
160 // frames with a reference to a given port, we will disconnect it and notify
161 // the other end of the channel.
162 static v8::Handle<v8::Value> PortRelease(const v8::Arguments& args) {
163 if (args.Length() >= 1 && args[0]->IsInt32()) {
164 int port_id = args[0]->Int32Value();
[email protected]4ae8a452009-11-03 23:21:27165 if (HasPortData(port_id) && --GetPortData(port_id).ref_count == 0) {
[email protected]cc661e52009-07-27 19:18:41166 // Send via the RenderThread because the RenderView might be closing.
167 EventBindings::GetRenderThread()->Send(
[email protected]44c49c92011-03-28 16:17:23168 new ExtensionHostMsg_CloseChannel(port_id));
[email protected]4ae8a452009-11-03 23:21:27169 ClearPortData(port_id);
[email protected]cc661e52009-07-27 19:18:41170 }
[email protected]3ca29e22009-07-01 00:01:03171 }
172 return v8::Undefined();
173 }
[email protected]a672cb92009-12-29 00:28:43174
175 static v8::Handle<v8::Value> GetL10nMessage(const v8::Arguments& args) {
176 if (args.Length() != 3 || !args[0]->IsString()) {
177 NOTREACHED() << "Bad arguments";
178 return v8::Undefined();
179 }
180
181 std::string extension_id;
182 if (args[2]->IsNull() || !args[2]->IsString()) {
183 return v8::Undefined();
184 } else {
185 extension_id = *v8::String::Utf8Value(args[2]->ToString());
186 if (extension_id.empty())
187 return v8::Undefined();
188 }
189
190 L10nMessagesMap* l10n_messages = GetL10nMessagesMap(extension_id);
191 if (!l10n_messages) {
192 // Get the current RenderView so that we can send a routed IPC message
193 // from the correct source.
[email protected]37acaf2a82011-09-03 03:38:47194 RenderView* renderview = GetCurrentRenderView();
[email protected]a672cb92009-12-29 00:28:43195 if (!renderview)
196 return v8::Undefined();
197
198 L10nMessagesMap messages;
199 // A sync call to load message catalogs for current extension.
[email protected]44c49c92011-03-28 16:17:23200 renderview->Send(new ExtensionHostMsg_GetMessageBundle(
[email protected]a672cb92009-12-29 00:28:43201 extension_id, &messages));
202
[email protected]a672cb92009-12-29 00:28:43203 // Save messages we got.
204 ExtensionToL10nMessagesMap& l10n_messages_map =
205 *GetExtensionToL10nMessagesMap();
206 l10n_messages_map[extension_id] = messages;
207
208 l10n_messages = GetL10nMessagesMap(extension_id);
[email protected]a672cb92009-12-29 00:28:43209 }
210
211 std::string message_name = *v8::String::AsciiValue(args[0]);
212 std::string message =
213 ExtensionMessageBundle::GetL10nMessage(message_name, *l10n_messages);
214
215 std::vector<std::string> substitutions;
216 if (args[1]->IsNull() || args[1]->IsUndefined()) {
217 // chrome.i18n.getMessage("message_name");
218 // chrome.i18n.getMessage("message_name", null);
219 return v8::String::New(message.c_str());
220 } else if (args[1]->IsString()) {
221 // chrome.i18n.getMessage("message_name", "one param");
222 std::string substitute = *v8::String::Utf8Value(args[1]->ToString());
223 substitutions.push_back(substitute);
224 } else if (args[1]->IsArray()) {
225 // chrome.i18n.getMessage("message_name", ["more", "params"]);
[email protected]02291212011-09-02 13:27:41226 v8::Local<v8::Array> placeholders = v8::Local<v8::Array>::Cast(args[1]);
[email protected]a672cb92009-12-29 00:28:43227 uint32_t count = placeholders->Length();
228 if (count <= 0 || count > 9)
229 return v8::Undefined();
230 for (uint32_t i = 0; i < count; ++i) {
231 std::string substitute =
232 *v8::String::Utf8Value(
233 placeholders->Get(v8::Integer::New(i))->ToString());
234 substitutions.push_back(substitute);
235 }
236 } else {
237 NOTREACHED() << "Couldn't parse second parameter.";
238 return v8::Undefined();
239 }
240
241 return v8::String::New(ReplaceStringPlaceholders(
242 message, substitutions, NULL).c_str());
243 }
[email protected]0aa477bd2009-03-23 22:21:43244};
245
246} // namespace
247
[email protected]17bc2fd2009-04-16 00:05:52248const char* RendererExtensionBindings::kName =
249 "chrome/RendererExtensionBindings";
250
[email protected]ae5281b2011-09-06 08:11:50251v8::Extension* RendererExtensionBindings::Get(
252 ExtensionRendererContext* context) {
253 static v8::Extension* extension = new ExtensionImpl(context);
[email protected]ad1f9bd2009-07-30 20:23:15254 return extension;
[email protected]0aa477bd2009-03-23 22:21:43255}