[email protected] | b1cf337 | 2011-04-20 21:28:10 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 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 | |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 7 | #include <map> |
| 8 | #include <string> |
| 9 | |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 10 | #include "base/basictypes.h" |
[email protected] | 625332e0 | 2010-12-14 07:48:49 | [diff] [blame] | 11 | #include "base/lazy_instance.h" |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 12 | #include "chrome/common/extensions/extension_message_bundle.h" |
[email protected] | 44c49c9 | 2011-03-28 16:17:23 | [diff] [blame] | 13 | #include "chrome/common/extensions/extension_messages.h" |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 14 | #include "chrome/common/url_constants.h" |
[email protected] | a40caa97 | 2009-04-08 18:35:34 | [diff] [blame] | 15 | #include "chrome/renderer/extensions/event_bindings.h" |
[email protected] | e354e88 | 2011-08-30 01:00:04 | [diff] [blame] | 16 | #include "chrome/renderer/extensions/extension_base.h" |
[email protected] | ae5281b | 2011-09-06 08:11:50 | [diff] [blame] | 17 | #include "chrome/renderer/extensions/extension_renderer_context.h" |
[email protected] | 10e6ab57 | 2011-04-14 23:42:00 | [diff] [blame] | 18 | #include "content/renderer/render_thread.h" |
[email protected] | 6091604 | 2011-03-19 00:43:36 | [diff] [blame] | 19 | #include "content/renderer/render_view.h" |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 20 | #include "grit/renderer_resources.h" |
[email protected] | 82fc0f5 | 2011-09-06 23:39:22 | [diff] [blame^] | 21 | #include "v8/include/v8.h" |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 22 | |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 23 | // Message passing API example (in a content script): |
| 24 | // var extension = |
[email protected] | 28c47ee9 | 2009-05-05 18:25:01 | [diff] [blame] | 25 | // new chrome.Extension('00123456789abcdef0123456789abcdef0123456'); |
[email protected] | a40caa97 | 2009-04-08 18:35:34 | [diff] [blame] | 26 | // var port = extension.connect(); |
| 27 | // port.postMessage('Can you hear me now?'); |
| 28 | // port.onmessage.addListener(function(msg, port) { |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 29 | // alert('response=' + msg); |
| 30 | // port.postMessage('I got your reponse'); |
[email protected] | a40caa97 | 2009-04-08 18:35:34 | [diff] [blame] | 31 | // }); |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 32 | |
| 33 | namespace { |
| 34 | |
[email protected] | cc661e5 | 2009-07-27 19:18:41 | [diff] [blame] | 35 | struct 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] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 43 | |
[email protected] | 625332e0 | 2010-12-14 07:48:49 | [diff] [blame] | 44 | static base::LazyInstance<ExtensionData> g_extension_data( |
| 45 | base::LINKER_INITIALIZED); |
| 46 | |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 47 | static bool HasPortData(int port_id) { |
[email protected] | 625332e0 | 2010-12-14 07:48:49 | [diff] [blame] | 48 | return g_extension_data.Get().ports.find(port_id) != |
| 49 | g_extension_data.Get().ports.end(); |
[email protected] | 4ae8a45 | 2009-11-03 23:21:27 | [diff] [blame] | 50 | } |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 51 | |
| 52 | static ExtensionData::PortData& GetPortData(int port_id) { |
[email protected] | 625332e0 | 2010-12-14 07:48:49 | [diff] [blame] | 53 | return g_extension_data.Get().ports[port_id]; |
[email protected] | cc661e5 | 2009-07-27 19:18:41 | [diff] [blame] | 54 | } |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 55 | |
| 56 | static void ClearPortData(int port_id) { |
[email protected] | 625332e0 | 2010-12-14 07:48:49 | [diff] [blame] | 57 | g_extension_data.Get().ports.erase(port_id); |
[email protected] | 4ae8a45 | 2009-11-03 23:21:27 | [diff] [blame] | 58 | } |
[email protected] | cc661e5 | 2009-07-27 19:18:41 | [diff] [blame] | 59 | |
[email protected] | 4ae8a45 | 2009-11-03 23:21:27 | [diff] [blame] | 60 | const char kPortClosedError[] = "Attempting to use a disconnected port object"; |
[email protected] | a40caa97 | 2009-04-08 18:35:34 | [diff] [blame] | 61 | const char* kExtensionDeps[] = { EventBindings::kName }; |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 62 | |
[email protected] | 4083f05 | 2009-06-30 19:52:09 | [diff] [blame] | 63 | class ExtensionImpl : public ExtensionBase { |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 64 | public: |
[email protected] | ae5281b | 2011-09-06 08:11:50 | [diff] [blame] | 65 | explicit ExtensionImpl(ExtensionRendererContext* context) |
[email protected] | 4083f05 | 2009-06-30 19:52:09 | [diff] [blame] | 66 | : ExtensionBase(RendererExtensionBindings::kName, |
[email protected] | 687b960 | 2010-12-08 10:43:08 | [diff] [blame] | 67 | GetStringResource(IDR_RENDERER_EXTENSION_BINDINGS_JS), |
[email protected] | ae5281b | 2011-09-06 08:11:50 | [diff] [blame] | 68 | arraysize(kExtensionDeps), kExtensionDeps, context) { |
[email protected] | a40caa97 | 2009-04-08 18:35:34 | [diff] [blame] | 69 | } |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 70 | ~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] | 3ca29e2 | 2009-07-01 00:01:03 | [diff] [blame] | 78 | } else if (name->Equals(v8::String::New("CloseChannel"))) { |
| 79 | return v8::FunctionTemplate::New(CloseChannel); |
[email protected] | cc661e5 | 2009-07-27 19:18:41 | [diff] [blame] | 80 | } 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] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 84 | } else if (name->Equals(v8::String::New("GetL10nMessage"))) { |
| 85 | return v8::FunctionTemplate::New(GetL10nMessage); |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 86 | } |
[email protected] | 4083f05 | 2009-06-30 19:52:09 | [diff] [blame] | 87 | return ExtensionBase::GetNativeFunction(name); |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 88 | } |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 89 | |
| 90 | // Creates a new messaging channel to the given extension. |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 91 | static v8::Handle<v8::Value> OpenChannelToExtension( |
| 92 | const v8::Arguments& args) { |
[email protected] | 9a7b22e | 2009-05-12 00:48:58 | [diff] [blame] | 93 | // Get the current RenderView so that we can send a routed IPC message from |
| 94 | // the correct source. |
[email protected] | 37acaf2a8 | 2011-09-03 03:38:47 | [diff] [blame] | 95 | RenderView* renderview = GetCurrentRenderView(); |
[email protected] | 4b5d64ff | 2009-05-01 21:17:49 | [diff] [blame] | 96 | if (!renderview) |
| 97 | return v8::Undefined(); |
| 98 | |
[email protected] | ea4d0790d | 2009-10-09 18:13:27 | [diff] [blame] | 99 | 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] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 104 | int port_id = -1; |
[email protected] | 44c49c9 | 2011-03-28 16:17:23 | [diff] [blame] | 105 | renderview->Send(new ExtensionHostMsg_OpenChannelToExtension( |
[email protected] | ea4d0790d | 2009-10-09 18:13:27 | [diff] [blame] | 106 | renderview->routing_id(), source_id, target_id, |
| 107 | channel_name, &port_id)); |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 108 | return v8::Integer::New(port_id); |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 109 | } |
| 110 | return v8::Undefined(); |
| 111 | } |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 112 | |
| 113 | // Sends a message along the given channel. |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 114 | static v8::Handle<v8::Value> PostMessage(const v8::Arguments& args) { |
[email protected] | 37acaf2a8 | 2011-09-03 03:38:47 | [diff] [blame] | 115 | RenderView* renderview = GetCurrentRenderView(); |
[email protected] | 4b5d64ff | 2009-05-01 21:17:49 | [diff] [blame] | 116 | if (!renderview) |
| 117 | return v8::Undefined(); |
| 118 | |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 119 | if (args.Length() >= 2 && args[0]->IsInt32() && args[1]->IsString()) { |
[email protected] | 75e5a87 | 2009-04-02 23:56:11 | [diff] [blame] | 120 | int port_id = args[0]->Int32Value(); |
[email protected] | 4ae8a45 | 2009-11-03 23:21:27 | [diff] [blame] | 121 | if (!HasPortData(port_id)) { |
| 122 | return v8::ThrowException(v8::Exception::Error( |
| 123 | v8::String::New(kPortClosedError))); |
| 124 | } |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 125 | std::string message = *v8::String::Utf8Value(args[1]->ToString()); |
[email protected] | 44c49c9 | 2011-03-28 16:17:23 | [diff] [blame] | 126 | renderview->Send(new ExtensionHostMsg_PostMessage( |
[email protected] | 4b5d64ff | 2009-05-01 21:17:49 | [diff] [blame] | 127 | renderview->routing_id(), port_id, message)); |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 128 | } |
| 129 | return v8::Undefined(); |
| 130 | } |
[email protected] | 3ca29e2 | 2009-07-01 00:01:03 | [diff] [blame] | 131 | |
[email protected] | cc661e5 | 2009-07-27 19:18:41 | [diff] [blame] | 132 | // Forcefully disconnects a port. |
[email protected] | 3ca29e2 | 2009-07-01 00:01:03 | [diff] [blame] | 133 | static v8::Handle<v8::Value> CloseChannel(const v8::Arguments& args) { |
[email protected] | 85e55fa | 2010-10-28 18:13:58 | [diff] [blame] | 134 | if (args.Length() >= 2 && args[0]->IsInt32() && args[1]->IsBoolean()) { |
[email protected] | 3ca29e2 | 2009-07-01 00:01:03 | [diff] [blame] | 135 | int port_id = args[0]->Int32Value(); |
[email protected] | 4ae8a45 | 2009-11-03 23:21:27 | [diff] [blame] | 136 | if (!HasPortData(port_id)) { |
| 137 | return v8::Undefined(); |
| 138 | } |
[email protected] | 3ca29e2 | 2009-07-01 00:01:03 | [diff] [blame] | 139 | // Send via the RenderThread because the RenderView might be closing. |
[email protected] | 85e55fa | 2010-10-28 18:13:58 | [diff] [blame] | 140 | bool notify_browser = args[1]->BooleanValue(); |
| 141 | if (notify_browser) |
| 142 | EventBindings::GetRenderThread()->Send( |
[email protected] | 44c49c9 | 2011-03-28 16:17:23 | [diff] [blame] | 143 | new ExtensionHostMsg_CloseChannel(port_id)); |
[email protected] | 4ae8a45 | 2009-11-03 23:21:27 | [diff] [blame] | 144 | ClearPortData(port_id); |
[email protected] | cc661e5 | 2009-07-27 19:18:41 | [diff] [blame] | 145 | } |
| 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] | 4ae8a45 | 2009-11-03 23:21:27 | [diff] [blame] | 165 | if (HasPortData(port_id) && --GetPortData(port_id).ref_count == 0) { |
[email protected] | cc661e5 | 2009-07-27 19:18:41 | [diff] [blame] | 166 | // Send via the RenderThread because the RenderView might be closing. |
| 167 | EventBindings::GetRenderThread()->Send( |
[email protected] | 44c49c9 | 2011-03-28 16:17:23 | [diff] [blame] | 168 | new ExtensionHostMsg_CloseChannel(port_id)); |
[email protected] | 4ae8a45 | 2009-11-03 23:21:27 | [diff] [blame] | 169 | ClearPortData(port_id); |
[email protected] | cc661e5 | 2009-07-27 19:18:41 | [diff] [blame] | 170 | } |
[email protected] | 3ca29e2 | 2009-07-01 00:01:03 | [diff] [blame] | 171 | } |
| 172 | return v8::Undefined(); |
| 173 | } |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 174 | |
| 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] | 37acaf2a8 | 2011-09-03 03:38:47 | [diff] [blame] | 194 | RenderView* renderview = GetCurrentRenderView(); |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 195 | if (!renderview) |
| 196 | return v8::Undefined(); |
| 197 | |
| 198 | L10nMessagesMap messages; |
| 199 | // A sync call to load message catalogs for current extension. |
[email protected] | 44c49c9 | 2011-03-28 16:17:23 | [diff] [blame] | 200 | renderview->Send(new ExtensionHostMsg_GetMessageBundle( |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 201 | extension_id, &messages)); |
| 202 | |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 203 | // 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] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 209 | } |
| 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] | 0229121 | 2011-09-02 13:27:41 | [diff] [blame] | 226 | v8::Local<v8::Array> placeholders = v8::Local<v8::Array>::Cast(args[1]); |
[email protected] | a672cb9 | 2009-12-29 00:28:43 | [diff] [blame] | 227 | 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] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 244 | }; |
| 245 | |
| 246 | } // namespace |
| 247 | |
[email protected] | 17bc2fd | 2009-04-16 00:05:52 | [diff] [blame] | 248 | const char* RendererExtensionBindings::kName = |
| 249 | "chrome/RendererExtensionBindings"; |
| 250 | |
[email protected] | ae5281b | 2011-09-06 08:11:50 | [diff] [blame] | 251 | v8::Extension* RendererExtensionBindings::Get( |
| 252 | ExtensionRendererContext* context) { |
| 253 | static v8::Extension* extension = new ExtensionImpl(context); |
[email protected] | ad1f9bd | 2009-07-30 20:23:15 | [diff] [blame] | 254 | return extension; |
[email protected] | 0aa477bd | 2009-03-23 22:21:43 | [diff] [blame] | 255 | } |