blob: 296e5d26f91320a1d56c5eeff8b223e96906589a [file] [log] [blame]
// Copyright 2014 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 "shell/external_application_listener.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_checker.h"
#include "base/tracked_objects.h"
#include "mojo/common/common_type_converters.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/public/cpp/bindings/error_handler.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "shell/domain_socket/net_errors.h"
#include "shell/domain_socket/socket_descriptor.h"
#include "shell/external_application_registrar.mojom.h"
#include "shell/incoming_connection_listener.h"
#include "url/gurl.h"
namespace mojo {
namespace shell {
namespace {
const char kDefaultListenSocketPath[] = "/var/run/mojo/system_socket";
class RegistrarImpl : public ExternalApplicationRegistrar, public ErrorHandler {
public:
RegistrarImpl(const ExternalApplicationListener::RegisterCallback& callback,
embedder::ScopedPlatformHandle incoming_socket,
scoped_refptr<base::TaskRunner> io_runner)
: register_callback_(callback),
binding_(this),
channel_info_(nullptr),
weak_ptr_factory_(this) {
binding_.Bind(
embedder::CreateChannel(incoming_socket.Pass(), io_runner,
base::Bind(&RegistrarImpl::OnChannelCreated,
weak_ptr_factory_.GetWeakPtr()),
base::MessageLoop::current()->task_runner()));
binding_.set_error_handler(this);
}
~RegistrarImpl() override {
if (channel_info_) {
// TODO(vtl): The |base::DoNothing| here is bad. We're relying on the I/O
// thread being destroyed after us (and probably also relying on the work
// on the I/O thread to be relatively prompt).
embedder::DestroyChannel(channel_info_, base::Bind(&base::DoNothing),
nullptr);
}
}
private:
void OnConnectionError() override {
DCHECK(channel_info_);
embedder::WillDestroyChannelSoon(channel_info_);
}
void Register(
const String& app_url,
Array<String> args,
const Callback<void(InterfaceRequest<Application>)>& callback) override {
ApplicationPtr application;
InterfaceRequest<Application> application_request = GetProxy(&application);
register_callback_.Run(app_url.To<GURL>(),
args.To<std::vector<std::string>>(),
application.Pass());
callback.Run(application_request.Pass());
}
static void OnChannelCreated(base::WeakPtr<RegistrarImpl> self,
embedder::ChannelInfo* channel_info) {
if (self) {
self->channel_info_ = channel_info;
} else {
// TODO(vtl): See TODO in destructor.
embedder::DestroyChannel(channel_info, base::Bind(&base::DoNothing),
nullptr);
}
}
const ExternalApplicationListener::RegisterCallback register_callback_;
StrongBinding<ExternalApplicationRegistrar> binding_;
embedder::ChannelInfo* channel_info_;
base::WeakPtrFactory<RegistrarImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(RegistrarImpl);
};
} // namespace
// static
base::FilePath ExternalApplicationListener::ConstructDefaultSocketPath() {
return base::FilePath(FILE_PATH_LITERAL(kDefaultListenSocketPath));
}
ExternalApplicationListener::ExternalApplicationListener(
const scoped_refptr<base::SequencedTaskRunner>& shell_runner,
const scoped_refptr<base::SequencedTaskRunner>& io_runner)
: shell_runner_(shell_runner),
io_runner_(io_runner),
signal_on_listening_(true, false),
weak_ptr_factory_(this) {
DCHECK(shell_runner_.get() && shell_runner_->RunsTasksOnCurrentThread());
DCHECK(io_runner_.get());
listener_thread_checker_.DetachFromThread(); // Will attach in StartListener.
}
ExternalApplicationListener::~ExternalApplicationListener() {
DCHECK(register_thread_checker_.CalledOnValidThread());
weak_ptr_factory_.InvalidateWeakPtrs();
// listener_ needs to be destroyed on io_runner_, and it has to die before
// this object does, as it holds a pointer back to this instance.
base::WaitableEvent stop_listening_event(true, false);
io_runner_->PostTask(
FROM_HERE, base::Bind(&ExternalApplicationListener::StopListening,
base::Unretained(this), &stop_listening_event));
stop_listening_event.Wait();
}
void ExternalApplicationListener::ListenInBackground(
const base::FilePath& listen_socket_path,
const RegisterCallback& register_callback) {
DCHECK(register_thread_checker_.CalledOnValidThread());
ListenInBackgroundWithErrorCallback(listen_socket_path, register_callback,
ErrorCallback());
}
void ExternalApplicationListener::ListenInBackgroundWithErrorCallback(
const base::FilePath& listen_socket_path,
const RegisterCallback& register_callback,
const ErrorCallback& error_callback) {
DCHECK(register_thread_checker_.CalledOnValidThread());
register_callback_ = register_callback;
error_callback_ = error_callback;
io_runner_->PostTask(FROM_HERE,
base::Bind(&ExternalApplicationListener::StartListening,
base::Unretained(this), listen_socket_path));
}
void ExternalApplicationListener::WaitForListening() {
DCHECK(register_thread_checker_.CalledOnValidThread());
signal_on_listening_.Wait();
}
void ExternalApplicationListener::StartListening(
const base::FilePath& listen_socket_path) {
CHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_IO);
DCHECK(listener_thread_checker_.CalledOnValidThread());
listener_.reset(new IncomingConnectionListener(listen_socket_path, this));
listener_->StartListening();
}
void ExternalApplicationListener::StopListening(base::WaitableEvent* event) {
DCHECK(listener_thread_checker_.CalledOnValidThread());
listener_.reset();
event->Signal();
}
void ExternalApplicationListener::OnListening(int rv) {
DCHECK(listener_thread_checker_.CalledOnValidThread());
signal_on_listening_.Signal();
shell_runner_->PostTask(
FROM_HERE,
base::Bind(
&ExternalApplicationListener::RunErrorCallbackIfListeningFailed,
weak_ptr_factory_.GetWeakPtr(), rv));
}
void ExternalApplicationListener::OnConnection(SocketDescriptor incoming) {
DCHECK(listener_thread_checker_.CalledOnValidThread());
embedder::ScopedPlatformHandle incoming_socket(
(embedder::PlatformHandle(incoming)));
shell_runner_->PostTask(
FROM_HERE, base::Bind(&ExternalApplicationListener::CreateRegistrar,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&incoming_socket)));
}
void ExternalApplicationListener::RunErrorCallbackIfListeningFailed(int rv) {
DCHECK(register_thread_checker_.CalledOnValidThread());
if (rv != net::OK && !error_callback_.is_null())
error_callback_.Run(rv);
}
void ExternalApplicationListener::CreateRegistrar(
embedder::ScopedPlatformHandle incoming_socket) {
DCHECK(register_thread_checker_.CalledOnValidThread());
DVLOG(1) << "Accepted incoming connection";
// Passes ownership of incoming_socket to registrar.
new RegistrarImpl(register_callback_, incoming_socket.Pass(), io_runner_);
}
} // namespace shell
} // namespace mojo