blob: c80830b1ca26e8c1561f1baccecfbfd08dedb5df [file] [log] [blame]
// Copyright 2015 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 "mandoline/services/core_services/core_services_application_delegate.h"
#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "components/clipboard/clipboard_application_delegate.h"
#include "components/resource_provider/resource_provider_app.h"
#include "components/view_manager/surfaces/surfaces_service_application.h"
#include "components/view_manager/view_manager_app.h"
#include "mandoline/ui/browser/browser.h"
#include "mojo/application/public/cpp/application_connection.h"
#include "mojo/application/public/cpp/application_impl.h"
#include "mojo/common/message_pump_mojo.h"
#include "mojo/services/network/network_service_delegate.h"
#include "mojo/services/tracing/tracing_app.h"
#include "url/gurl.h"
#if !defined(OS_ANDROID)
#include "mandoline/ui/omnibox/omnibox_impl.h"
#endif
namespace core_services {
class ApplicationThread;
// A base::Thread which hosts a mojo::ApplicationImpl on its own thread.
//
// Why not base::SimpleThread? The comments in SimpleThread discourage its use,
// and we want most of what base::Thread provides. The hack where we call
// SetThreadWasQuitProperly() in Run() is already used in the chrome codebase.
// (By the time we building a base::Thread here, we already have a MessageLoop
// on our thread, along with a lot of other bookkeeping objects, too. This is
// why we don't call into ApplicationRunner; we already have an AtExitManager et
// all at this point.)
class ApplicationThread : public base::Thread {
public:
ApplicationThread(
const base::WeakPtr<CoreServicesApplicationDelegate>
core_services_application,
const std::string& name,
scoped_ptr<mojo::ApplicationDelegate> delegate,
mojo::InterfaceRequest<mojo::Application> request)
: base::Thread(name),
core_services_application_(core_services_application),
core_services_application_task_runner_(
base::MessageLoop::current()->task_runner()),
delegate_(delegate.Pass()),
request_(request.Pass()) {
}
~ApplicationThread() override {
Stop();
}
// Overridden from base::Thread:
void Run(base::MessageLoop* message_loop) override {
{
application_impl_.reset(new mojo::ApplicationImpl(
delegate_.get(), request_.Pass()));
base::Thread::Run(message_loop);
application_impl_.reset();
}
delegate_.reset();
core_services_application_task_runner_->PostTask(
FROM_HERE,
base::Bind(&CoreServicesApplicationDelegate::ApplicationThreadDestroyed,
core_services_application_,
this));
// TODO(erg): This is a hack.
//
// Right now, most of our services do not receive
// Application::RequestQuit() calls. jam@ is currently working on shutting
// down everything cleanly. In the long run, we don't wan this here (we
// want this set in ShutdownCleanly()), but until we can rely on
// RequestQuit() getting delivered we have to manually do this here.
//
// Remove this ASAP.
Thread::SetThreadWasQuitProperly(true);
}
void RequestQuit() {
if (!IsRunning())
return;
task_runner()->PostTask(
FROM_HERE,
base::Bind(&ApplicationThread::ShutdownCleanly,
base::Unretained(this)));
}
void ShutdownCleanly() {
application_impl_->QuitNow();
Thread::SetThreadWasQuitProperly(true);
}
private:
base::WeakPtr<CoreServicesApplicationDelegate> core_services_application_;
scoped_refptr<base::SingleThreadTaskRunner>
core_services_application_task_runner_;
scoped_ptr<mojo::ApplicationImpl> application_impl_;
scoped_ptr<mojo::ApplicationDelegate> delegate_;
mojo::InterfaceRequest<mojo::Application> request_;
DISALLOW_COPY_AND_ASSIGN(ApplicationThread);
};
CoreServicesApplicationDelegate::CoreServicesApplicationDelegate()
: weak_factory_(this) {
}
CoreServicesApplicationDelegate::~CoreServicesApplicationDelegate() {
application_threads_.clear();
}
void CoreServicesApplicationDelegate::ApplicationThreadDestroyed(
ApplicationThread* thread) {
ScopedVector<ApplicationThread>::iterator iter =
std::find(application_threads_.begin(),
application_threads_.end(),
thread);
DCHECK(iter != application_threads_.end());
application_threads_.erase(iter);
}
bool CoreServicesApplicationDelegate::ConfigureIncomingConnection(
mojo::ApplicationConnection* connection) {
connection->AddService(this);
return true;
}
void CoreServicesApplicationDelegate::Quit() {
// Fire off RequestQuit() messages to all the threads before we try to join
// on them.
for (ApplicationThread* thread : application_threads_)
thread->RequestQuit();
// This will delete all threads. This also performs a blocking join, waiting
// for the threads to end.
application_threads_.clear();
weak_factory_.InvalidateWeakPtrs();
}
void CoreServicesApplicationDelegate::Create(
mojo::ApplicationConnection* connection,
mojo::InterfaceRequest<mojo::ContentHandler> request) {
handler_bindings_.AddBinding(this, request.Pass());
}
void CoreServicesApplicationDelegate::StartApplication(
mojo::InterfaceRequest<mojo::Application> request,
mojo::URLResponsePtr response) {
std::string url = response->url;
scoped_ptr<mojo::ApplicationDelegate> delegate;
if (url == "mojo://clipboard/")
delegate.reset(new clipboard::ClipboardApplicationDelegate);
else if (url == "mojo://network_service/")
delegate.reset(new NetworkServiceDelegate);
#if !defined(OS_ANDROID)
else if (url == "mojo://omnibox/")
delegate.reset(new mandoline::OmniboxImpl);
#endif
else if (url == "mojo://resource_provider/")
delegate.reset(new resource_provider::ResourceProviderApp);
else if (url == "mojo://surfaces_service/")
delegate.reset(new surfaces::SurfacesServiceApplication);
else if (url == "mojo://tracing/")
delegate.reset(new tracing::TracingApp);
else if (url == "mojo://view_manager/")
delegate.reset(new view_manager::ViewManagerApp);
else if (url == "mojo://browser/")
delegate.reset(new mandoline::Browser);
else
NOTREACHED() << "This application package does not support " << url;
base::Thread::Options thread_options;
// In the case of mojo:network_service, we must use an IO message loop.
if (url == "mojo://network_service/") {
thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
} else if (url == "mojo://view_manager/") {
thread_options.message_loop_type = base::MessageLoop::TYPE_UI;
} else {
// We must use a MessagePumpMojo to awake on mojo messages.
thread_options.message_pump_factory =
base::Bind(&mojo::common::MessagePumpMojo::Create);
}
scoped_ptr<ApplicationThread> thread(
new ApplicationThread(weak_factory_.GetWeakPtr(), url, delegate.Pass(),
request.Pass()));
thread->StartWithOptions(thread_options);
application_threads_.push_back(thread.Pass());
}
} // namespace core_services