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

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

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

Powered by Google App Engine
This is Rietveld 408576698