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

Side by Side Diff: extensions/renderer/messaging_bindings.cc

Issue 2300453002: [Extensions] Begin making Extension port initialization asynchronous (Closed)
Patch Set: Nasko's Created 4 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
« no previous file with comments | « extensions/renderer/messaging_bindings.h ('k') | extensions/renderer/resources/messaging.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/renderer/messaging_bindings.h" 5 #include "extensions/renderer/messaging_bindings.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 8
9 #include <map> 9 #include <map>
10 #include <string> 10 #include <string>
11 11
12 #include "base/bind.h" 12 #include "base/bind.h"
13 #include "base/bind_helpers.h" 13 #include "base/bind_helpers.h"
14 #include "base/callback.h" 14 #include "base/callback.h"
15 #include "base/callback_helpers.h"
16 #include "base/lazy_instance.h"
15 #include "base/message_loop/message_loop.h" 17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram_macros.h"
16 #include "base/values.h" 19 #include "base/values.h"
17 #include "content/public/child/v8_value_converter.h" 20 #include "content/public/child/v8_value_converter.h"
18 #include "content/public/common/child_process_host.h" 21 #include "content/public/common/child_process_host.h"
19 #include "content/public/renderer/render_frame.h" 22 #include "content/public/renderer/render_frame.h"
20 #include "content/public/renderer/render_thread.h" 23 #include "content/public/renderer/render_thread.h"
21 #include "extensions/common/api/messaging/message.h" 24 #include "extensions/common/api/messaging/message.h"
22 #include "extensions/common/extension_messages.h" 25 #include "extensions/common/extension_messages.h"
23 #include "extensions/common/manifest_handlers/externally_connectable.h" 26 #include "extensions/common/manifest_handlers/externally_connectable.h"
24 #include "extensions/renderer/extension_frame_helper.h" 27 #include "extensions/renderer/extension_frame_helper.h"
28 #include "extensions/renderer/extension_port.h"
25 #include "extensions/renderer/gc_callback.h" 29 #include "extensions/renderer/gc_callback.h"
26 #include "extensions/renderer/script_context.h" 30 #include "extensions/renderer/script_context.h"
27 #include "extensions/renderer/script_context_set.h" 31 #include "extensions/renderer/script_context_set.h"
28 #include "extensions/renderer/v8_helpers.h" 32 #include "extensions/renderer/v8_helpers.h"
29 #include "third_party/WebKit/public/web/WebDocument.h" 33 #include "third_party/WebKit/public/web/WebDocument.h"
30 #include "third_party/WebKit/public/web/WebLocalFrame.h" 34 #include "third_party/WebKit/public/web/WebLocalFrame.h"
31 #include "third_party/WebKit/public/web/WebScopedUserGesture.h" 35 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
32 #include "third_party/WebKit/public/web/WebScopedWindowFocusAllowedIndicator.h" 36 #include "third_party/WebKit/public/web/WebScopedWindowFocusAllowedIndicator.h"
33 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" 37 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
34 #include "v8/include/v8.h" 38 #include "v8/include/v8.h"
35 39
36 // Message passing API example (in a content script): 40 // Message passing API example (in a content script):
37 // var extension =
38 // new chrome.Extension('00123456789abcdef0123456789abcdef0123456');
39 // var port = runtime.connect(); 41 // var port = runtime.connect();
40 // port.postMessage('Can you hear me now?'); 42 // port.postMessage('Can you hear me now?');
41 // port.onmessage.addListener(function(msg, port) { 43 // port.onmessage.addListener(function(msg, port) {
42 // alert('response=' + msg); 44 // alert('response=' + msg);
43 // port.postMessage('I got your reponse'); 45 // port.postMessage('I got your reponse');
44 // }); 46 // });
45 47
46 namespace extensions { 48 namespace extensions {
47 49
48 using v8_helpers::ToV8String; 50 using v8_helpers::ToV8String;
49 using v8_helpers::IsEmptyOrUndefied; 51 using v8_helpers::IsEmptyOrUndefied;
50 52
51 namespace { 53 namespace {
52 54
53 void HasMessagePort(int port_id, 55 // A global map between ScriptContext and MessagingBindings.
56 base::LazyInstance<std::map<ScriptContext*, MessagingBindings*>>
57 g_messaging_map = LAZY_INSTANCE_INITIALIZER;
58
59 void HasMessagePort(int global_port_id,
54 bool* has_port, 60 bool* has_port,
55 ScriptContext* script_context) { 61 ScriptContext* script_context) {
56 if (*has_port) 62 if (*has_port)
57 return; // Stop checking if the port was found. 63 return; // Stop checking if the port was found.
58 64
59 v8::Isolate* isolate = script_context->isolate(); 65 MessagingBindings* bindings = g_messaging_map.Get()[script_context];
60 v8::HandleScope handle_scope(isolate); 66 DCHECK(bindings);
61 67 if (bindings->GetPortWithGlobalId(global_port_id))
62 v8::Local<v8::Value> port_id_handle = v8::Integer::New(isolate, port_id); 68 *has_port = true;
63 v8::Local<v8::Value> v8_has_port =
64 script_context->module_system()->CallModuleMethod("messaging", "hasPort",
65 1, &port_id_handle);
66 if (IsEmptyOrUndefied(v8_has_port))
67 return;
68 CHECK(v8_has_port->IsBoolean());
69 if (!v8_has_port.As<v8::Boolean>()->Value())
70 return;
71 *has_port = true;
72 } 69 }
73 70
74 void DispatchOnConnectToScriptContext( 71 void DispatchOnConnectToScriptContext(
75 int target_port_id, 72 int global_target_port_id,
76 const std::string& channel_name, 73 const std::string& channel_name,
77 const ExtensionMsg_TabConnectionInfo* source, 74 const ExtensionMsg_TabConnectionInfo* source,
78 const ExtensionMsg_ExternalConnectionInfo& info, 75 const ExtensionMsg_ExternalConnectionInfo& info,
79 const std::string& tls_channel_id, 76 const std::string& tls_channel_id,
80 bool* port_created, 77 bool* port_created,
81 ScriptContext* script_context) { 78 ScriptContext* script_context) {
79 MessagingBindings* bindings = g_messaging_map.Get()[script_context];
80 DCHECK(bindings);
81
82 int opposite_port_id = global_target_port_id ^ 1;
83 if (bindings->GetPortWithGlobalId(opposite_port_id))
84 return; // The channel was opened by this same context; ignore it.
85
86 ExtensionPort* port =
87 bindings->CreateNewPortWithGlobalId(global_target_port_id);
88 int local_port_id = port->local_id();
89 // Remove the port.
90 base::ScopedClosureRunner remove_port(
91 base::Bind(&MessagingBindings::RemovePortWithLocalId,
92 bindings->GetWeakPtr(), local_port_id));
93
82 v8::Isolate* isolate = script_context->isolate(); 94 v8::Isolate* isolate = script_context->isolate();
83 v8::HandleScope handle_scope(isolate); 95 v8::HandleScope handle_scope(isolate);
84 96
85 std::unique_ptr<content::V8ValueConverter> converter(
86 content::V8ValueConverter::create());
87 97
88 const std::string& source_url_spec = info.source_url.spec(); 98 const std::string& source_url_spec = info.source_url.spec();
89 std::string target_extension_id = script_context->GetExtensionID(); 99 std::string target_extension_id = script_context->GetExtensionID();
90 const Extension* extension = script_context->extension(); 100 const Extension* extension = script_context->extension();
91 101
92 v8::Local<v8::Value> tab = v8::Null(isolate); 102 v8::Local<v8::Value> tab = v8::Null(isolate);
93 v8::Local<v8::Value> tls_channel_id_value = v8::Undefined(isolate); 103 v8::Local<v8::Value> tls_channel_id_value = v8::Undefined(isolate);
94 v8::Local<v8::Value> guest_process_id = v8::Undefined(isolate); 104 v8::Local<v8::Value> guest_process_id = v8::Undefined(isolate);
95 v8::Local<v8::Value> guest_render_frame_routing_id = v8::Undefined(isolate); 105 v8::Local<v8::Value> guest_render_frame_routing_id = v8::Undefined(isolate);
96 106
97 if (extension) { 107 if (extension) {
98 if (!source->tab.empty() && !extension->is_platform_app()) 108 if (!source->tab.empty() && !extension->is_platform_app()) {
109 std::unique_ptr<content::V8ValueConverter> converter(
110 content::V8ValueConverter::create());
99 tab = converter->ToV8Value(&source->tab, script_context->v8_context()); 111 tab = converter->ToV8Value(&source->tab, script_context->v8_context());
112 }
100 113
101 ExternallyConnectableInfo* externally_connectable = 114 ExternallyConnectableInfo* externally_connectable =
102 ExternallyConnectableInfo::Get(extension); 115 ExternallyConnectableInfo::Get(extension);
103 if (externally_connectable && 116 if (externally_connectable &&
104 externally_connectable->accepts_tls_channel_id) { 117 externally_connectable->accepts_tls_channel_id) {
105 v8::Local<v8::String> v8_tls_channel_id; 118 v8::Local<v8::String> v8_tls_channel_id;
106 if (ToV8String(isolate, tls_channel_id.c_str(), &v8_tls_channel_id)) 119 if (ToV8String(isolate, tls_channel_id.c_str(), &v8_tls_channel_id))
107 tls_channel_id_value = v8_tls_channel_id; 120 tls_channel_id_value = v8_tls_channel_id;
108 } 121 }
109 122
(...skipping 12 matching lines...) Expand all
122 !ToV8String(isolate, info.source_id.c_str(), &v8_source_id) || 135 !ToV8String(isolate, info.source_id.c_str(), &v8_source_id) ||
123 !ToV8String(isolate, target_extension_id.c_str(), 136 !ToV8String(isolate, target_extension_id.c_str(),
124 &v8_target_extension_id) || 137 &v8_target_extension_id) ||
125 !ToV8String(isolate, source_url_spec.c_str(), &v8_source_url_spec)) { 138 !ToV8String(isolate, source_url_spec.c_str(), &v8_source_url_spec)) {
126 NOTREACHED() << "dispatchOnConnect() passed non-string argument"; 139 NOTREACHED() << "dispatchOnConnect() passed non-string argument";
127 return; 140 return;
128 } 141 }
129 142
130 v8::Local<v8::Value> arguments[] = { 143 v8::Local<v8::Value> arguments[] = {
131 // portId 144 // portId
132 v8::Integer::New(isolate, target_port_id), 145 v8::Integer::New(isolate, local_port_id),
133 // channelName 146 // channelName
134 v8_channel_name, 147 v8_channel_name,
135 // sourceTab 148 // sourceTab
136 tab, 149 tab,
137 // source_frame_id 150 // source_frame_id
138 v8::Integer::New(isolate, source->frame_id), 151 v8::Integer::New(isolate, source->frame_id),
139 // guestProcessId 152 // guestProcessId
140 guest_process_id, 153 guest_process_id,
141 // guestRenderFrameRoutingId 154 // guestRenderFrameRoutingId
142 guest_render_frame_routing_id, 155 guest_render_frame_routing_id,
143 // sourceExtensionId 156 // sourceExtensionId
144 v8_source_id, 157 v8_source_id,
145 // targetExtensionId 158 // targetExtensionId
146 v8_target_extension_id, 159 v8_target_extension_id,
147 // sourceUrl 160 // sourceUrl
148 v8_source_url_spec, 161 v8_source_url_spec,
149 // tlsChannelId 162 // tlsChannelId
150 tls_channel_id_value, 163 tls_channel_id_value,
151 }; 164 };
152 165
153 v8::Local<v8::Value> retval = 166 v8::Local<v8::Value> retval =
154 script_context->module_system()->CallModuleMethod( 167 script_context->module_system()->CallModuleMethod(
155 "messaging", "dispatchOnConnect", arraysize(arguments), arguments); 168 "messaging", "dispatchOnConnect", arraysize(arguments), arguments);
156 169
157 if (!IsEmptyOrUndefied(retval)) { 170 if (!IsEmptyOrUndefied(retval)) {
158 CHECK(retval->IsBoolean()); 171 CHECK(retval->IsBoolean());
159 *port_created |= retval.As<v8::Boolean>()->Value(); 172 bool used = retval.As<v8::Boolean>()->Value();
173 *port_created |= used;
174 if (used) // Port was used; don't remove it.
175 remove_port.ReplaceClosure(base::Closure());
160 } else { 176 } else {
161 LOG(ERROR) << "Empty return value from dispatchOnConnect."; 177 LOG(ERROR) << "Empty return value from dispatchOnConnect.";
162 } 178 }
163 } 179 }
164 180
165 void DeliverMessageToScriptContext(const Message& message, 181 void DeliverMessageToScriptContext(const Message& message,
166 int target_port_id, 182 int global_target_port_id,
167 ScriptContext* script_context) { 183 ScriptContext* script_context) {
184 MessagingBindings* bindings = g_messaging_map.Get()[script_context];
185 DCHECK(bindings);
186 ExtensionPort* port = bindings->GetPortWithGlobalId(global_target_port_id);
187 if (!port)
188 return;
189
168 v8::Isolate* isolate = script_context->isolate(); 190 v8::Isolate* isolate = script_context->isolate();
169 v8::HandleScope handle_scope(isolate); 191 v8::HandleScope handle_scope(isolate);
170 192
171 // Check to see whether the context has this port before bothering to create
172 // the message.
173 v8::Local<v8::Value> port_id_handle = 193 v8::Local<v8::Value> port_id_handle =
174 v8::Integer::New(isolate, target_port_id); 194 v8::Integer::New(isolate, port->local_id());
175 v8::Local<v8::Value> has_port =
176 script_context->module_system()->CallModuleMethod("messaging", "hasPort",
177 1, &port_id_handle);
178 // Could be empty/undefined if an exception was thrown.
179 // TODO(kalman): Should this be built into CallModuleMethod?
180 if (IsEmptyOrUndefied(has_port))
181 return;
182 CHECK(has_port->IsBoolean());
183 if (!has_port.As<v8::Boolean>()->Value())
184 return;
185 195
186 v8::Local<v8::String> v8_data; 196 v8::Local<v8::String> v8_data;
187 if (!ToV8String(isolate, message.data.c_str(), &v8_data)) 197 if (!ToV8String(isolate, message.data.c_str(), &v8_data))
188 return; 198 return;
189 std::vector<v8::Local<v8::Value>> arguments; 199 std::vector<v8::Local<v8::Value>> arguments;
190 arguments.push_back(v8_data); 200 arguments.push_back(v8_data);
191 arguments.push_back(port_id_handle); 201 arguments.push_back(port_id_handle);
192 202
193 std::unique_ptr<blink::WebScopedUserGesture> web_user_gesture; 203 std::unique_ptr<blink::WebScopedUserGesture> web_user_gesture;
194 std::unique_ptr<blink::WebScopedWindowFocusAllowedIndicator> 204 std::unique_ptr<blink::WebScopedWindowFocusAllowedIndicator>
195 allow_window_focus; 205 allow_window_focus;
196 if (message.user_gesture) { 206 if (message.user_gesture) {
197 web_user_gesture.reset(new blink::WebScopedUserGesture); 207 web_user_gesture.reset(new blink::WebScopedUserGesture);
198 208
199 if (script_context->web_frame()) { 209 if (script_context->web_frame()) {
200 blink::WebDocument document = script_context->web_frame()->document(); 210 blink::WebDocument document = script_context->web_frame()->document();
201 allow_window_focus.reset(new blink::WebScopedWindowFocusAllowedIndicator( 211 allow_window_focus.reset(new blink::WebScopedWindowFocusAllowedIndicator(
202 &document)); 212 &document));
203 } 213 }
204 } 214 }
205 215
206 script_context->module_system()->CallModuleMethod( 216 script_context->module_system()->CallModuleMethod(
207 "messaging", "dispatchOnMessage", &arguments); 217 "messaging", "dispatchOnMessage", &arguments);
208 } 218 }
209 219
210 void DispatchOnDisconnectToScriptContext(int port_id, 220 void DispatchOnDisconnectToScriptContext(int global_port_id,
211 const std::string& error_message, 221 const std::string& error_message,
212 ScriptContext* script_context) { 222 ScriptContext* script_context) {
223 MessagingBindings* bindings = g_messaging_map.Get()[script_context];
224 DCHECK(bindings);
225 ExtensionPort* port = bindings->GetPortWithGlobalId(global_port_id);
226 if (!port)
227 return;
228
213 v8::Isolate* isolate = script_context->isolate(); 229 v8::Isolate* isolate = script_context->isolate();
214 v8::HandleScope handle_scope(isolate); 230 v8::HandleScope handle_scope(isolate);
215 231
216 std::vector<v8::Local<v8::Value>> arguments; 232 std::vector<v8::Local<v8::Value>> arguments;
217 arguments.push_back(v8::Integer::New(isolate, port_id)); 233 arguments.push_back(v8::Integer::New(isolate, port->local_id()));
218 v8::Local<v8::String> v8_error_message; 234 v8::Local<v8::String> v8_error_message;
219 if (!error_message.empty()) 235 if (!error_message.empty())
220 ToV8String(isolate, error_message.c_str(), &v8_error_message); 236 ToV8String(isolate, error_message.c_str(), &v8_error_message);
221 if (!v8_error_message.IsEmpty()) { 237 if (!v8_error_message.IsEmpty()) {
222 arguments.push_back(v8_error_message); 238 arguments.push_back(v8_error_message);
223 } else { 239 } else {
224 arguments.push_back(v8::Null(isolate)); 240 arguments.push_back(v8::Null(isolate));
225 } 241 }
226 242
227 script_context->module_system()->CallModuleMethod( 243 script_context->module_system()->CallModuleMethod(
228 "messaging", "dispatchOnDisconnect", &arguments); 244 "messaging", "dispatchOnDisconnect", &arguments);
229 } 245 }
230 246
231 } // namespace 247 } // namespace
232 248
233 MessagingBindings::MessagingBindings(ScriptContext* context) 249 MessagingBindings::MessagingBindings(ScriptContext* context)
234 : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) { 250 : ObjectBackedNativeHandler(context), weak_ptr_factory_(this) {
251 g_messaging_map.Get()[context] = this;
235 RouteFunction("CloseChannel", base::Bind(&MessagingBindings::CloseChannel, 252 RouteFunction("CloseChannel", base::Bind(&MessagingBindings::CloseChannel,
236 base::Unretained(this))); 253 base::Unretained(this)));
237 RouteFunction("PostMessage", base::Bind(&MessagingBindings::PostMessage, 254 RouteFunction("PostMessage", base::Bind(&MessagingBindings::PostMessage,
238 base::Unretained(this))); 255 base::Unretained(this)));
239 // TODO(fsamuel, kalman): Move BindToGC out of messaging natives. 256 // TODO(fsamuel, kalman): Move BindToGC out of messaging natives.
240 RouteFunction("BindToGC", base::Bind(&MessagingBindings::BindToGC, 257 RouteFunction("BindToGC", base::Bind(&MessagingBindings::BindToGC,
241 base::Unretained(this))); 258 base::Unretained(this)));
259 RouteFunction("OpenChannelToExtension", "runtime.connect",
260 base::Bind(&MessagingBindings::OpenChannelToExtension,
261 base::Unretained(this)));
262 RouteFunction("OpenChannelToNativeApp", "runtime.connectNative",
263 base::Bind(&MessagingBindings::OpenChannelToNativeApp,
264 base::Unretained(this)));
265 RouteFunction(
266 "OpenChannelToTab",
267 base::Bind(&MessagingBindings::OpenChannelToTab, base::Unretained(this)));
242 } 268 }
243 269
244 MessagingBindings::~MessagingBindings() {} 270 MessagingBindings::~MessagingBindings() {
271 g_messaging_map.Get().erase(context());
272 }
245 273
246 // static 274 // static
247 void MessagingBindings::ValidateMessagePort( 275 void MessagingBindings::ValidateMessagePort(
248 const ScriptContextSet& context_set, 276 const ScriptContextSet& context_set,
249 int port_id, 277 int global_port_id,
250 content::RenderFrame* render_frame) { 278 content::RenderFrame* render_frame) {
251 int routing_id = render_frame->GetRoutingID(); 279 int routing_id = render_frame->GetRoutingID();
252 280
253 bool has_port = false; 281 bool has_port = false;
254 context_set.ForEach(render_frame, 282 context_set.ForEach(render_frame,
255 base::Bind(&HasMessagePort, port_id, &has_port)); 283 base::Bind(&HasMessagePort, global_port_id, &has_port));
256 // Note: HasMessagePort invokes a JavaScript function. If the runtime of the
257 // extension bindings in JS have been compromised, then |render_frame| may be
258 // invalid at this point.
259 284
260 // A reply is only sent if the port is missing, because the port is assumed to 285 // A reply is only sent if the port is missing, because the port is assumed to
261 // exist unless stated otherwise. 286 // exist unless stated otherwise.
262 if (!has_port) { 287 if (!has_port) {
263 content::RenderThread::Get()->Send( 288 content::RenderThread::Get()->Send(
264 new ExtensionHostMsg_CloseMessagePort(routing_id, port_id, false)); 289 new ExtensionHostMsg_CloseMessagePort(routing_id,
290 global_port_id, false));
265 } 291 }
266 } 292 }
267 293
268 // static 294 // static
269 void MessagingBindings::DispatchOnConnect( 295 void MessagingBindings::DispatchOnConnect(
270 const ScriptContextSet& context_set, 296 const ScriptContextSet& context_set,
271 int target_port_id, 297 int target_port_id,
272 const std::string& channel_name, 298 const std::string& channel_name,
273 const ExtensionMsg_TabConnectionInfo& source, 299 const ExtensionMsg_TabConnectionInfo& source,
274 const ExtensionMsg_ExternalConnectionInfo& info, 300 const ExtensionMsg_ExternalConnectionInfo& info,
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
308 void MessagingBindings::DispatchOnDisconnect( 334 void MessagingBindings::DispatchOnDisconnect(
309 const ScriptContextSet& context_set, 335 const ScriptContextSet& context_set,
310 int port_id, 336 int port_id,
311 const std::string& error_message, 337 const std::string& error_message,
312 content::RenderFrame* restrict_to_render_frame) { 338 content::RenderFrame* restrict_to_render_frame) {
313 context_set.ForEach( 339 context_set.ForEach(
314 restrict_to_render_frame, 340 restrict_to_render_frame,
315 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message)); 341 base::Bind(&DispatchOnDisconnectToScriptContext, port_id, error_message));
316 } 342 }
317 343
344 ExtensionPort* MessagingBindings::GetPortWithGlobalId(int id) {
345 for (const auto& key_value : ports_) {
346 if (key_value.second->global_id() == id)
347 return key_value.second.get();
348 }
349 return nullptr;
350 }
351
352 ExtensionPort* MessagingBindings::CreateNewPortWithGlobalId(int global_id) {
353 int local_id = GetNextLocalId();
354 ExtensionPort* port =
355 ports_
356 .insert(std::make_pair(
357 local_id, base::MakeUnique<ExtensionPort>(context(), local_id)))
358 .first->second.get();
359 port->SetGlobalId(global_id);
360 return port;
361 }
362
363 void MessagingBindings::RemovePortWithLocalId(int local_id) {
364 ports_.erase(local_id);
365 }
366
367 base::WeakPtr<MessagingBindings> MessagingBindings::GetWeakPtr() {
368 return weak_ptr_factory_.GetWeakPtr();
369 }
370
318 void MessagingBindings::PostMessage( 371 void MessagingBindings::PostMessage(
319 const v8::FunctionCallbackInfo<v8::Value>& args) { 372 const v8::FunctionCallbackInfo<v8::Value>& args) {
320 // Arguments are (int32_t port_id, string message). 373 // Arguments are (int32_t port_id, string message).
321 CHECK(args.Length() == 2 && args[0]->IsInt32() && args[1]->IsString()); 374 CHECK(args.Length() == 2);
375 CHECK(args[0]->IsInt32());
376 CHECK(args[1]->IsString());
322 377
323 int port_id = args[0].As<v8::Int32>()->Value(); 378 int port_id = args[0].As<v8::Int32>()->Value();
324 379 auto iter = ports_.find(port_id);
325 content::RenderFrame* render_frame = context()->GetRenderFrame(); 380 if (iter != ports_.end()) {
326 if (render_frame) { 381 iter->second->PostExtensionMessage(base::MakeUnique<Message>(
327 render_frame->Send(new ExtensionHostMsg_PostMessage( 382 *v8::String::Utf8Value(args[1]),
328 render_frame->GetRoutingID(), port_id, 383 blink::WebUserGestureIndicator::isProcessingUserGesture()));
329 Message(*v8::String::Utf8Value(args[1]),
330 blink::WebUserGestureIndicator::isProcessingUserGesture())));
331 } 384 }
332 } 385 }
333 386
334 void MessagingBindings::CloseChannel( 387 void MessagingBindings::CloseChannel(
335 const v8::FunctionCallbackInfo<v8::Value>& args) { 388 const v8::FunctionCallbackInfo<v8::Value>& args) {
336 // Arguments are (int32_t port_id, bool force_close). 389 // Arguments are (int32_t port_id, bool force_close).
337 CHECK_EQ(2, args.Length()); 390 CHECK_EQ(2, args.Length());
338 CHECK(args[0]->IsInt32()); 391 CHECK(args[0]->IsInt32());
339 CHECK(args[1]->IsBoolean()); 392 CHECK(args[1]->IsBoolean());
340 393
341 int port_id = args[0].As<v8::Int32>()->Value(); 394 int port_id = args[0].As<v8::Int32>()->Value();
342 bool force_close = args[1].As<v8::Boolean>()->Value(); 395 bool force_close = args[1].As<v8::Boolean>()->Value();
343 ClosePort(port_id, force_close); 396 ClosePort(port_id, force_close);
344 } 397 }
345 398
346 void MessagingBindings::BindToGC( 399 void MessagingBindings::BindToGC(
347 const v8::FunctionCallbackInfo<v8::Value>& args) { 400 const v8::FunctionCallbackInfo<v8::Value>& args) {
348 CHECK(args.Length() == 3 && args[0]->IsObject() && args[1]->IsFunction() && 401 CHECK(args.Length() == 3);
349 args[2]->IsInt32()); 402 CHECK(args[0]->IsObject());
350 int port_id = args[2].As<v8::Int32>()->Value(); 403 CHECK(args[1]->IsFunction());
404 CHECK(args[2]->IsInt32());
405 int local_port_id = args[2].As<v8::Int32>()->Value();
351 base::Closure fallback = base::Bind(&base::DoNothing); 406 base::Closure fallback = base::Bind(&base::DoNothing);
352 if (port_id >= 0) { 407 if (local_port_id >= 0) {
353 // TODO(robwu): Falling back to closing the port shouldn't be needed. If 408 // TODO(robwu): Falling back to closing the port shouldn't be needed. If
354 // the script context is destroyed, then the frame has navigated. But that 409 // the script context is destroyed, then the frame has navigated. But that
355 // is already detected by the browser, so this logic is redundant. Remove 410 // is already detected by the browser, so this logic is redundant. Remove
356 // this fallback (and move BindToGC out of messaging because it is also 411 // this fallback (and move BindToGC out of messaging because it is also
357 // used in other places that have nothing to do with messaging...). 412 // used in other places that have nothing to do with messaging...).
358 fallback = base::Bind(&MessagingBindings::ClosePort, 413 fallback = base::Bind(&MessagingBindings::ClosePort,
359 weak_ptr_factory_.GetWeakPtr(), port_id, 414 weak_ptr_factory_.GetWeakPtr(), local_port_id,
360 false /* force_close */); 415 false /* force_close */);
361 } 416 }
362 // Destroys itself when the object is GC'd or context is invalidated. 417 // Destroys itself when the object is GC'd or context is invalidated.
363 new GCCallback(context(), args[0].As<v8::Object>(), 418 new GCCallback(context(), args[0].As<v8::Object>(),
364 args[1].As<v8::Function>(), fallback); 419 args[1].As<v8::Function>(), fallback);
365 } 420 }
366 421
367 void MessagingBindings::ClosePort(int port_id, bool force_close) { 422 void MessagingBindings::OpenChannelToExtension(
423 const v8::FunctionCallbackInfo<v8::Value>& args) {
424 content::RenderFrame* render_frame = context()->GetRenderFrame();
425 if (!render_frame)
426 return;
427
428 // The Javascript code should validate/fill the arguments.
429 CHECK_EQ(args.Length(), 3);
430 CHECK(args[0]->IsString());
431 CHECK(args[1]->IsString());
432 CHECK(args[2]->IsBoolean());
433
434 int local_id = GetNextLocalId();
435 ports_[local_id] = base::MakeUnique<ExtensionPort>(context(), local_id);
436
437 ExtensionMsg_ExternalConnectionInfo info;
438 // For messaging APIs, hosted apps should be considered a web page so hide
439 // its extension ID.
440 const Extension* extension = context()->extension();
441 if (extension && !extension->is_hosted_app())
442 info.source_id = extension->id();
443
444 info.target_id = *v8::String::Utf8Value(args[0]);
445 info.source_url = context()->url();
446 std::string channel_name = *v8::String::Utf8Value(args[1]);
447 // TODO(devlin): Why is this not part of info?
448 bool include_tls_channel_id =
449 args.Length() > 2 ? args[2]->BooleanValue() : false;
450
451 ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame);
452 DCHECK(frame_helper);
453 frame_helper->RequestPortId(
454 info, channel_name, include_tls_channel_id,
455 base::Bind(&MessagingBindings::SetGlobalPortId,
456 weak_ptr_factory_.GetWeakPtr(), local_id));
457
458 args.GetReturnValue().Set(static_cast<int32_t>(local_id));
459 }
460
461 void MessagingBindings::OpenChannelToNativeApp(
462 const v8::FunctionCallbackInfo<v8::Value>& args) {
463 // The Javascript code should validate/fill the arguments.
464 CHECK_EQ(args.Length(), 1);
465 CHECK(args[0]->IsString());
466 // This should be checked by our function routing code.
467 CHECK(context()->GetAvailability("runtime.connectNative").is_available());
468
469 content::RenderFrame* render_frame = context()->GetRenderFrame();
470 if (!render_frame)
471 return;
472
473 std::string native_app_name = *v8::String::Utf8Value(args[0]);
474
475 int local_id = GetNextLocalId();
476 ExtensionPort* port =
477 ports_
478 .insert(std::make_pair(
479 local_id, base::MakeUnique<ExtensionPort>(context(), local_id)))
480 .first->second.get();
481
482 int global_id = -1;
483 {
484 SCOPED_UMA_HISTOGRAM_TIMER(
485 "Extensions.Messaging.GetPortIdSyncTime.NativeApp");
486 // TODO(devlin): Make this async. crbug.com/642380
487 render_frame->Send(new ExtensionHostMsg_OpenChannelToNativeApp(
488 render_frame->GetRoutingID(), native_app_name, &global_id));
489 }
490
491 port->SetGlobalId(global_id);
492 args.GetReturnValue().Set(static_cast<int32_t>(local_id));
493 }
494
495 void MessagingBindings::OpenChannelToTab(
496 const v8::FunctionCallbackInfo<v8::Value>& args) {
497 content::RenderFrame* render_frame = context()->GetRenderFrame();
498 if (!render_frame)
499 return;
500
501 // tabs_custom_bindings.js unwraps arguments to tabs.connect/sendMessage and
502 // passes them to OpenChannelToTab, in the following order:
503 // - |tab_id| - Positive number that specifies the destination of the channel.
504 // - |frame_id| - Target frame(s) in the tab where onConnect is dispatched:
505 // -1 for all frames, 0 for the main frame, >0 for a child frame.
506 // - |extension_id| - ID of the initiating extension.
507 // - |channel_name| - A user-defined channel name.
508 CHECK(args.Length() == 4);
509 CHECK(args[0]->IsInt32());
510 CHECK(args[1]->IsInt32());
511 CHECK(args[2]->IsString());
512 CHECK(args[3]->IsString());
513
514 int local_id = GetNextLocalId();
515 ExtensionPort* port =
516 ports_
517 .insert(std::make_pair(
518 local_id, base::MakeUnique<ExtensionPort>(context(), local_id)))
519 .first->second.get();
520
521 ExtensionMsg_TabTargetConnectionInfo info;
522 info.tab_id = args[0]->Int32Value();
523 info.frame_id = args[1]->Int32Value();
524 std::string extension_id = *v8::String::Utf8Value(args[2]);
525 std::string channel_name = *v8::String::Utf8Value(args[3]);
526 int global_id = -1;
527 {
528 SCOPED_UMA_HISTOGRAM_TIMER("Extensions.Messaging.GetPortIdSyncTime.Tab");
529 // TODO(devlin): Make this async. crbug.com/642380
530 render_frame->Send(new ExtensionHostMsg_OpenChannelToTab(
531 render_frame->GetRoutingID(), info, extension_id, channel_name,
532 &global_id));
533 }
534 port->SetGlobalId(global_id);
535 args.GetReturnValue().Set(static_cast<int32_t>(local_id));
536 }
537
538 void MessagingBindings::ClosePort(int local_port_id, bool force_close) {
368 // TODO(robwu): Merge this logic with CloseChannel once the TODO in BindToGC 539 // TODO(robwu): Merge this logic with CloseChannel once the TODO in BindToGC
369 // has been addressed. 540 // has been addressed.
370 content::RenderFrame* render_frame = context()->GetRenderFrame(); 541 auto iter = ports_.find(local_port_id);
371 if (render_frame) { 542 if (iter != ports_.end()) {
372 render_frame->Send(new ExtensionHostMsg_CloseMessagePort( 543 std::unique_ptr<ExtensionPort> port = std::move(iter->second);
373 render_frame->GetRoutingID(), port_id, force_close)); 544 ports_.erase(iter);
545 port->Close(force_close);
546 // If the port hasn't been initialized, we can't delete it just yet, because
547 // we need to wait for it to send any pending messages. This is important
548 // to support the flow:
549 // var port = chrome.runtime.connect();
550 // port.postMessage({message});
551 // port.disconnect();
552 if (!port->initialized())
553 disconnected_ports_[port->local_id()] = std::move(port);
374 } 554 }
375 } 555 }
376 556
557 void MessagingBindings::SetGlobalPortId(int local_id, int global_id) {
558 auto iter = ports_.find(local_id);
559 if (iter != ports_.end()) {
560 iter->second->SetGlobalId(global_id);
561 return;
562 }
563
564 iter = disconnected_ports_.find(local_id);
565 DCHECK(iter != disconnected_ports_.end());
566 iter->second->SetGlobalId(global_id);
567 // Setting the global id dispatches pending messages, so we can delete the
568 // port now.
569 disconnected_ports_.erase(iter);
570 }
571
572 int MessagingBindings::GetNextLocalId() { return next_local_id_++; }
573
377 } // namespace extensions 574 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/messaging_bindings.h ('k') | extensions/renderer/resources/messaging.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698