| 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 |