summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/mus/sysui_application.cc5
-rw-r--r--ash/mus/sysui_application.h1
-rw-r--r--components/resource_provider/manifest.json7
-rw-r--r--mash/browser_driver/browser_driver_application_delegate.cc14
-rw-r--r--mash/browser_driver/browser_driver_application_delegate.h3
-rw-r--r--mash/example/window_type_launcher/BUILD.gn1
-rw-r--r--mash/example/window_type_launcher/window_type_launcher.cc18
-rw-r--r--mash/example/window_type_launcher/window_type_launcher.h1
-rw-r--r--mash/init/init.cc34
-rw-r--r--mash/init/init.h5
-rw-r--r--mash/login/login.cc4
-rw-r--r--mash/login/public/interfaces/login.mojom1
-rw-r--r--mash/login/ui.cc1
-rw-r--r--mash/quick_launch/quick_launch_application.cc5
-rw-r--r--mash/quick_launch/quick_launch_application.h1
-rw-r--r--mash/shell/BUILD.gn1
-rw-r--r--mash/shell/public/interfaces/shell.mojom2
-rw-r--r--mash/shell/shell_application_delegate.cc18
-rw-r--r--mash/shell/shell_application_delegate.h2
-rw-r--r--mojo/services/catalog/manifest.json7
-rw-r--r--mojo/services/tracing/manifest.json7
-rw-r--r--mojo/shell/manifest.json6
-rw-r--r--mojo/shell/shell.cc97
-rw-r--r--mojo/shell/shell.h16
-rw-r--r--mojo/shell/tests/connect/BUILD.gn19
-rw-r--r--mojo/shell/tests/connect/connect_test_app_manifest.json2
-rw-r--r--mojo/shell/tests/connect/connect_test_singleton_app.cc36
-rw-r--r--mojo/shell/tests/connect/connect_test_singleton_app_manifest.json10
-rw-r--r--mojo/shell/tests/connect/connect_unittest.cc19
-rw-r--r--mojo/shell/tests/lifecycle/BUILD.gn22
-rw-r--r--mojo/shell/tests/lifecycle/app_client.cc4
-rw-r--r--mojo/shell/tests/lifecycle/app_client.h1
-rw-r--r--mojo/shell/tests/lifecycle/lifecycle_unittest.cc29
-rw-r--r--mojo/shell/tests/lifecycle/lifecycle_unittest.mojom9
-rw-r--r--mojo/shell/tests/lifecycle/parent.cc80
-rw-r--r--mojo/shell/tests/lifecycle/parent_manifest.json12
36 files changed, 426 insertions, 74 deletions
diff --git a/ash/mus/sysui_application.cc b/ash/mus/sysui_application.cc
index 16585a3..15a5186 100644
--- a/ash/mus/sysui_application.cc
+++ b/ash/mus/sysui_application.cc
@@ -16,6 +16,7 @@
#include "ash/shell_window_ids.h"
#include "base/bind.h"
#include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/threading/sequenced_worker_pool.h"
#include "components/mus/public/cpp/property_type_converters.h"
@@ -288,5 +289,9 @@ bool SysUIApplication::AcceptConnection(mojo::Connection* connection) {
return true;
}
+void SysUIApplication::ShellConnectionLost() {
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
} // namespace sysui
} // namespace ash
diff --git a/ash/mus/sysui_application.h b/ash/mus/sysui_application.h
index dba0f5e..2535c5e0 100644
--- a/ash/mus/sysui_application.h
+++ b/ash/mus/sysui_application.h
@@ -26,6 +26,7 @@ class SysUIApplication : public mojo::ShellClient {
const mojo::Identity& identity,
uint32_t id) override;
bool AcceptConnection(mojo::Connection* connection) override;
+ void ShellConnectionLost() override;
mojo::TracingImpl tracing_;
scoped_ptr<AshInit> ash_init_;
diff --git a/components/resource_provider/manifest.json b/components/resource_provider/manifest.json
index 56499af..d02e9b3 100644
--- a/components/resource_provider/manifest.json
+++ b/components/resource_provider/manifest.json
@@ -1,5 +1,10 @@
{
+ "manifest_version": 1,
"name": "mojo:resource_provider",
"display_name": "Resource Provider",
- "capabilities": { "*": [ "*" ] }
+ "capabilities": {
+ "required": {
+ "mojo:shell": { "classes": [ "all_users" ] }
+ }
+ }
}
diff --git a/mash/browser_driver/browser_driver_application_delegate.cc b/mash/browser_driver/browser_driver_application_delegate.cc
index 64ee8e4..d30225e 100644
--- a/mash/browser_driver/browser_driver_application_delegate.cc
+++ b/mash/browser_driver/browser_driver_application_delegate.cc
@@ -7,6 +7,7 @@
#include <stdint.h>
#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
#include "components/mus/public/cpp/event_matcher.h"
#include "mojo/shell/public/cpp/connection.h"
#include "mojo/shell/public/cpp/connector.h"
@@ -42,11 +43,14 @@ void AssertTrue(bool success) {
DCHECK(success);
}
+void DoNothing() {}
+
} // namespace
BrowserDriverApplicationDelegate::BrowserDriverApplicationDelegate()
: connector_(nullptr),
- binding_(this) {}
+ binding_(this),
+ weak_factory_(this) {}
BrowserDriverApplicationDelegate::~BrowserDriverApplicationDelegate() {}
@@ -63,6 +67,12 @@ bool BrowserDriverApplicationDelegate::AcceptConnection(
return true;
}
+void BrowserDriverApplicationDelegate::ShellConnectionLost() {
+ // Prevent the code in AddAccelerators() from keeping this app alive.
+ binding_.set_connection_error_handler(base::Bind(&DoNothing));
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
void BrowserDriverApplicationDelegate::OnAccelerator(
uint32_t id, mus::mojom::EventPtr event) {
switch (static_cast<Accelerator>(id)) {
@@ -92,7 +102,7 @@ void BrowserDriverApplicationDelegate::AddAccelerators() {
// to re-add our accelerators when the window manager comes back up.
binding_.set_connection_error_handler(
base::Bind(&BrowserDriverApplicationDelegate::AddAccelerators,
- base::Unretained(this)));
+ weak_factory_.GetWeakPtr()));
for (const AcceleratorSpec& spec : g_spec) {
registrar->AddAccelerator(
diff --git a/mash/browser_driver/browser_driver_application_delegate.h b/mash/browser_driver/browser_driver_application_delegate.h
index 2d722f4..f517a76 100644
--- a/mash/browser_driver/browser_driver_application_delegate.h
+++ b/mash/browser_driver/browser_driver_application_delegate.h
@@ -12,6 +12,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "components/mus/public/interfaces/accelerator_registrar.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/shell/public/cpp/shell_client.h"
@@ -30,6 +31,7 @@ class BrowserDriverApplicationDelegate : public mojo::ShellClient,
void Initialize(mojo::Connector* connector, const mojo::Identity& identity,
uint32_t id) override;
bool AcceptConnection(mojo::Connection* connection) override;
+ void ShellConnectionLost() override;
// mus::mojom::AcceleratorHandler:
void OnAccelerator(uint32_t id, mus::mojom::EventPtr event) override;
@@ -38,6 +40,7 @@ class BrowserDriverApplicationDelegate : public mojo::ShellClient,
mojo::Connector* connector_;
mojo::Binding<mus::mojom::AcceleratorHandler> binding_;
+ base::WeakPtrFactory<BrowserDriverApplicationDelegate> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(BrowserDriverApplicationDelegate);
};
diff --git a/mash/example/window_type_launcher/BUILD.gn b/mash/example/window_type_launcher/BUILD.gn
index 245d93d..7018487 100644
--- a/mash/example/window_type_launcher/BUILD.gn
+++ b/mash/example/window_type_launcher/BUILD.gn
@@ -24,7 +24,6 @@ executable("window_type_launcher") {
"//base:base_static",
"//build/config/sanitizers:deps",
"//components/mus/public/interfaces",
- "//mash/login/public/interfaces",
"//mash/shell/public/interfaces",
"//mojo/common:common_base",
"//mojo/converters/geometry",
diff --git a/mash/example/window_type_launcher/window_type_launcher.cc b/mash/example/window_type_launcher/window_type_launcher.cc
index b469395..1d56a87 100644
--- a/mash/example/window_type_launcher/window_type_launcher.cc
+++ b/mash/example/window_type_launcher/window_type_launcher.cc
@@ -5,8 +5,8 @@
#include "mash/example/window_type_launcher/window_type_launcher.h"
#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
-#include "mash/login/public/interfaces/login.mojom.h"
#include "mash/shell/public/interfaces/shell.mojom.h"
#include "mojo/converters/geometry/geometry_type_converters.h"
#include "mojo/shell/public/cpp/connection.h"
@@ -283,13 +283,13 @@ class WindowTypeLauncherView : public views::WidgetDelegateView,
connector_->ConnectToInterface("mojo:mash_shell", &shell);
shell->LockScreen();
} else if (sender == logout_button_) {
- mash::login::mojom::LoginPtr login;
- connector_->ConnectToInterface("mojo:login", &login);
- login->Logout();
+ mash::shell::mojom::ShellPtr shell;
+ connector_->ConnectToInterface("mojo:mash_shell", &shell);
+ shell->Logout();
} else if (sender == switch_user_button_) {
- mash::login::mojom::LoginPtr login;
- connector_->ConnectToInterface("mojo:login", &login);
- login->SwitchUser();
+ mash::shell::mojom::ShellPtr shell;
+ connector_->ConnectToInterface("mojo:mash_shell", &shell);
+ shell->SwitchUser();
} else if (sender == widgets_button_) {
NOTIMPLEMENTED();
}
@@ -383,3 +383,7 @@ void WindowTypeLauncher::Initialize(mojo::Connector* connector,
widget->Init(params);
widget->Show();
}
+
+void WindowTypeLauncher::ShellConnectionLost() {
+ base::MessageLoop::current()->QuitWhenIdle();
+}
diff --git a/mash/example/window_type_launcher/window_type_launcher.h b/mash/example/window_type_launcher/window_type_launcher.h
index dbbf40f..4d5a266 100644
--- a/mash/example/window_type_launcher/window_type_launcher.h
+++ b/mash/example/window_type_launcher/window_type_launcher.h
@@ -22,6 +22,7 @@ class WindowTypeLauncher : public mojo::ShellClient {
// mojo::ShellClient:
void Initialize(mojo::Connector* connector, const mojo::Identity& identity,
uint32_t id) override;
+ void ShellConnectionLost() override;
scoped_ptr<views::AuraInit> aura_init_;
diff --git a/mash/init/init.cc b/mash/init/init.cc
index ffe16a6..aab8448 100644
--- a/mash/init/init.cc
+++ b/mash/init/init.cc
@@ -14,7 +14,8 @@
namespace mash {
namespace init {
-Init::Init() : connector_(nullptr), login_user_id_(base::GenerateGUID()) {}
+Init::Init()
+ : connector_(nullptr) {}
Init::~Init() {}
void Init::Initialize(mojo::Connector* connector,
@@ -22,18 +23,23 @@ void Init::Initialize(mojo::Connector* connector,
uint32_t id) {
connector_ = connector;
connector_->Connect("mojo:mus");
+ StartTracing();
+ StartResourceProvider();
StartLogin();
}
void Init::StartService(const mojo::String& name,
const mojo::String& user_id) {
- DCHECK(user_services_.find(user_id) == user_services_.end());
- mojo::Connector::ConnectParams params(mojo::Identity(name, user_id));
- user_services_[user_id] = connector_->Connect(&params);
+ if (user_services_.find(user_id) == user_services_.end()) {
+ mojo::Connector::ConnectParams params(mojo::Identity(name, user_id));
+ scoped_ptr<mojo::Connection> connection = connector_->Connect(&params);
+ connection->SetConnectionLostClosure(
+ base::Bind(&Init::UserServiceQuit, base::Unretained(this), user_id));
+ user_services_[user_id] = std::move(connection);
+ }
}
void Init::StopServicesForUser(const mojo::String& user_id) {
- // TODO(beng): Make shell cascade shutdown of services.
auto it = user_services_.find(user_id);
if (it != user_services_.end())
user_services_.erase(it);
@@ -43,10 +49,22 @@ void Init::Create(mojo::Connection* connection, mojom::InitRequest request) {
init_bindings_.AddBinding(this, std::move(request));
}
+void Init::UserServiceQuit(const std::string& user_id) {
+ auto it = user_services_.find(user_id);
+ DCHECK(it != user_services_.end());
+ user_services_.erase(it);
+}
+
+void Init::StartTracing() {
+ connector_->Connect("mojo:tracing");
+}
+
+void Init::StartResourceProvider() {
+ connector_->Connect("mojo:resource_provider");
+}
+
void Init::StartLogin() {
- mojo::Connector::ConnectParams params(
- mojo::Identity("mojo:login", login_user_id_));
- login_connection_ = connector_->Connect(&params);
+ login_connection_ = connector_->Connect("mojo:login");
login_connection_->AddInterface<mojom::Init>(this);
login_connection_->SetConnectionLostClosure(
base::Bind(&Init::StartLogin, base::Unretained(this)));
diff --git a/mash/init/init.h b/mash/init/init.h
index b650f50..452b089 100644
--- a/mash/init/init.h
+++ b/mash/init/init.h
@@ -43,13 +43,16 @@ class Init : public mojo::ShellClient,
const mojo::String& user_id) override;
void StopServicesForUser(const mojo::String& user_id) override;
+ void UserServiceQuit(const std::string& user_id);
+
+ void StartTracing();
+ void StartResourceProvider();
void StartLogin();
mojo::Connector* connector_;
scoped_ptr<mojo::Connection> login_connection_;
mojo::BindingSet<mojom::Init> init_bindings_;
std::map<std::string, scoped_ptr<mojo::Connection>> user_services_;
- const std::string login_user_id_;
DISALLOW_COPY_AND_ASSIGN(Init);
};
diff --git a/mash/login/login.cc b/mash/login/login.cc
index 05edab5..1ae4e77 100644
--- a/mash/login/login.cc
+++ b/mash/login/login.cc
@@ -32,10 +32,6 @@ class Login : public mojom::Login {
void ShowLoginUI() override {
UI::Show(connector_, controller_);
}
- void Logout() override {
- controller_->init()->StopServicesForUser(user_id_);
- UI::Show(connector_, controller_);
- }
void SwitchUser() override {
UI::Show(connector_, controller_);
}
diff --git a/mash/login/public/interfaces/login.mojom b/mash/login/public/interfaces/login.mojom
index 8ddf08e..5a14cfa7 100644
--- a/mash/login/public/interfaces/login.mojom
+++ b/mash/login/public/interfaces/login.mojom
@@ -6,6 +6,5 @@ module mash.login.mojom;
interface Login {
ShowLoginUI();
- Logout();
SwitchUser();
};
diff --git a/mash/login/ui.cc b/mash/login/ui.cc
index 4a36f56..b2f1770 100644
--- a/mash/login/ui.cc
+++ b/mash/login/ui.cc
@@ -79,6 +79,7 @@ UI::~UI() {
// Prevent the window manager from restarting during graceful shutdown.
window_manager_connection_->SetConnectionLostClosure(base::Closure());
is_showing_ = false;
+ // TODO(beng): we should be terminating this app at this point.
}
views::View* UI::GetContentsView() { return this; }
diff --git a/mash/quick_launch/quick_launch_application.cc b/mash/quick_launch/quick_launch_application.cc
index c4887a4..0f505a8 100644
--- a/mash/quick_launch/quick_launch_application.cc
+++ b/mash/quick_launch/quick_launch_application.cc
@@ -5,6 +5,7 @@
#include "mash/quick_launch/quick_launch_application.h"
#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "mojo/public/c/system/main.h"
@@ -105,5 +106,9 @@ bool QuickLaunchApplication::AcceptConnection(mojo::Connection* connection) {
return true;
}
+void QuickLaunchApplication::ShellConnectionLost() {
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
} // namespace quick_launch
} // namespace mash
diff --git a/mash/quick_launch/quick_launch_application.h b/mash/quick_launch/quick_launch_application.h
index 130a1dd..93d72b2 100644
--- a/mash/quick_launch/quick_launch_application.h
+++ b/mash/quick_launch/quick_launch_application.h
@@ -28,6 +28,7 @@ class QuickLaunchApplication : public mojo::ShellClient {
const mojo::Identity& identity,
uint32_t id) override;
bool AcceptConnection(mojo::Connection* connection) override;
+ void ShellConnectionLost() override;
mojo::TracingImpl tracing_;
scoped_ptr<views::AuraInit> aura_init_;
diff --git a/mash/shell/BUILD.gn b/mash/shell/BUILD.gn
index 51b693d..ffe637b 100644
--- a/mash/shell/BUILD.gn
+++ b/mash/shell/BUILD.gn
@@ -16,6 +16,7 @@ source_set("lib") {
deps = [
"//base",
+ "//mash/login/public/interfaces",
"//mash/shell/public/interfaces",
"//mojo/common",
"//mojo/public/cpp/bindings",
diff --git a/mash/shell/public/interfaces/shell.mojom b/mash/shell/public/interfaces/shell.mojom
index faf0e82..77d1e80 100644
--- a/mash/shell/public/interfaces/shell.mojom
+++ b/mash/shell/public/interfaces/shell.mojom
@@ -11,6 +11,8 @@ interface ScreenlockStateListener {
};
interface Shell {
+ Logout();
+ SwitchUser();
LockScreen();
UnlockScreen();
AddScreenlockStateListener(ScreenlockStateListener listener);
diff --git a/mash/shell/shell_application_delegate.cc b/mash/shell/shell_application_delegate.cc
index 8c217bc..408ec7f 100644
--- a/mash/shell/shell_application_delegate.cc
+++ b/mash/shell/shell_application_delegate.cc
@@ -6,6 +6,8 @@
#include "base/bind.h"
#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "mash/login/public/interfaces/login.mojom.h"
#include "mojo/shell/public/cpp/connection.h"
#include "mojo/shell/public/cpp/connector.h"
@@ -32,6 +34,22 @@ bool ShellApplicationDelegate::AcceptConnection(mojo::Connection* connection) {
return true;
}
+void ShellApplicationDelegate::Logout() {
+ // TODO(beng): Notify connected listeners that login is happening, potentially
+ // give them the option to stop it.
+ mash::login::mojom::LoginPtr login;
+ connector_->ConnectToInterface("mojo:login", &login);
+ login->ShowLoginUI();
+ // This kills the user environment.
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void ShellApplicationDelegate::SwitchUser() {
+ mash::login::mojom::LoginPtr login;
+ connector_->ConnectToInterface("mojo:login", &login);
+ login->SwitchUser();
+}
+
void ShellApplicationDelegate::AddScreenlockStateListener(
mojom::ScreenlockStateListenerPtr listener) {
listener->ScreenlockStateChanged(screen_locked_);
diff --git a/mash/shell/shell_application_delegate.h b/mash/shell/shell_application_delegate.h
index ba692e87..98c4404 100644
--- a/mash/shell/shell_application_delegate.h
+++ b/mash/shell/shell_application_delegate.h
@@ -39,6 +39,8 @@ class ShellApplicationDelegate
bool AcceptConnection(mojo::Connection* connection) override;
// mash::shell::mojom::Shell:
+ void Logout() override;
+ void SwitchUser() override;
void AddScreenlockStateListener(
mojom::ScreenlockStateListenerPtr listener) override;
void LockScreen() override;
diff --git a/mojo/services/catalog/manifest.json b/mojo/services/catalog/manifest.json
index 0fa970d..770cc26 100644
--- a/mojo/services/catalog/manifest.json
+++ b/mojo/services/catalog/manifest.json
@@ -1,5 +1,10 @@
{
+ "manifest_version": 1,
"name": "mojo:catalog",
"display_name": "Catalog",
- "capabilities": {}
+ "capabilities": {
+ "required": {
+ "mojo:shell": { "classes": [ "all_users" ] }
+ }
+ }
}
diff --git a/mojo/services/tracing/manifest.json b/mojo/services/tracing/manifest.json
index 30d9371..adbdc58 100644
--- a/mojo/services/tracing/manifest.json
+++ b/mojo/services/tracing/manifest.json
@@ -1,5 +1,10 @@
{
+ "manifest_version": 1,
"name": "mojo:tracing",
"display_name": "Tracing Collector",
- "capabilities": {}
+ "capabilities": {
+ "required": {
+ "mojo:shell": { "classes": [ "all_users" ] }
+ }
+ }
}
diff --git a/mojo/shell/manifest.json b/mojo/shell/manifest.json
index 7d006e6..24a6f77 100644
--- a/mojo/shell/manifest.json
+++ b/mojo/shell/manifest.json
@@ -6,7 +6,11 @@
"provided": {
"user_id": [ ],
"client_process": [ ],
- "instance_name": [ ]
+ "instance_name": [ ],
+ "all_users": [ ]
+ },
+ "required": {
+ "mojo:shell": { "classes": [ "all_users" ] }
}
}
}
diff --git a/mojo/shell/shell.cc b/mojo/shell/shell.cc
index 4cefbc3..1fb16b6 100644
--- a/mojo/shell/shell.cc
+++ b/mojo/shell/shell.cc
@@ -129,28 +129,25 @@ class Shell::Instance : public mojom::Connector,
DCHECK_NE(mojom::kInvalidInstanceID, id_);
}
- ~Instance() override {}
-
- void OnShellClientLost() {
- shell_client_.reset();
- OnConnectionLost();
+ ~Instance() override {
+ if (parent_)
+ parent_->RemoveChild(this);
+ // |children_| will be modified during destruction.
+ std::set<Instance*> children = children_;
+ for (auto child : children)
+ shell_->OnInstanceError(child);
}
- void OnConnectionLost() {
- // Any time a Connector is lost or we lose the ShellClient connection, it
- // may have been the last pipe using this Instance. If so, clean up.
- if (connectors_.empty() && !shell_client_) {
- // Deletes |this|.
- shell_->OnInstanceError(this);
- }
+ Instance* parent() { return parent_; }
+ void AddChild(Instance* child) {
+ children_.insert(child);
+ child->parent_ = this;
}
-
- void OnInitializeResponse(mojom::ConnectorRequest connector_request) {
- if (connector_request.is_pending()) {
- connectors_.AddBinding(this, std::move(connector_request));
- connectors_.set_connection_error_handler(
- base::Bind(&Instance::OnConnectionLost, base::Unretained(this)));
- }
+ void RemoveChild(Instance* child) {
+ auto it = children_.find(child);
+ DCHECK(it != children_.end());
+ children_.erase(it);
+ child->parent_ = nullptr;
}
void ConnectToClient(scoped_ptr<ConnectParams> params) {
@@ -176,7 +173,8 @@ class Shell::Instance : public mojom::Connector,
CHECK(!shell_client_);
shell_client_ = std::move(client);
shell_client_.set_connection_error_handler(
- base::Bind(&Instance::OnShellClientLost, base::Unretained(this)));
+ base::Bind(&Instance::OnShellClientLost, base::Unretained(this),
+ shell_->GetWeakPtr()));
shell_client_->Initialize(mojom::Identity::From(identity_), id_,
base::Bind(&Instance::OnInitializeResponse,
base::Unretained(this)));
@@ -384,6 +382,29 @@ class Shell::Instance : public mojom::Connector,
shell_->NotifyPIDAvailable(id_, pid_);
}
+ void OnShellClientLost(base::WeakPtr<mojo::shell::Shell> shell) {
+ shell_client_.reset();
+ OnConnectionLost(shell);
+ }
+
+ void OnConnectionLost(base::WeakPtr<mojo::shell::Shell> shell) {
+ // Any time a Connector is lost or we lose the ShellClient connection, it
+ // may have been the last pipe using this Instance. If so, clean up.
+ if (shell && connectors_.empty() && !shell_client_) {
+ // Deletes |this|.
+ shell->OnInstanceError(this);
+ }
+ }
+
+ void OnInitializeResponse(mojom::ConnectorRequest connector_request) {
+ if (connector_request.is_pending()) {
+ connectors_.AddBinding(this, std::move(connector_request));
+ connectors_.set_connection_error_handler(
+ base::Bind(&Instance::OnConnectionLost, base::Unretained(this),
+ shell_->GetWeakPtr()));
+ }
+ }
+
mojo::shell::Shell* const shell_;
// An id that identifies this instance. Distinct from pid, as a single process
@@ -399,6 +420,8 @@ class Shell::Instance : public mojom::Connector,
BindingSet<mojom::Shell> shell_bindings_;
NativeRunner* runner_ = nullptr;
base::ProcessId pid_ = base::kNullProcessId;
+ Instance* parent_ = nullptr;
+ std::set<Instance*> children_;
base::WeakPtrFactory<Instance> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Instance);
@@ -425,9 +448,10 @@ Shell::Shell(scoped_ptr<NativeRunnerFactory> native_runner_factory,
weak_ptr_factory_(this) {
mojom::ShellClientPtr client;
mojom::ShellClientRequest request = GetProxy(&client);
- Instance* instance = CreateInstance(CreateShellIdentity(),
+ Instance* instance = CreateInstance(Identity(), CreateShellIdentity(),
GetPermissiveCapabilities());
instance->StartWithClient(std::move(client));
+ singletons_.insert(kShellName);
shell_connection_.reset(new ShellConnection(this, std::move(request)));
if (catalog)
@@ -467,7 +491,7 @@ mojom::ShellClientRequest Shell::InitInstanceForEmbedder(
void Shell::SetLoaderForName(scoped_ptr<Loader> loader,
const std::string& name) {
- NameToLoaderMap::iterator it = name_to_loader_.find(name);
+ auto it = name_to_loader_.find(name);
if (it != name_to_loader_.end())
delete it->second;
name_to_loader_[name] = loader.release();
@@ -496,8 +520,10 @@ bool Shell::AcceptConnection(Connection* connection) {
// Shell, private:
void Shell::InitCatalog(mojom::ShellClientPtr catalog) {
- Instance* instance =
- CreateInstance(CreateCatalogIdentity(), CapabilitySpec());
+ Instance* instance = CreateInstance(CreateShellIdentity(),
+ CreateCatalogIdentity(),
+ CapabilitySpec());
+ singletons_.insert(kCatalogName);
instance->StartWithClient(std::move(catalog));
// TODO(beng): this doesn't work anymore.
@@ -509,7 +535,9 @@ void Shell::InitCatalog(mojom::ShellClientPtr catalog) {
}
void Shell::TerminateShellConnections() {
- STLDeleteValues(&identity_to_instance_);
+ Instance* instance = GetExistingInstance(CreateShellIdentity());
+ DCHECK(instance);
+ OnInstanceError(instance);
}
void Shell::OnInstanceError(Instance* instance) {
@@ -588,12 +616,16 @@ bool Shell::ConnectToExistingInstance(scoped_ptr<ConnectParams>* params) {
return !!instance;
}
-Shell::Instance* Shell::CreateInstance(const Identity& target,
+Shell::Instance* Shell::CreateInstance(const Identity& source,
+ const Identity& target,
const CapabilitySpec& spec) {
CHECK(target.user_id() != mojom::kInheritUserID);
Instance* instance = new Instance(this, target, spec);
DCHECK(identity_to_instance_.find(target) ==
identity_to_instance_.end());
+ Instance* source_instance = GetExistingInstance(source);
+ if (source_instance)
+ source_instance->AddChild(instance);
identity_to_instance_[target] = instance;
mojom::InstanceInfoPtr info = instance->CreateInstanceInfo();
instance_listeners_.ForAllPtrs(
@@ -678,13 +710,16 @@ void Shell::OnGotResolvedName(mojom::ShellResolverPtr resolver,
capabilities = capabilities_ptr.To<CapabilitySpec>();
// Clients that request "all_users" class from the shell are allowed to
- // field connection requests from any user.
- if (HasClass(capabilities, kCapabilityClass_AllUsers))
+ // field connection requests from any user. They also run with a synthetic
+ // user id generated here. The user id provided via Connect() is ignored.
+ if (HasClass(capabilities, kCapabilityClass_AllUsers)) {
singletons_.insert(target.name());
+ target.set_user_id(base::GenerateGUID());
+ }
mojom::ClientProcessConnectionPtr client_process_connection =
params->TakeClientProcessConnection();
- Instance* instance = CreateInstance(target, capabilities);
+ Instance* instance = CreateInstance(params->source(), target, capabilities);
// Below are various paths through which a new Instance can be bound to a
// ShellClient proxy.
@@ -736,6 +771,10 @@ Loader* Shell::GetLoaderForName(const std::string& name) {
return default_loader_.get();
}
+base::WeakPtr<Shell> Shell::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
void Shell::CleanupRunner(NativeRunner* runner) {
for (auto it = native_runners_.begin(); it != native_runners_.end(); ++it) {
if (it->get() == runner) {
diff --git a/mojo/shell/shell.h b/mojo/shell/shell.h
index 8b9f975..c060518 100644
--- a/mojo/shell/shell.h
+++ b/mojo/shell/shell.h
@@ -87,11 +87,6 @@ class Shell : public ShellClient {
private:
class Instance;
- using IdentityToInstanceMap = std::map<Identity, Instance*>;
- using NameToLoaderMap = std::map<std::string, Loader*>;
- using IdentityToShellClientFactoryMap =
- std::map<Identity, mojom::ShellClientFactoryPtr>;
-
// ShellClient:
bool AcceptConnection(Connection* connection) override;
@@ -125,7 +120,8 @@ class Shell : public ShellClient {
// and this function returns true.
bool ConnectToExistingInstance(scoped_ptr<ConnectParams>* params);
- Instance* CreateInstance(const Identity& target,
+ Instance* CreateInstance(const Identity& source,
+ const Identity& target,
const CapabilitySpec& spec);
// Called from the instance implementing mojom::Shell.
@@ -168,20 +164,22 @@ class Shell : public ShellClient {
// is no loader configured for the name.
Loader* GetLoaderForName(const std::string& name);
+ base::WeakPtr<Shell> GetWeakPtr();
+
void CleanupRunner(NativeRunner* runner);
// Loader management.
// Loaders are chosen in the order they are listed here.
- NameToLoaderMap name_to_loader_;
+ std::map<std::string, Loader*> name_to_loader_;
scoped_ptr<Loader> default_loader_;
- IdentityToInstanceMap identity_to_instance_;
+ std::map<Identity, Instance*> identity_to_instance_;
// Tracks the names of instances that are allowed to field connection requests
// from all users.
std::set<std::string> singletons_;
- IdentityToShellClientFactoryMap shell_client_factories_;
+ std::map<Identity, mojom::ShellClientFactoryPtr> shell_client_factories_;
// Counter used to assign ids to client factories.
uint32_t shell_client_factory_id_counter_;
diff --git a/mojo/shell/tests/connect/BUILD.gn b/mojo/shell/tests/connect/BUILD.gn
index 48c19a15..fc70dd0 100644
--- a/mojo/shell/tests/connect/BUILD.gn
+++ b/mojo/shell/tests/connect/BUILD.gn
@@ -24,6 +24,7 @@ source_set("connect") {
data_deps = [
":connect_test_app",
":connect_test_class_app",
+ ":connect_test_singleton_app",
":connect_test_driver",
":connect_test_exe",
":connect_test_package",
@@ -123,6 +124,24 @@ mojo_application_manifest("connect_test_class_app_manifest") {
source = "connect_test_class_app_manifest.json"
}
+mojo_native_application("connect_test_singleton_app") {
+ testonly = true
+ sources = [
+ "connect_test_singleton_app.cc",
+ ]
+ deps = [
+ ":connect_test_singleton_app_manifest",
+ "//base",
+ "//mojo/common:common_base",
+ "//mojo/shell/public/cpp:sources",
+ ]
+}
+
+mojo_application_manifest("connect_test_singleton_app_manifest") {
+ application_name = "connect_test_singleton_app"
+ source = "connect_test_singleton_app_manifest.json"
+}
+
executable("connect_test_driver") {
testonly = true
diff --git a/mojo/shell/tests/connect/connect_test_app_manifest.json b/mojo/shell/tests/connect/connect_test_app_manifest.json
index 93b9782..7c45922 100644
--- a/mojo/shell/tests/connect/connect_test_app_manifest.json
+++ b/mojo/shell/tests/connect/connect_test_app_manifest.json
@@ -9,7 +9,7 @@
"classes": [ "class" ],
"interfaces": ["mojo::shell::test::mojom::ConnectTestService"]
},
- "mojo:shell": { "classes": [ "user_id", "all_users" ] }
+ "mojo:shell": { "classes": [ "user_id" ] }
}
}
}
diff --git a/mojo/shell/tests/connect/connect_test_singleton_app.cc b/mojo/shell/tests/connect/connect_test_singleton_app.cc
new file mode 100644
index 0000000..1ee8cbd
--- /dev/null
+++ b/mojo/shell/tests/connect/connect_test_singleton_app.cc
@@ -0,0 +1,36 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/shell/public/cpp/application_runner.h"
+#include "mojo/shell/public/cpp/shell_client.h"
+
+namespace mojo {
+namespace shell {
+
+class ConnectTestSingletonApp : public ShellClient {
+ public:
+ ConnectTestSingletonApp() {}
+ ~ConnectTestSingletonApp() override {}
+
+ private:
+ // mojo::ShellClient:
+ void Initialize(Connector* connector, const Identity& identity,
+ uint32_t id) override {}
+ bool AcceptConnection(Connection* connection) override {
+ return true;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ConnectTestSingletonApp);
+};
+
+} // namespace shell
+} // namespace mojo
+
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ return mojo::ApplicationRunner(
+ new mojo::shell::ConnectTestSingletonApp).Run(shell_handle);
+}
diff --git a/mojo/shell/tests/connect/connect_test_singleton_app_manifest.json b/mojo/shell/tests/connect/connect_test_singleton_app_manifest.json
new file mode 100644
index 0000000..550314d
--- /dev/null
+++ b/mojo/shell/tests/connect/connect_test_singleton_app_manifest.json
@@ -0,0 +1,10 @@
+{
+ "manifest_version": 1,
+ "name": "mojo:connect_test_singleton_app",
+ "display_name": "Connect Test Singleton App",
+ "capabilities": {
+ "required": {
+ "mojo:shell": { "classes": [ "all_users" ] }
+ }
+ }
+}
diff --git a/mojo/shell/tests/connect/connect_unittest.cc b/mojo/shell/tests/connect/connect_unittest.cc
index 9cfb0a7..355f6bac 100644
--- a/mojo/shell/tests/connect/connect_unittest.cc
+++ b/mojo/shell/tests/connect/connect_unittest.cc
@@ -30,6 +30,7 @@ const char kTestAppName[] = "mojo:connect_test_app";
const char kTestAppAName[] = "mojo:connect_test_a";
const char kTestAppBName[] = "mojo:connect_test_b";
const char kTestClassAppName[] = "mojo:connect_test_class_app";
+const char kTestSingletonAppName[] = "mojo:connect_test_singleton_app";
const char kTestDriverName[] = "exe:connect_test_driver";
void ReceiveOneString(std::string* out_string,
@@ -355,26 +356,30 @@ TEST_F(ConnectTest, ConnectToClientProcess_Blocked) {
// Verifies that a client with the "all_users" capability class can receive
// connections from clients run as other users.
TEST_F(ConnectTest, AllUsersSingleton) {
- // Connect to an instance with an explicitly different user_id.
+ // Connect to an instance with an explicitly different user_id. This supplied
+ // user id should be ignored by the shell (which will generate its own
+ // synthetic user id for all-user singleton instances).
const std::string singleton_userid = base::GenerateGUID();
- Connector::ConnectParams params(Identity(kTestAppName, singleton_userid));
+ Connector::ConnectParams params(
+ Identity(kTestSingletonAppName, singleton_userid));
scoped_ptr<Connection> connection = connector()->Connect(&params);
{
base::RunLoop loop;
connection->AddConnectionCompletedClosure(base::Bind(&QuitLoop, &loop));
loop.Run();
- EXPECT_EQ(connection->GetRemoteIdentity().user_id(), singleton_userid);
+ EXPECT_NE(connection->GetRemoteIdentity().user_id(), singleton_userid);
}
- // This connects using the current client's user_id, but should be bound to
- // the instance run as |singleton_userid|.
+ // This connects using the current client's user_id. It should be bound to the
+ // same service started above, with the same shell-generated user id.
scoped_ptr<Connection> inherit_connection =
- connector()->Connect(kTestAppName);
+ connector()->Connect(kTestSingletonAppName);
{
base::RunLoop loop;
inherit_connection->AddConnectionCompletedClosure(
base::Bind(&QuitLoop, &loop));
loop.Run();
- EXPECT_EQ(connection->GetRemoteIdentity().user_id(), singleton_userid);
+ EXPECT_EQ(inherit_connection->GetRemoteIdentity().user_id(),
+ connection->GetRemoteIdentity().user_id());
}
}
diff --git a/mojo/shell/tests/lifecycle/BUILD.gn b/mojo/shell/tests/lifecycle/BUILD.gn
index 34b0a64..fbc2ac7 100644
--- a/mojo/shell/tests/lifecycle/BUILD.gn
+++ b/mojo/shell/tests/lifecycle/BUILD.gn
@@ -25,6 +25,7 @@ source_set("lifecycle") {
data_deps = [
":lifecycle_unittest_app",
+ ":lifecycle_unittest_parent",
":lifecycle_unittest_exe",
":lifecycle_unittest_package",
":manifest",
@@ -115,6 +116,27 @@ mojo_application_manifest("lifecycle_unittest_app_manifest") {
source = "app_manifest.json"
}
+mojo_native_application("lifecycle_unittest_parent") {
+ testonly = true
+ sources = [
+ "parent.cc",
+ ]
+ deps = [
+ ":interfaces",
+ "//base",
+ "//mojo/shell/public/cpp:sources",
+ ]
+
+ data_deps = [
+ ":lifecycle_unittest_parent_manifest",
+ ]
+}
+
+mojo_application_manifest("lifecycle_unittest_parent_manifest") {
+ application_name = "lifecycle_unittest_parent"
+ source = "parent_manifest.json"
+}
+
executable("lifecycle_unittest_exe") {
testonly = true
sources = [
diff --git a/mojo/shell/tests/lifecycle/app_client.cc b/mojo/shell/tests/lifecycle/app_client.cc
index 657d967..a2400ec 100644
--- a/mojo/shell/tests/lifecycle/app_client.cc
+++ b/mojo/shell/tests/lifecycle/app_client.cc
@@ -20,6 +20,10 @@ bool AppClient::AcceptConnection(mojo::Connection* connection) {
return true;
}
+void AppClient::ShellConnectionLost() {
+ GracefulQuit();
+}
+
void AppClient::Create(mojo::Connection* connection,
LifecycleControlRequest request) {
bindings_.AddBinding(this, std::move(request));
diff --git a/mojo/shell/tests/lifecycle/app_client.h b/mojo/shell/tests/lifecycle/app_client.h
index 5a7d5e6..d389f20 100644
--- a/mojo/shell/tests/lifecycle/app_client.h
+++ b/mojo/shell/tests/lifecycle/app_client.h
@@ -37,6 +37,7 @@ class AppClient : public ShellClient,
// ShellClient:
bool AcceptConnection(Connection* connection) override;
+ void ShellConnectionLost() override;
// InterfaceFactory<LifecycleControl>:
void Create(Connection* connection, LifecycleControlRequest request) override;
diff --git a/mojo/shell/tests/lifecycle/lifecycle_unittest.cc b/mojo/shell/tests/lifecycle/lifecycle_unittest.cc
index fd34f11..27e068f 100644
--- a/mojo/shell/tests/lifecycle/lifecycle_unittest.cc
+++ b/mojo/shell/tests/lifecycle/lifecycle_unittest.cc
@@ -18,6 +18,7 @@ namespace shell {
namespace {
const char kTestAppName[] = "mojo:lifecycle_unittest_app";
+const char kTestParentName[] = "mojo:lifecycle_unittest_parent";
const char kTestExeName[] = "exe:lifecycle_unittest_exe";
const char kTestPackageName[] = "mojo:lifecycle_unittest_package";
const char kTestPackageAppNameA[] = "mojo:lifecycle_unittest_package_app_a";
@@ -438,6 +439,34 @@ TEST_F(LifecycleTest, Exe_TerminateProcess) {
EXPECT_EQ(0u, instances()->GetNewInstanceCount());
}
+TEST_F(LifecycleTest, ShutdownTree) {
+ // Verifies that Instances are destroyed when their creator is.
+ scoped_ptr<Connection> parent_connection =
+ connector()->Connect(kTestParentName);
+ test::mojom::ParentPtr parent;
+ parent_connection->GetInterface(&parent);
+
+ // This asks kTestParentName to open a connection to kTestAppName and blocks
+ // on a response from a Ping().
+ {
+ base::RunLoop loop;
+ parent->ConnectToChild(base::Bind(&QuitLoop, &loop));
+ loop.Run();
+ }
+
+ // Should now have two new instances (parent and child).
+ EXPECT_EQ(2u, instances()->GetNewInstanceCount());
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestParentName));
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestAppName));
+
+ parent->Quit();
+
+ // Quitting the parent should cascade-quit the child.
+ WaitForInstanceDestruction();
+ EXPECT_EQ(0u, instances()->GetNewInstanceCount());
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestParentName));
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestAppName));
+}
} // namespace shell
} // namespace mojo
diff --git a/mojo/shell/tests/lifecycle/lifecycle_unittest.mojom b/mojo/shell/tests/lifecycle/lifecycle_unittest.mojom
index 1ae54e8..2197e3b 100644
--- a/mojo/shell/tests/lifecycle/lifecycle_unittest.mojom
+++ b/mojo/shell/tests/lifecycle/lifecycle_unittest.mojom
@@ -21,3 +21,12 @@ interface LifecycleControl {
// longer tracking this application.
CloseShellConnection();
};
+
+// Implemented by an app that connects to another app, thereby creating an
+// instance for it in the shell.
+interface Parent {
+ // Connects to another app and runs the callback when that app has acked a
+ // Ping.
+ ConnectToChild() => ();
+ Quit();
+};
diff --git a/mojo/shell/tests/lifecycle/parent.cc b/mojo/shell/tests/lifecycle/parent.cc
new file mode 100644
index 0000000..27b2099
--- /dev/null
+++ b/mojo/shell/tests/lifecycle/parent.cc
@@ -0,0 +1,80 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/shell/public/cpp/application_runner.h"
+#include "mojo/shell/public/cpp/connector.h"
+#include "mojo/shell/public/cpp/shell_client.h"
+#include "mojo/shell/tests/lifecycle/lifecycle_unittest.mojom.h"
+
+namespace {
+
+void QuitLoop(base::RunLoop* loop) {
+ loop->Quit();
+}
+
+class Parent
+ : public mojo::ShellClient,
+ public mojo::InterfaceFactory<mojo::shell::test::mojom::Parent>,
+ public mojo::shell::test::mojom::Parent {
+ public:
+ Parent() {}
+ ~Parent() override {
+ connector_ = nullptr;
+ child_connection_.reset();
+ parent_bindings_.CloseAllBindings();
+ }
+
+ private:
+ // ShellClient:
+ void Initialize(mojo::Connector* connector, const mojo::Identity& identity,
+ uint32_t id) override {
+ connector_ = connector;
+ }
+ bool AcceptConnection(mojo::Connection* connection) override {
+ connection->AddInterface<mojo::shell::test::mojom::Parent>(this);
+ return true;
+ }
+
+ // InterfaceFactory<mojo::shell::test::mojom::Parent>:
+ void Create(mojo::Connection* connection,
+ mojo::shell::test::mojom::ParentRequest request) override {
+ parent_bindings_.AddBinding(this, std::move(request));
+ }
+
+ // Parent:
+ void ConnectToChild(const ConnectToChildCallback& callback) override {
+ child_connection_ = connector_->Connect("mojo:lifecycle_unittest_app");
+ mojo::shell::test::mojom::LifecycleControlPtr lifecycle;
+ child_connection_->GetInterface(&lifecycle);
+ {
+ base::RunLoop loop;
+ lifecycle->Ping(base::Bind(&QuitLoop, &loop));
+ base::MessageLoop::ScopedNestableTaskAllower allow(
+ base::MessageLoop::current());
+ loop.Run();
+ }
+ callback.Run();
+ }
+ void Quit() override {
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ mojo::Connector* connector_;
+ scoped_ptr<mojo::Connection> child_connection_;
+ mojo::BindingSet<mojo::shell::test::mojom::Parent> parent_bindings_;
+
+ DISALLOW_COPY_AND_ASSIGN(Parent);
+};
+
+} // namespace
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ Parent* parent = new Parent;
+ return mojo::ApplicationRunner(parent).Run(shell_handle);
+}
diff --git a/mojo/shell/tests/lifecycle/parent_manifest.json b/mojo/shell/tests/lifecycle/parent_manifest.json
new file mode 100644
index 0000000..25191c8
--- /dev/null
+++ b/mojo/shell/tests/lifecycle/parent_manifest.json
@@ -0,0 +1,12 @@
+{
+ "manifest_version": 1,
+ "name": "mojo:lifecycle_unittest_parent",
+ "display_name": "Lifecycle Unittest Parent",
+ "capabilities": {
+ "required": {
+ "mojo:lifecycle_unittest_app": {
+ "interfaces": [ "mojo::shell::test::mojom::LifecycleControl" ]
+ }
+ }
+ }
+}