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

Side by Side Diff: mojo/shell/shell.cc

Issue 1877753003: Move mojo\shell to services\shell (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@62scan
Patch Set: . Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « mojo/shell/shell.h ('k') | mojo/shell/standalone/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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(&params))
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(&params),
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(&params))
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
OLDNEW
« no previous file with comments | « mojo/shell/shell.h ('k') | mojo/shell/standalone/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698