blob: 8e0c28e3920b80ff5984b3524eae58bbec108b03 [file] [log] [blame]
// 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 <algorithm>
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "mojo/public/c/system/main.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/shell/public/cpp/application_runner.h"
#include "services/shell/public/cpp/shell_connection.h"
#include "services/shell/public/interfaces/shell_client_factory.mojom.h"
#include "services/shell/tests/lifecycle/app_client.h"
#include "services/shell/tests/lifecycle/lifecycle_unittest.mojom.h"
namespace {
class PackagedApp : public shell::ShellClient,
public shell::InterfaceFactory<LifecycleControl>,
public LifecycleControl {
public:
using DestructCallback = base::Callback<void(PackagedApp*)>;
PackagedApp(shell::mojom::ShellClientRequest request,
const DestructCallback& shell_connection_closed_callback,
const DestructCallback& destruct_callback)
: connection_(new shell::ShellConnection(this, std::move(request))),
shell_connection_closed_callback_(shell_connection_closed_callback),
destruct_callback_(destruct_callback) {
bindings_.set_connection_error_handler(base::Bind(&PackagedApp::BindingLost,
base::Unretained(this)));
}
~PackagedApp() override {
destruct_callback_.Run(this);
}
private:
// shell::ShellClient:
bool AcceptConnection(shell::Connection* connection) override {
connection->AddInterface<LifecycleControl>(this);
return true;
}
// shell::InterfaceFactory<LifecycleControl>
void Create(shell::Connection* connection,
LifecycleControlRequest request) override {
bindings_.AddBinding(this, std::move(request));
}
// LifecycleControl:
void Ping(const PingCallback& callback) override {
callback.Run();
}
void GracefulQuit() override {
shell_connection_closed_callback_.Run(this);
delete this;
// This will have closed all |bindings_|.
}
void Crash() override {
// When multiple instances are vended from the same package instance, this
// will cause all instances to be quit.
exit(1);
}
void CloseShellConnection() override {
shell_connection_closed_callback_.Run(this);
connection_.reset();
// This only closed our relationship with the shell, existing |bindings_|
// remain active.
}
void BindingLost() {
if (bindings_.empty())
delete this;
}
std::unique_ptr<shell::ShellConnection> connection_;
mojo::BindingSet<LifecycleControl> bindings_;
// Run when this object's connection to the shell is closed.
DestructCallback shell_connection_closed_callback_;
// Run when this object is destructed.
DestructCallback destruct_callback_;
DISALLOW_COPY_AND_ASSIGN(PackagedApp);
};
class Package
: public shell::ShellClient,
public shell::InterfaceFactory<shell::mojom::ShellClientFactory>,
public shell::mojom::ShellClientFactory {
public:
Package() {}
~Package() override {}
void set_runner(shell::ApplicationRunner* runner) {
app_client_.set_runner(runner);
}
private:
// shell::test::AppClient:
bool AcceptConnection(shell::Connection* connection) override {
connection->AddInterface<shell::mojom::ShellClientFactory>(this);
return app_client_.AcceptConnection(connection);
}
// shell::InterfaceFactory<shell::mojom::ShellClientFactory>:
void Create(shell::Connection* connection,
shell::mojom::ShellClientFactoryRequest request) override {
bindings_.AddBinding(this, std::move(request));
}
// shell::mojom::ShellClientFactory:
void CreateShellClient(shell::mojom::ShellClientRequest request,
const mojo::String& name) override {
++shell_connection_refcount_;
apps_.push_back(
new PackagedApp(std::move(request),
base::Bind(&Package::AppShellConnectionClosed,
base::Unretained(this)),
base::Bind(&Package::AppDestructed,
base::Unretained(this))));
}
void AppShellConnectionClosed(PackagedApp* app) {
if (!--shell_connection_refcount_)
app_client_.CloseShellConnection();
}
void AppDestructed(PackagedApp* app) {
auto it = std::find(apps_.begin(), apps_.end(), app);
DCHECK(it != apps_.end());
apps_.erase(it);
if (apps_.empty() && base::MessageLoop::current()->is_running())
base::MessageLoop::current()->QuitWhenIdle();
}
shell::test::AppClient app_client_;
int shell_connection_refcount_ = 0;
mojo::BindingSet<shell::mojom::ShellClientFactory> bindings_;
std::vector<PackagedApp*> apps_;
DISALLOW_COPY_AND_ASSIGN(Package);
};
} // namespace
MojoResult MojoMain(MojoHandle shell_handle) {
Package* package = new Package;
shell::ApplicationRunner runner(package);
package->set_runner(&runner);
return runner.Run(shell_handle);
}