OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "mojo/shell/shell.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <utility> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/command_line.h" | |
13 #include "base/guid.h" | |
14 #include "base/logging.h" | |
15 #include "base/macros.h" | |
16 #include "base/process/process.h" | |
17 #include "base/process/process_handle.h" | |
18 #include "base/stl_util.h" | |
19 #include "base/strings/string_util.h" | |
20 #include "base/trace_event/trace_event.h" | |
21 #include "mojo/common/url_type_converters.h" | |
22 #include "mojo/public/cpp/bindings/binding.h" | |
23 #include "mojo/public/cpp/bindings/binding_set.h" | |
24 #include "mojo/shell/connect_util.h" | |
25 #include "mojo/shell/public/cpp/connector.h" | |
26 #include "mojo/shell/public/cpp/names.h" | |
27 #include "mojo/shell/public/cpp/shell_connection.h" | |
28 #include "mojo/shell/public/interfaces/connector.mojom.h" | |
29 #include "mojo/shell/public/interfaces/shell.mojom.h" | |
30 #include "mojo/shell/public/interfaces/shell_client.mojom.h" | |
31 #include "mojo/util/filename_util.h" | |
32 #include "url/gurl.h" | |
33 | |
34 namespace mojo { | |
35 namespace shell { | |
36 namespace { | |
37 const char kCatalogName[] = "mojo:catalog"; | |
38 const char kShellName[] = "mojo:shell"; | |
39 const char kCapabilityClass_UserID[] = "user_id"; | |
40 const char kCapabilityClass_ClientProcess[] = "client_process"; | |
41 const char kCapabilityClass_InstanceName[] = "instance_name"; | |
42 const char kCapabilityClass_AllUsers[] = "all_users"; | |
43 | |
44 void EmptyResolverCallback(mojom::ResolveResultPtr result) {} | |
45 | |
46 } | |
47 | |
48 Identity CreateShellIdentity() { | |
49 return Identity(kShellName, mojom::kRootUserID); | |
50 } | |
51 | |
52 Identity CreateCatalogIdentity() { | |
53 return Identity(kCatalogName, mojom::kRootUserID); | |
54 } | |
55 | |
56 CapabilitySpec GetPermissiveCapabilities() { | |
57 CapabilitySpec capabilities; | |
58 CapabilityRequest spec; | |
59 spec.interfaces.insert("*"); | |
60 capabilities.required["*"] = spec; | |
61 return capabilities; | |
62 } | |
63 | |
64 CapabilityRequest GetCapabilityRequest(const CapabilitySpec& source_spec, | |
65 const Identity& target) { | |
66 // Start by looking for specs specific to the supplied identity. | |
67 auto it = source_spec.required.find(target.name()); | |
68 if (it != source_spec.required.end()) | |
69 return it->second; | |
70 | |
71 // Fall back to looking for a wildcard rule. | |
72 it = source_spec.required.find("*"); | |
73 if (it != source_spec.required.end()) | |
74 return it->second; | |
75 | |
76 // Finally, nothing is allowed. | |
77 return CapabilityRequest(); | |
78 } | |
79 | |
80 CapabilityRequest GenerateCapabilityRequestForConnection( | |
81 const CapabilitySpec& source_spec, | |
82 const Identity& target, | |
83 const CapabilitySpec& target_spec) { | |
84 CapabilityRequest request = GetCapabilityRequest(source_spec, target); | |
85 // Flatten all interfaces from classes requested by the source into the | |
86 // allowed interface set in the request. | |
87 for (const auto& class_name : request.classes) { | |
88 auto it = target_spec.provided.find(class_name); | |
89 if (it != target_spec.provided.end()) { | |
90 for (const auto& interface_name : it->second) | |
91 request.interfaces.insert(interface_name); | |
92 } | |
93 } | |
94 return request; | |
95 } | |
96 | |
97 bool HasClass(const CapabilitySpec& spec, const std::string& class_name) { | |
98 auto it = spec.required.find(kShellName); | |
99 if (it == spec.required.end()) | |
100 return false; | |
101 return it->second.classes.find(class_name) != it->second.classes.end(); | |
102 } | |
103 | |
104 // Encapsulates a connection to an instance of an application, tracked by the | |
105 // shell's Shell. | |
106 class Shell::Instance : public mojom::Connector, | |
107 public mojom::PIDReceiver, | |
108 public ShellClient, | |
109 public InterfaceFactory<mojom::Shell>, | |
110 public mojom::Shell { | |
111 public: | |
112 Instance(mojo::shell::Shell* shell, | |
113 const Identity& identity, | |
114 const CapabilitySpec& capability_spec) | |
115 : shell_(shell), | |
116 id_(GenerateUniqueID()), | |
117 identity_(identity), | |
118 capability_spec_(capability_spec), | |
119 allow_any_application_(capability_spec.required.count("*") == 1), | |
120 pid_receiver_binding_(this), | |
121 weak_factory_(this) { | |
122 if (identity_.name() == kShellName || | |
123 shell_->GetLoaderForName(identity_.name())) { | |
124 pid_ = base::Process::Current().Pid(); | |
125 } | |
126 DCHECK_NE(mojom::kInvalidInstanceID, id_); | |
127 } | |
128 | |
129 ~Instance() override { | |
130 if (parent_) | |
131 parent_->RemoveChild(this); | |
132 // |children_| will be modified during destruction. | |
133 std::set<Instance*> children = children_; | |
134 for (auto child : children) | |
135 shell_->OnInstanceError(child); | |
136 } | |
137 | |
138 Instance* parent() { return parent_; } | |
139 void AddChild(Instance* child) { | |
140 children_.insert(child); | |
141 child->parent_ = this; | |
142 } | |
143 void RemoveChild(Instance* child) { | |
144 auto it = children_.find(child); | |
145 DCHECK(it != children_.end()); | |
146 children_.erase(it); | |
147 child->parent_ = nullptr; | |
148 } | |
149 | |
150 void ConnectToClient(scoped_ptr<ConnectParams> params) { | |
151 CHECK(shell_client_.is_bound()); | |
152 params->connect_callback().Run(mojom::ConnectResult::SUCCEEDED, | |
153 identity_.user_id(), id_); | |
154 uint32_t source_id = mojom::kInvalidInstanceID; | |
155 CapabilityRequest spec; | |
156 spec.interfaces.insert("*"); | |
157 Instance* source = shell_->GetExistingInstance(params->source()); | |
158 if (source) { | |
159 spec = GenerateCapabilityRequestForConnection( | |
160 source->capability_spec_, identity_, capability_spec_); | |
161 source_id = source->id(); | |
162 } | |
163 shell_client_->AcceptConnection( | |
164 mojom::Identity::From(params->source()), source_id, | |
165 params->TakeRemoteInterfaces(), params->TakeLocalInterfaces(), | |
166 mojom::CapabilityRequest::From(spec), params->target().name()); | |
167 } | |
168 | |
169 void StartWithClient(mojom::ShellClientPtr client) { | |
170 CHECK(!shell_client_); | |
171 shell_client_ = std::move(client); | |
172 shell_client_.set_connection_error_handler( | |
173 base::Bind(&Instance::OnShellClientLost, base::Unretained(this), | |
174 shell_->GetWeakPtr())); | |
175 shell_client_->Initialize(mojom::Identity::From(identity_), id_, | |
176 base::Bind(&Instance::OnInitializeResponse, | |
177 base::Unretained(this))); | |
178 } | |
179 | |
180 void StartWithClientProcessConnection( | |
181 mojom::ClientProcessConnectionPtr client_process_connection) { | |
182 mojom::ShellClientPtr client; | |
183 client.Bind(mojom::ShellClientPtrInfo( | |
184 std::move(client_process_connection->shell_client), 0)); | |
185 pid_receiver_binding_.Bind( | |
186 std::move(client_process_connection->pid_receiver_request)); | |
187 StartWithClient(std::move(client)); | |
188 } | |
189 | |
190 void StartWithFilePath(const base::FilePath& path) { | |
191 CHECK(!shell_client_); | |
192 scoped_ptr<NativeRunner> runner = | |
193 shell_->native_runner_factory_->Create(path); | |
194 bool start_sandboxed = false; | |
195 mojom::ShellClientPtr client = runner->Start( | |
196 path, identity_, start_sandboxed, | |
197 base::Bind(&Instance::PIDAvailable, weak_factory_.GetWeakPtr()), | |
198 base::Bind(&mojo::shell::Shell::CleanupRunner, | |
199 shell_->weak_ptr_factory_.GetWeakPtr(), runner.get())); | |
200 shell_->native_runners_.push_back(std::move(runner)); | |
201 StartWithClient(std::move(client)); | |
202 } | |
203 | |
204 mojom::InstanceInfoPtr CreateInstanceInfo() const { | |
205 mojom::InstanceInfoPtr info(mojom::InstanceInfo::New()); | |
206 info->id = id_; | |
207 info->identity = mojom::Identity::From(identity_); | |
208 info->pid = pid_; | |
209 return info; | |
210 } | |
211 | |
212 const CapabilitySpec& capability_spec() const { | |
213 return capability_spec_; | |
214 } | |
215 const Identity& identity() const { return identity_; } | |
216 uint32_t id() const { return id_; } | |
217 | |
218 // ShellClient: | |
219 bool AcceptConnection(Connection* connection) override { | |
220 connection->AddInterface<mojom::Shell>(this); | |
221 return true; | |
222 } | |
223 | |
224 private: | |
225 // mojom::Connector implementation: | |
226 void Connect(mojom::IdentityPtr target_ptr, | |
227 mojom::InterfaceProviderRequest remote_interfaces, | |
228 mojom::InterfaceProviderPtr local_interfaces, | |
229 mojom::ClientProcessConnectionPtr client_process_connection, | |
230 const ConnectCallback& callback) override { | |
231 Identity target = target_ptr.To<Identity>(); | |
232 if (target.user_id() == mojom::kInheritUserID) | |
233 target.set_user_id(identity_.user_id()); | |
234 | |
235 if (!ValidateIdentity(target, callback)) | |
236 return; | |
237 if (!ValidateClientProcessConnection(&client_process_connection, target, | |
238 callback)) { | |
239 return; | |
240 } | |
241 if (!ValidateCapabilities(target, callback)) | |
242 return; | |
243 | |
244 scoped_ptr<ConnectParams> params(new ConnectParams); | |
245 params->set_source(identity_); | |
246 params->set_target(target); | |
247 params->set_remote_interfaces(std::move(remote_interfaces)); | |
248 params->set_local_interfaces(std::move(local_interfaces)); | |
249 params->set_client_process_connection(std::move(client_process_connection)); | |
250 params->set_connect_callback(callback); | |
251 shell_->Connect(std::move(params)); | |
252 } | |
253 | |
254 void Clone(mojom::ConnectorRequest request) override { | |
255 connectors_.AddBinding(this, std::move(request)); | |
256 } | |
257 | |
258 // mojom::PIDReceiver: | |
259 void SetPID(uint32_t pid) override { | |
260 PIDAvailable(pid); | |
261 } | |
262 | |
263 // InterfaceFactory<mojom::Shell>: | |
264 void Create(Connection* connection, | |
265 mojom::ShellRequest request) override { | |
266 shell_bindings_.AddBinding(this, std::move(request)); | |
267 } | |
268 | |
269 // mojom::Shell implementation: | |
270 void AddInstanceListener(mojom::InstanceListenerPtr listener) override { | |
271 // TODO(beng): this should only track the instances matching this user, and | |
272 // root. | |
273 shell_->AddInstanceListener(std::move(listener)); | |
274 } | |
275 | |
276 bool ValidateIdentity(const Identity& identity, | |
277 const ConnectCallback& callback) { | |
278 if (!IsValidName(identity.name())) { | |
279 LOG(ERROR) << "Error: invalid Name: " << identity.name(); | |
280 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | |
281 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
282 return false; | |
283 } | |
284 if (!base::IsValidGUID(identity.user_id())) { | |
285 LOG(ERROR) << "Error: invalid user_id: " << identity.user_id(); | |
286 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | |
287 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
288 return false; | |
289 } | |
290 return true; | |
291 } | |
292 | |
293 bool ValidateClientProcessConnection( | |
294 mojom::ClientProcessConnectionPtr* client_process_connection, | |
295 const Identity& target, | |
296 const ConnectCallback& callback) { | |
297 if (!client_process_connection->is_null()) { | |
298 if (!HasClass(capability_spec_, kCapabilityClass_ClientProcess)) { | |
299 LOG(ERROR) << "Instance: " << identity_.name() << " attempting " | |
300 << "to register an instance for a process it created for " | |
301 << "target: " << target.name() << " without the " | |
302 << "mojo:shell{client_process} capability class."; | |
303 callback.Run(mojom::ConnectResult::ACCESS_DENIED, | |
304 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
305 return false; | |
306 } | |
307 | |
308 if (!(*client_process_connection)->shell_client.is_valid() || | |
309 !(*client_process_connection)->pid_receiver_request.is_valid()) { | |
310 LOG(ERROR) << "Must supply both shell_client AND " | |
311 << "pid_receiver_request when sending " | |
312 << "client_process_connection."; | |
313 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | |
314 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
315 return false; | |
316 } | |
317 if (shell_->GetExistingInstance(target)) { | |
318 LOG(ERROR) << "Cannot client process matching existing identity:" | |
319 << "Name: " << target.name() << " User: " | |
320 << target.user_id() << " Instance: " << target.instance(); | |
321 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | |
322 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
323 return false; | |
324 } | |
325 } | |
326 return true; | |
327 } | |
328 | |
329 bool ValidateCapabilities(const Identity& target, | |
330 const ConnectCallback& callback) { | |
331 // TODO(beng): Need to do the following additional policy validation of | |
332 // whether this instance is allowed to connect using: | |
333 // - a non-null client_process_connection. | |
334 if (target.user_id() != identity_.user_id() && | |
335 target.user_id() != mojom::kRootUserID && | |
336 !HasClass(capability_spec_, kCapabilityClass_UserID)) { | |
337 LOG(ERROR) << "Instance: " << identity_.name() << " running as: " | |
338 << identity_.user_id() << " attempting to connect to: " | |
339 << target.name() << " as: " << target.user_id() << " without " | |
340 << " the mojo:shell{user_id} capability class."; | |
341 callback.Run(mojom::ConnectResult::ACCESS_DENIED, | |
342 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
343 return false; | |
344 } | |
345 if (!target.instance().empty() && | |
346 target.instance() != GetNamePath(target.name()) && | |
347 !HasClass(capability_spec_, kCapabilityClass_InstanceName)) { | |
348 LOG(ERROR) << "Instance: " << identity_.name() << " attempting to " | |
349 << "connect to " << target.name() << " using Instance name: " | |
350 << target.instance() << " without the " | |
351 << "mojo:shell{instance_name} capability class."; | |
352 callback.Run(mojom::ConnectResult::ACCESS_DENIED, | |
353 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
354 return false; | |
355 | |
356 } | |
357 | |
358 if (allow_any_application_ || | |
359 capability_spec_.required.find(target.name()) != | |
360 capability_spec_.required.end()) { | |
361 return true; | |
362 } | |
363 LOG(ERROR) << "Capabilities prevented connection from: " << | |
364 identity_.name() << " to: " << target.name(); | |
365 callback.Run(mojom::ConnectResult::ACCESS_DENIED, | |
366 mojom::kInheritUserID, mojom::kInvalidInstanceID); | |
367 return false; | |
368 } | |
369 | |
370 uint32_t GenerateUniqueID() const { | |
371 static uint32_t id = mojom::kInvalidInstanceID; | |
372 ++id; | |
373 CHECK_NE(mojom::kInvalidInstanceID, id); | |
374 return id; | |
375 } | |
376 | |
377 void PIDAvailable(base::ProcessId pid) { | |
378 pid_ = pid; | |
379 shell_->NotifyPIDAvailable(id_, pid_); | |
380 } | |
381 | |
382 void OnShellClientLost(base::WeakPtr<mojo::shell::Shell> shell) { | |
383 shell_client_.reset(); | |
384 OnConnectionLost(shell); | |
385 } | |
386 | |
387 void OnConnectionLost(base::WeakPtr<mojo::shell::Shell> shell) { | |
388 // Any time a Connector is lost or we lose the ShellClient connection, it | |
389 // may have been the last pipe using this Instance. If so, clean up. | |
390 if (shell && connectors_.empty() && !shell_client_) { | |
391 // Deletes |this|. | |
392 shell->OnInstanceError(this); | |
393 } | |
394 } | |
395 | |
396 void OnInitializeResponse(mojom::ConnectorRequest connector_request) { | |
397 if (connector_request.is_pending()) { | |
398 connectors_.AddBinding(this, std::move(connector_request)); | |
399 connectors_.set_connection_error_handler( | |
400 base::Bind(&Instance::OnConnectionLost, base::Unretained(this), | |
401 shell_->GetWeakPtr())); | |
402 } | |
403 } | |
404 | |
405 mojo::shell::Shell* const shell_; | |
406 | |
407 // An id that identifies this instance. Distinct from pid, as a single process | |
408 // may vend multiple application instances, and this object may exist before a | |
409 // process is launched. | |
410 const uint32_t id_; | |
411 const Identity identity_; | |
412 const CapabilitySpec capability_spec_; | |
413 const bool allow_any_application_; | |
414 mojom::ShellClientPtr shell_client_; | |
415 Binding<mojom::PIDReceiver> pid_receiver_binding_; | |
416 BindingSet<mojom::Connector> connectors_; | |
417 BindingSet<mojom::Shell> shell_bindings_; | |
418 NativeRunner* runner_ = nullptr; | |
419 base::ProcessId pid_ = base::kNullProcessId; | |
420 Instance* parent_ = nullptr; | |
421 std::set<Instance*> children_; | |
422 base::WeakPtrFactory<Instance> weak_factory_; | |
423 | |
424 DISALLOW_COPY_AND_ASSIGN(Instance); | |
425 }; | |
426 | |
427 // static | |
428 Shell::TestAPI::TestAPI(Shell* shell) : shell_(shell) {} | |
429 Shell::TestAPI::~TestAPI() {} | |
430 | |
431 bool Shell::TestAPI::HasRunningInstanceForName(const std::string& name) const { | |
432 for (const auto& entry : shell_->identity_to_instance_) { | |
433 if (entry.first.name() == name) | |
434 return true; | |
435 } | |
436 return false; | |
437 } | |
438 | |
439 //////////////////////////////////////////////////////////////////////////////// | |
440 // Shell, public: | |
441 | |
442 Shell::Shell(scoped_ptr<NativeRunnerFactory> native_runner_factory, | |
443 mojom::ShellClientPtr catalog) | |
444 : native_runner_factory_(std::move(native_runner_factory)), | |
445 weak_ptr_factory_(this) { | |
446 mojom::ShellClientPtr client; | |
447 mojom::ShellClientRequest request = GetProxy(&client); | |
448 Instance* instance = CreateInstance(Identity(), CreateShellIdentity(), | |
449 GetPermissiveCapabilities()); | |
450 instance->StartWithClient(std::move(client)); | |
451 singletons_.insert(kShellName); | |
452 shell_connection_.reset(new ShellConnection(this, std::move(request))); | |
453 | |
454 if (catalog) | |
455 InitCatalog(std::move(catalog)); | |
456 } | |
457 | |
458 Shell::~Shell() { | |
459 TerminateShellConnections(); | |
460 STLDeleteValues(&name_to_loader_); | |
461 for (auto& runner : native_runners_) | |
462 runner.reset(); | |
463 } | |
464 | |
465 void Shell::SetInstanceQuitCallback( | |
466 base::Callback<void(const Identity&)> callback) { | |
467 instance_quit_callback_ = callback; | |
468 } | |
469 | |
470 void Shell::Connect(scoped_ptr<ConnectParams> params) { | |
471 Connect(std::move(params), nullptr); | |
472 } | |
473 | |
474 mojom::ShellClientRequest Shell::InitInstanceForEmbedder( | |
475 const std::string& name) { | |
476 scoped_ptr<ConnectParams> params(new ConnectParams); | |
477 | |
478 Identity embedder_identity(name, mojom::kRootUserID); | |
479 params->set_source(embedder_identity); | |
480 params->set_target(embedder_identity); | |
481 | |
482 mojom::ShellClientPtr client; | |
483 mojom::ShellClientRequest request = GetProxy(&client); | |
484 Connect(std::move(params), std::move(client)); | |
485 | |
486 return request; | |
487 } | |
488 | |
489 void Shell::SetLoaderForName(scoped_ptr<Loader> loader, | |
490 const std::string& name) { | |
491 auto it = name_to_loader_.find(name); | |
492 if (it != name_to_loader_.end()) | |
493 delete it->second; | |
494 name_to_loader_[name] = loader.release(); | |
495 } | |
496 | |
497 //////////////////////////////////////////////////////////////////////////////// | |
498 // Shell, ShellClient implementation: | |
499 | |
500 bool Shell::AcceptConnection(Connection* connection) { | |
501 // The only interface we expose is mojom::Shell, and access to this interface | |
502 // is brokered by a policy specific to each caller, managed by the caller's | |
503 // instance. Here we look to see who's calling, and forward to the caller's | |
504 // instance to continue. | |
505 Instance* instance = nullptr; | |
506 for (const auto& entry : identity_to_instance_) { | |
507 if (entry.second->id() == connection->GetRemoteInstanceID()) { | |
508 instance = entry.second; | |
509 break; | |
510 } | |
511 } | |
512 DCHECK(instance); | |
513 return instance->AcceptConnection(connection); | |
514 } | |
515 | |
516 //////////////////////////////////////////////////////////////////////////////// | |
517 // Shell, private: | |
518 | |
519 void Shell::InitCatalog(mojom::ShellClientPtr catalog) { | |
520 Instance* instance = CreateInstance(CreateShellIdentity(), | |
521 CreateCatalogIdentity(), | |
522 CapabilitySpec()); | |
523 singletons_.insert(kCatalogName); | |
524 instance->StartWithClient(std::move(catalog)); | |
525 | |
526 // TODO(beng): this doesn't work anymore. | |
527 // Seed the catalog with manifest info for the shell & catalog. | |
528 mojo::shell::mojom::ShellResolverPtr resolver; | |
529 shell_connection_->connector()->ConnectToInterface(kCatalogName, &resolver); | |
530 resolver->ResolveMojoName(kCatalogName, base::Bind(&EmptyResolverCallback)); | |
531 resolver->ResolveMojoName(kShellName, base::Bind(&EmptyResolverCallback)); | |
532 } | |
533 | |
534 void Shell::TerminateShellConnections() { | |
535 Instance* instance = GetExistingInstance(CreateShellIdentity()); | |
536 DCHECK(instance); | |
537 OnInstanceError(instance); | |
538 } | |
539 | |
540 void Shell::OnInstanceError(Instance* instance) { | |
541 const Identity identity = instance->identity(); | |
542 // Remove the shell. | |
543 auto it = identity_to_instance_.find(identity); | |
544 DCHECK(it != identity_to_instance_.end()); | |
545 int id = instance->id(); | |
546 delete it->second; | |
547 identity_to_instance_.erase(it); | |
548 instance_listeners_.ForAllPtrs([this, id](mojom::InstanceListener* listener) { | |
549 listener->InstanceDestroyed(id); | |
550 }); | |
551 if (!instance_quit_callback_.is_null()) | |
552 instance_quit_callback_.Run(identity); | |
553 } | |
554 | |
555 void Shell::Connect(scoped_ptr<ConnectParams> params, | |
556 mojom::ShellClientPtr client) { | |
557 TRACE_EVENT_INSTANT1("mojo_shell", "Shell::Connect", | |
558 TRACE_EVENT_SCOPE_THREAD, "original_name", | |
559 params->target().name()); | |
560 DCHECK(IsValidName(params->target().name())); | |
561 DCHECK(base::IsValidGUID(params->target().user_id())); | |
562 DCHECK_NE(mojom::kInheritUserID, params->target().user_id()); | |
563 DCHECK(!client.is_bound() || !identity_to_instance_.count(params->target())); | |
564 | |
565 // Connect to an existing matching instance, if possible. | |
566 if (!client.is_bound() && ConnectToExistingInstance(¶ms)) | |
567 return; | |
568 | |
569 // The catalog needs to see the source identity as that of the originating | |
570 // app so it loads the correct store. Since the catalog is itself run as root | |
571 // when this re-enters Connect() it'll be handled by | |
572 // ConnectToExistingInstance(). | |
573 mojom::ShellResolverPtr resolver; | |
574 ConnectToInterface(this, Identity(kShellName, params->target().user_id()), | |
575 CreateCatalogIdentity(), &resolver); | |
576 | |
577 std::string name = params->target().name(); | |
578 mojom::ShellResolver* resolver_raw = resolver.get(); | |
579 resolver_raw->ResolveMojoName( | |
580 name, | |
581 base::Bind(&Shell::OnGotResolvedName, weak_ptr_factory_.GetWeakPtr(), | |
582 base::Passed(std::move(resolver)), base::Passed(¶ms), | |
583 base::Passed(&client))); | |
584 } | |
585 | |
586 Shell::Instance* Shell::GetExistingInstance(const Identity& identity) const { | |
587 const auto& it = identity_to_instance_.find(identity); | |
588 Instance* instance = it != identity_to_instance_.end() ? it->second : nullptr; | |
589 if (instance) | |
590 return instance; | |
591 | |
592 if (singletons_.find(identity.name()) != singletons_.end()) { | |
593 for (auto entry : identity_to_instance_) { | |
594 if (entry.first.name() == identity.name() && | |
595 entry.first.instance() == identity.instance()) { | |
596 return entry.second; | |
597 } | |
598 } | |
599 } | |
600 return nullptr; | |
601 } | |
602 | |
603 void Shell::NotifyPIDAvailable(uint32_t id, base::ProcessId pid) { | |
604 instance_listeners_.ForAllPtrs([id, pid](mojom::InstanceListener* listener) { | |
605 listener->InstancePIDAvailable(id, pid); | |
606 }); | |
607 } | |
608 | |
609 bool Shell::ConnectToExistingInstance(scoped_ptr<ConnectParams>* params) { | |
610 Instance* instance = GetExistingInstance((*params)->target()); | |
611 if (instance) | |
612 instance->ConnectToClient(std::move(*params)); | |
613 return !!instance; | |
614 } | |
615 | |
616 Shell::Instance* Shell::CreateInstance(const Identity& source, | |
617 const Identity& target, | |
618 const CapabilitySpec& spec) { | |
619 CHECK(target.user_id() != mojom::kInheritUserID); | |
620 Instance* instance = new Instance(this, target, spec); | |
621 DCHECK(identity_to_instance_.find(target) == | |
622 identity_to_instance_.end()); | |
623 Instance* source_instance = GetExistingInstance(source); | |
624 if (source_instance) | |
625 source_instance->AddChild(instance); | |
626 identity_to_instance_[target] = instance; | |
627 mojom::InstanceInfoPtr info = instance->CreateInstanceInfo(); | |
628 instance_listeners_.ForAllPtrs( | |
629 [this, &info](mojom::InstanceListener* listener) { | |
630 listener->InstanceCreated(info.Clone()); | |
631 }); | |
632 return instance; | |
633 } | |
634 | |
635 void Shell::AddInstanceListener(mojom::InstanceListenerPtr listener) { | |
636 // TODO(beng): filter instances provided by those visible to this client. | |
637 Array<mojom::InstanceInfoPtr> instances; | |
638 for (auto& instance : identity_to_instance_) | |
639 instances.push_back(instance.second->CreateInstanceInfo()); | |
640 listener->SetExistingInstances(std::move(instances)); | |
641 | |
642 instance_listeners_.AddPtr(std::move(listener)); | |
643 } | |
644 | |
645 void Shell::CreateShellClientWithFactory(const Identity& source, | |
646 const Identity& shell_client_factory, | |
647 const std::string& name, | |
648 mojom::ShellClientRequest request) { | |
649 mojom::ShellClientFactory* factory = | |
650 GetShellClientFactory(shell_client_factory, source); | |
651 factory->CreateShellClient(std::move(request), name); | |
652 } | |
653 | |
654 mojom::ShellClientFactory* Shell::GetShellClientFactory( | |
655 const Identity& shell_client_factory_identity, | |
656 const Identity& source_identity) { | |
657 auto it = shell_client_factories_.find(shell_client_factory_identity); | |
658 if (it != shell_client_factories_.end()) | |
659 return it->second.get(); | |
660 | |
661 mojom::ShellClientFactoryPtr factory; | |
662 ConnectToInterface(this, source_identity, shell_client_factory_identity, | |
663 &factory); | |
664 mojom::ShellClientFactory* factory_interface = factory.get(); | |
665 factory.set_connection_error_handler( | |
666 base::Bind(&Shell::OnShellClientFactoryLost, | |
667 weak_ptr_factory_.GetWeakPtr(), | |
668 shell_client_factory_identity)); | |
669 shell_client_factories_[shell_client_factory_identity] = std::move(factory); | |
670 return factory_interface; | |
671 } | |
672 | |
673 void Shell::OnShellClientFactoryLost(const Identity& which) { | |
674 // Remove the mapping. | |
675 auto it = shell_client_factories_.find(which); | |
676 DCHECK(it != shell_client_factories_.end()); | |
677 shell_client_factories_.erase(it); | |
678 } | |
679 | |
680 void Shell::OnGotResolvedName(mojom::ShellResolverPtr resolver, | |
681 scoped_ptr<ConnectParams> params, | |
682 mojom::ShellClientPtr client, | |
683 mojom::ResolveResultPtr result) { | |
684 std::string instance_name = params->target().instance(); | |
685 if (instance_name == GetNamePath(params->target().name()) && | |
686 result->qualifier != GetNamePath(result->resolved_name)) { | |
687 instance_name = result->qualifier; | |
688 } | |
689 Identity target(params->target().name(), params->target().user_id(), | |
690 instance_name); | |
691 params->set_target(target); | |
692 | |
693 // It's possible that when this manifest request was issued, another one was | |
694 // already in-progress and completed by the time this one did, and so the | |
695 // requested application may already be running. | |
696 if (ConnectToExistingInstance(¶ms)) | |
697 return; | |
698 | |
699 Identity source = params->source(); | |
700 // |capabilities_ptr| can be null when there is no manifest, e.g. for URL | |
701 // types not resolvable by the resolver. | |
702 CapabilitySpec capabilities = GetPermissiveCapabilities(); | |
703 if (!result->capabilities.is_null()) | |
704 capabilities = result->capabilities.To<CapabilitySpec>(); | |
705 | |
706 // Clients that request "all_users" class from the shell are allowed to | |
707 // field connection requests from any user. They also run with a synthetic | |
708 // user id generated here. The user id provided via Connect() is ignored. | |
709 // Additionally apps with the "all_users" class are not tied to the lifetime | |
710 // of the app that connected to them, instead they are owned by the shell. | |
711 Identity source_identity_for_creation; | |
712 if (HasClass(capabilities, kCapabilityClass_AllUsers)) { | |
713 singletons_.insert(target.name()); | |
714 target.set_user_id(base::GenerateGUID()); | |
715 source_identity_for_creation = CreateShellIdentity(); | |
716 } else { | |
717 source_identity_for_creation = params->source(); | |
718 } | |
719 | |
720 mojom::ClientProcessConnectionPtr client_process_connection = | |
721 params->TakeClientProcessConnection(); | |
722 Instance* instance = CreateInstance(source_identity_for_creation, | |
723 target, capabilities); | |
724 | |
725 // Below are various paths through which a new Instance can be bound to a | |
726 // ShellClient proxy. | |
727 if (client.is_bound()) { | |
728 // If a ShellClientPtr was provided, there's no more work to do: someone | |
729 // is already holding a corresponding ShellClientRequest. | |
730 instance->StartWithClient(std::move(client)); | |
731 } else if (!client_process_connection.is_null()) { | |
732 // Likewise if a ClientProcessConnection was given via Connect(), it | |
733 // provides the ShellClient proxy to use. | |
734 instance->StartWithClientProcessConnection( | |
735 std::move(client_process_connection)); | |
736 } else { | |
737 // Otherwise we create a new ShellClient pipe. | |
738 mojom::ShellClientRequest request = GetProxy(&client); | |
739 if (LoadWithLoader(target, &request)) { | |
740 instance->StartWithClient(std::move(client)); | |
741 } else { | |
742 CHECK(!result->package_url.is_null() && !result->capabilities.is_null()); | |
743 | |
744 if (target.name() != result->resolved_name) { | |
745 instance->StartWithClient(std::move(client)); | |
746 Identity factory(result->resolved_name, target.user_id(), | |
747 instance_name); | |
748 CreateShellClientWithFactory(source, factory, target.name(), | |
749 std::move(request)); | |
750 } else { | |
751 instance->StartWithFilePath( | |
752 util::UrlToFilePath(result->package_url.To<GURL>())); | |
753 } | |
754 } | |
755 } | |
756 | |
757 // Now that the instance has a ShellClient, we can connect to it. | |
758 instance->ConnectToClient(std::move(params)); | |
759 } | |
760 | |
761 bool Shell::LoadWithLoader(const Identity& target, | |
762 mojom::ShellClientRequest* request) { | |
763 Loader* loader = GetLoaderForName(target.name()); | |
764 if (!loader) | |
765 return false; | |
766 loader->Load(target.name(), std::move(*request)); | |
767 return true; | |
768 } | |
769 | |
770 Loader* Shell::GetLoaderForName(const std::string& name) { | |
771 auto name_it = name_to_loader_.find(name); | |
772 if (name_it != name_to_loader_.end()) | |
773 return name_it->second; | |
774 return default_loader_.get(); | |
775 } | |
776 | |
777 base::WeakPtr<Shell> Shell::GetWeakPtr() { | |
778 return weak_ptr_factory_.GetWeakPtr(); | |
779 } | |
780 | |
781 void Shell::CleanupRunner(NativeRunner* runner) { | |
782 for (auto it = native_runners_.begin(); it != native_runners_.end(); ++it) { | |
783 if (it->get() == runner) { | |
784 native_runners_.erase(it); | |
785 return; | |
786 } | |
787 } | |
788 } | |
789 | |
790 } // namespace shell | |
791 } // namespace mojo | |
OLD | NEW |