OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/common/mojo/mojo_shell_connection_impl.h" | |
6 | |
7 #include <queue> | |
8 #include <utility> | |
9 #include <vector> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/callback_helpers.h" | |
13 #include "base/lazy_instance.h" | |
14 #include "base/macros.h" | |
15 #include "base/memory/ptr_util.h" | |
16 #include "base/message_loop/message_loop.h" | |
17 #include "base/threading/thread_checker.h" | |
18 #include "content/common/mojo/embedded_application_runner.h" | |
19 #include "content/public/common/connection_filter.h" | |
20 #include "mojo/public/cpp/bindings/binding_set.h" | |
21 #include "mojo/public/cpp/system/message_pipe.h" | |
22 #include "services/shell/public/cpp/service.h" | |
23 #include "services/shell/public/cpp/service_context.h" | |
24 #include "services/shell/public/interfaces/service_factory.mojom.h" | |
25 #include "services/shell/runner/common/client_util.h" | |
26 | |
27 namespace content { | |
28 namespace { | |
29 | |
30 base::LazyInstance<std::unique_ptr<MojoShellConnection>>::Leaky | |
31 g_connection_for_process = LAZY_INSTANCE_INITIALIZER; | |
32 | |
33 MojoShellConnection::Factory* mojo_shell_connection_factory = nullptr; | |
34 | |
35 } // namespace | |
36 | |
37 // A ref-counted object which owns the IO thread state of a | |
38 // MojoShellConnectionImpl. This includes Service and ServiceFactory | |
39 // bindings. | |
40 class MojoShellConnectionImpl::IOThreadContext | |
41 : public base::RefCountedThreadSafe<IOThreadContext>, | |
42 public shell::Service, | |
43 public shell::InterfaceFactory<shell::mojom::ServiceFactory>, | |
44 public shell::mojom::ServiceFactory { | |
45 public: | |
46 using InitializeCallback = base::Callback<void(const shell::Identity&)>; | |
47 using ServiceFactoryCallback = | |
48 base::Callback<void(shell::mojom::ServiceRequest, const std::string&)>; | |
49 | |
50 IOThreadContext(shell::mojom::ServiceRequest service_request, | |
51 scoped_refptr<base::SequencedTaskRunner> io_task_runner, | |
52 std::unique_ptr<shell::Connector> io_thread_connector, | |
53 shell::mojom::ConnectorRequest connector_request) | |
54 : pending_service_request_(std::move(service_request)), | |
55 io_task_runner_(io_task_runner), | |
56 io_thread_connector_(std::move(io_thread_connector)), | |
57 pending_connector_request_(std::move(connector_request)), | |
58 weak_factory_(this) { | |
59 // This will be reattached by any of the IO thread functions on first call. | |
60 io_thread_checker_.DetachFromThread(); | |
61 } | |
62 | |
63 // Safe to call from any thread. | |
64 void Start(const InitializeCallback& initialize_callback, | |
65 const ServiceFactoryCallback& create_service_callback, | |
66 const base::Closure& stop_callback) { | |
67 DCHECK(!started_); | |
68 | |
69 started_ = true; | |
70 callback_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | |
71 initialize_handler_ = initialize_callback; | |
72 create_service_callback_ = create_service_callback; | |
73 stop_callback_ = stop_callback; | |
74 io_task_runner_->PostTask( | |
75 FROM_HERE, base::Bind(&IOThreadContext::StartOnIOThread, this)); | |
76 } | |
77 | |
78 // Safe to call from whichever thread called Start() (or may have called | |
79 // Start()). Must be called before IO thread shutdown. | |
80 void ShutDown() { | |
81 if (!started_) | |
82 return; | |
83 | |
84 bool posted = io_task_runner_->PostTask( | |
85 FROM_HERE, base::Bind(&IOThreadContext::ShutDownOnIOThread, this)); | |
86 DCHECK(posted); | |
87 } | |
88 | |
89 // Safe to call any time before a message is received from a process. | |
90 // i.e. can be called when starting the process but not afterwards. | |
91 int AddConnectionFilter(std::unique_ptr<ConnectionFilter> filter) { | |
92 base::AutoLock lock(lock_); | |
93 | |
94 int id = ++next_filter_id_; | |
95 | |
96 // We should never hit this in practice, but let's crash just in case. | |
97 CHECK_NE(id, kInvalidConnectionFilterId); | |
98 | |
99 connection_filters_[id] = std::move(filter); | |
100 return id; | |
101 } | |
102 | |
103 void RemoveConnectionFilter(int filter_id) { | |
104 io_task_runner_->PostTask( | |
105 FROM_HERE, | |
106 base::Bind(&IOThreadContext::RemoveConnectionFilterOnIOThread, this, | |
107 filter_id)); | |
108 } | |
109 | |
110 // Safe to call any time before Start() is called. | |
111 void SetDefaultBinderForBrowserConnection( | |
112 const shell::InterfaceRegistry::Binder& binder) { | |
113 DCHECK(!started_); | |
114 default_browser_binder_ = base::Bind( | |
115 &IOThreadContext::CallBinderOnTaskRunner, | |
116 base::ThreadTaskRunnerHandle::Get(), binder); | |
117 } | |
118 | |
119 private: | |
120 friend class base::RefCountedThreadSafe<IOThreadContext>; | |
121 | |
122 class MessageLoopObserver : public base::MessageLoop::DestructionObserver { | |
123 public: | |
124 explicit MessageLoopObserver(base::WeakPtr<IOThreadContext> context) | |
125 : context_(context) { | |
126 base::MessageLoop::current()->AddDestructionObserver(this); | |
127 } | |
128 | |
129 ~MessageLoopObserver() override { | |
130 base::MessageLoop::current()->RemoveDestructionObserver(this); | |
131 } | |
132 | |
133 void ShutDown() { | |
134 if (!is_active_) | |
135 return; | |
136 | |
137 // The call into |context_| below may reenter ShutDown(), hence we set | |
138 // |is_active_| to false here. | |
139 is_active_ = false; | |
140 if (context_) | |
141 context_->ShutDownOnIOThread(); | |
142 | |
143 delete this; | |
144 } | |
145 | |
146 private: | |
147 void WillDestroyCurrentMessageLoop() override { | |
148 DCHECK(is_active_); | |
149 ShutDown(); | |
150 } | |
151 | |
152 bool is_active_ = true; | |
153 base::WeakPtr<IOThreadContext> context_; | |
154 | |
155 DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver); | |
156 }; | |
157 | |
158 ~IOThreadContext() override {} | |
159 | |
160 void StartOnIOThread() { | |
161 // Should bind |io_thread_checker_| to the context's thread. | |
162 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
163 service_context_.reset(new shell::ServiceContext( | |
164 this, std::move(pending_service_request_), | |
165 std::move(io_thread_connector_), | |
166 std::move(pending_connector_request_))); | |
167 | |
168 // MessageLoopObserver owns itself. | |
169 message_loop_observer_ = | |
170 new MessageLoopObserver(weak_factory_.GetWeakPtr()); | |
171 } | |
172 | |
173 void ShutDownOnIOThread() { | |
174 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
175 | |
176 weak_factory_.InvalidateWeakPtrs(); | |
177 | |
178 // Note that this method may be invoked by MessageLoopObserver observing | |
179 // MessageLoop destruction. In that case, this call to ShutDown is | |
180 // effectively a no-op. In any case it's safe. | |
181 if (message_loop_observer_) { | |
182 message_loop_observer_->ShutDown(); | |
183 message_loop_observer_ = nullptr; | |
184 } | |
185 | |
186 // Resetting the ServiceContext below may otherwise release the last | |
187 // reference to this IOThreadContext. We keep it alive until the stack | |
188 // unwinds. | |
189 scoped_refptr<IOThreadContext> keepalive(this); | |
190 | |
191 factory_bindings_.CloseAllBindings(); | |
192 service_context_.reset(); | |
193 | |
194 ClearConnectionFiltersOnIOThread(); | |
195 } | |
196 | |
197 void ClearConnectionFiltersOnIOThread() { | |
198 base::AutoLock lock(lock_); | |
199 connection_filters_.clear(); | |
200 } | |
201 | |
202 void RemoveConnectionFilterOnIOThread(int filter_id) { | |
203 base::AutoLock lock(lock_); | |
204 auto it = connection_filters_.find(filter_id); | |
205 DCHECK(it != connection_filters_.end()); | |
206 connection_filters_.erase(it); | |
207 } | |
208 | |
209 void OnBrowserConnectionLost() { | |
210 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
211 has_browser_connection_ = false; | |
212 } | |
213 | |
214 ///////////////////////////////////////////////////////////////////////////// | |
215 // shell::Service implementation | |
216 | |
217 void OnStart(const shell::Identity& identity) override { | |
218 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
219 DCHECK(!initialize_handler_.is_null()); | |
220 id_ = identity; | |
221 | |
222 InitializeCallback handler = base::ResetAndReturn(&initialize_handler_); | |
223 callback_task_runner_->PostTask(FROM_HERE, base::Bind(handler, identity)); | |
224 } | |
225 | |
226 bool OnConnect(const shell::Identity& remote_identity, | |
227 shell::InterfaceRegistry* registry) override { | |
228 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
229 std::string remote_app = remote_identity.name(); | |
230 if (remote_app == "service:shell") { | |
231 // Only expose the SCF interface to the shell. | |
232 registry->AddInterface<shell::mojom::ServiceFactory>(this); | |
233 return true; | |
234 } | |
235 | |
236 bool accept = false; | |
237 { | |
238 base::AutoLock lock(lock_); | |
239 for (auto& entry : connection_filters_) { | |
240 accept |= entry.second->OnConnect(remote_identity, registry, | |
241 service_context_->connector()); | |
242 } | |
243 } | |
244 | |
245 if (remote_identity.name() == "service:content_browser" && | |
246 !has_browser_connection_) { | |
247 has_browser_connection_ = true; | |
248 registry->set_default_binder(default_browser_binder_); | |
249 registry->SetConnectionLostClosure( | |
250 base::Bind(&IOThreadContext::OnBrowserConnectionLost, this)); | |
251 return true; | |
252 } | |
253 | |
254 // If no filters were interested, reject the connection. | |
255 return accept; | |
256 } | |
257 | |
258 bool OnStop() override { | |
259 ClearConnectionFiltersOnIOThread(); | |
260 callback_task_runner_->PostTask(FROM_HERE, stop_callback_); | |
261 return true; | |
262 } | |
263 | |
264 ///////////////////////////////////////////////////////////////////////////// | |
265 // shell::InterfaceFactory<shell::mojom::ServiceFactory> implementation | |
266 | |
267 void Create(const shell::Identity& remote_identity, | |
268 shell::mojom::ServiceFactoryRequest request) override { | |
269 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
270 factory_bindings_.AddBinding(this, std::move(request)); | |
271 } | |
272 | |
273 ///////////////////////////////////////////////////////////////////////////// | |
274 // shell::mojom::ServiceFactory implementation | |
275 | |
276 void CreateService(shell::mojom::ServiceRequest request, | |
277 const std::string& name) override { | |
278 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
279 callback_task_runner_->PostTask( | |
280 FROM_HERE, | |
281 base::Bind(create_service_callback_, base::Passed(&request), name)); | |
282 } | |
283 | |
284 static void CallBinderOnTaskRunner( | |
285 scoped_refptr<base::SequencedTaskRunner> task_runner, | |
286 const shell::InterfaceRegistry::Binder& binder, | |
287 const std::string& interface_name, | |
288 mojo::ScopedMessagePipeHandle request_handle) { | |
289 task_runner->PostTask(FROM_HERE, base::Bind(binder, interface_name, | |
290 base::Passed(&request_handle))); | |
291 } | |
292 | |
293 base::ThreadChecker io_thread_checker_; | |
294 bool started_ = false; | |
295 | |
296 // Temporary state established on construction and consumed on the IO thread | |
297 // once the connection is started. | |
298 shell::mojom::ServiceRequest pending_service_request_; | |
299 scoped_refptr<base::SequencedTaskRunner> io_task_runner_; | |
300 std::unique_ptr<shell::Connector> io_thread_connector_; | |
301 shell::mojom::ConnectorRequest pending_connector_request_; | |
302 | |
303 // TaskRunner on which to run our owner's callbacks, i.e. the ones passed to | |
304 // Start(). | |
305 scoped_refptr<base::SequencedTaskRunner> callback_task_runner_; | |
306 | |
307 // Callback to run once Service::OnStart is invoked. | |
308 InitializeCallback initialize_handler_; | |
309 | |
310 // Callback to run when a new Service request is received. | |
311 ServiceFactoryCallback create_service_callback_; | |
312 | |
313 // Callback to run if the service is stopped by the service manager. | |
314 base::Closure stop_callback_; | |
315 | |
316 // Called once a connection has been received from the browser process & the | |
317 // default binder (below) has been set up. | |
318 bool has_browser_connection_ = false; | |
319 | |
320 shell::Identity id_; | |
321 | |
322 // Default binder callback used for the browser connection's | |
323 // InterfaceRegistry. | |
324 // | |
325 // TODO(rockot): Remove this once all interfaces exposed to the browser are | |
326 // exposed via a ConnectionFilter. | |
327 shell::InterfaceRegistry::Binder default_browser_binder_; | |
328 | |
329 std::unique_ptr<shell::ServiceContext> service_context_; | |
330 mojo::BindingSet<shell::mojom::ServiceFactory> factory_bindings_; | |
331 int next_filter_id_ = kInvalidConnectionFilterId; | |
332 | |
333 // Not owned. | |
334 MessageLoopObserver* message_loop_observer_ = nullptr; | |
335 | |
336 // Guards |connection_filters_|. | |
337 base::Lock lock_; | |
338 std::map<int, std::unique_ptr<ConnectionFilter>> connection_filters_; | |
339 | |
340 base::WeakPtrFactory<IOThreadContext> weak_factory_; | |
341 | |
342 DISALLOW_COPY_AND_ASSIGN(IOThreadContext); | |
343 }; | |
344 | |
345 //////////////////////////////////////////////////////////////////////////////// | |
346 // MojoShellConnection, public: | |
347 | |
348 // static | |
349 void MojoShellConnection::SetForProcess( | |
350 std::unique_ptr<MojoShellConnection> connection) { | |
351 DCHECK(!g_connection_for_process.Get()); | |
352 g_connection_for_process.Get() = std::move(connection); | |
353 } | |
354 | |
355 // static | |
356 MojoShellConnection* MojoShellConnection::GetForProcess() { | |
357 return g_connection_for_process.Get().get(); | |
358 } | |
359 | |
360 // static | |
361 void MojoShellConnection::DestroyForProcess() { | |
362 // This joins the shell controller thread. | |
363 g_connection_for_process.Get().reset(); | |
364 } | |
365 | |
366 // static | |
367 void MojoShellConnection::SetFactoryForTest(Factory* factory) { | |
368 DCHECK(!g_connection_for_process.Get()); | |
369 mojo_shell_connection_factory = factory; | |
370 } | |
371 | |
372 // static | |
373 std::unique_ptr<MojoShellConnection> MojoShellConnection::Create( | |
374 shell::mojom::ServiceRequest request, | |
375 scoped_refptr<base::SequencedTaskRunner> io_task_runner) { | |
376 if (mojo_shell_connection_factory) | |
377 return mojo_shell_connection_factory->Run(); | |
378 return base::MakeUnique<MojoShellConnectionImpl>( | |
379 std::move(request), io_task_runner); | |
380 } | |
381 | |
382 MojoShellConnection::~MojoShellConnection() {} | |
383 | |
384 //////////////////////////////////////////////////////////////////////////////// | |
385 // MojoShellConnectionImpl, public: | |
386 | |
387 MojoShellConnectionImpl::MojoShellConnectionImpl( | |
388 shell::mojom::ServiceRequest request, | |
389 scoped_refptr<base::SequencedTaskRunner> io_task_runner) | |
390 : weak_factory_(this) { | |
391 shell::mojom::ConnectorRequest connector_request; | |
392 connector_ = shell::Connector::Create(&connector_request); | |
393 | |
394 std::unique_ptr<shell::Connector> io_thread_connector = connector_->Clone(); | |
395 context_ = new IOThreadContext( | |
396 std::move(request), io_task_runner, std::move(io_thread_connector), | |
397 std::move(connector_request)); | |
398 } | |
399 | |
400 MojoShellConnectionImpl::~MojoShellConnectionImpl() { | |
401 context_->ShutDown(); | |
402 } | |
403 | |
404 //////////////////////////////////////////////////////////////////////////////// | |
405 // MojoShellConnectionImpl, MojoShellConnection implementation: | |
406 | |
407 void MojoShellConnectionImpl::Start() { | |
408 context_->Start( | |
409 base::Bind(&MojoShellConnectionImpl::OnContextInitialized, | |
410 weak_factory_.GetWeakPtr()), | |
411 base::Bind(&MojoShellConnectionImpl::CreateService, | |
412 weak_factory_.GetWeakPtr()), | |
413 base::Bind(&MojoShellConnectionImpl::OnConnectionLost, | |
414 weak_factory_.GetWeakPtr())); | |
415 } | |
416 | |
417 void MojoShellConnectionImpl::SetInitializeHandler( | |
418 const base::Closure& handler) { | |
419 DCHECK(initialize_handler_.is_null()); | |
420 initialize_handler_ = handler; | |
421 } | |
422 | |
423 shell::Connector* MojoShellConnectionImpl::GetConnector() { | |
424 return connector_.get(); | |
425 } | |
426 | |
427 const shell::Identity& MojoShellConnectionImpl::GetIdentity() const { | |
428 return identity_; | |
429 } | |
430 | |
431 void MojoShellConnectionImpl::SetConnectionLostClosure( | |
432 const base::Closure& closure) { | |
433 connection_lost_handler_ = closure; | |
434 } | |
435 | |
436 void MojoShellConnectionImpl::SetupInterfaceRequestProxies( | |
437 shell::InterfaceRegistry* registry, | |
438 shell::InterfaceProvider* provider) { | |
439 // It's safe to bind |registry| as a raw pointer because the caller must | |
440 // guarantee that it outlives |this|, and |this| is bound as a weak ptr here. | |
441 context_->SetDefaultBinderForBrowserConnection( | |
442 base::Bind(&MojoShellConnectionImpl::GetInterface, | |
443 weak_factory_.GetWeakPtr(), registry)); | |
444 | |
445 // TODO(beng): remove provider parameter. | |
446 } | |
447 | |
448 int MojoShellConnectionImpl::AddConnectionFilter( | |
449 std::unique_ptr<ConnectionFilter> filter) { | |
450 return context_->AddConnectionFilter(std::move(filter)); | |
451 } | |
452 | |
453 void MojoShellConnectionImpl::RemoveConnectionFilter(int filter_id) { | |
454 context_->RemoveConnectionFilter(filter_id); | |
455 } | |
456 | |
457 void MojoShellConnectionImpl::AddEmbeddedService( | |
458 const std::string& name, | |
459 const MojoApplicationInfo& info) { | |
460 std::unique_ptr<EmbeddedApplicationRunner> app( | |
461 new EmbeddedApplicationRunner(name, info)); | |
462 AddServiceRequestHandler( | |
463 name, base::Bind(&EmbeddedApplicationRunner::BindServiceRequest, | |
464 base::Unretained(app.get()))); | |
465 auto result = embedded_apps_.insert(std::make_pair(name, std::move(app))); | |
466 DCHECK(result.second); | |
467 } | |
468 | |
469 void MojoShellConnectionImpl::AddServiceRequestHandler( | |
470 const std::string& name, | |
471 const ServiceRequestHandler& handler) { | |
472 auto result = request_handlers_.insert(std::make_pair(name, handler)); | |
473 DCHECK(result.second); | |
474 } | |
475 | |
476 void MojoShellConnectionImpl::CreateService( | |
477 shell::mojom::ServiceRequest request, | |
478 const std::string& name) { | |
479 auto it = request_handlers_.find(name); | |
480 if (it != request_handlers_.end()) | |
481 it->second.Run(std::move(request)); | |
482 } | |
483 | |
484 void MojoShellConnectionImpl::OnContextInitialized( | |
485 const shell::Identity& identity) { | |
486 identity_ = identity; | |
487 if (!initialize_handler_.is_null()) | |
488 base::ResetAndReturn(&initialize_handler_).Run(); | |
489 } | |
490 | |
491 void MojoShellConnectionImpl::OnConnectionLost() { | |
492 if (!connection_lost_handler_.is_null()) | |
493 connection_lost_handler_.Run(); | |
494 } | |
495 | |
496 void MojoShellConnectionImpl::GetInterface( | |
497 shell::mojom::InterfaceProvider* provider, | |
498 const std::string& interface_name, | |
499 mojo::ScopedMessagePipeHandle request_handle) { | |
500 provider->GetInterface(interface_name, std::move(request_handle)); | |
501 } | |
502 | |
503 } // namespace content | |
504 | |
OLD | NEW |