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

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

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

Powered by Google App Engine
This is Rietveld 408576698