summaryrefslogtreecommitdiff
path: root/chromium/mojo/shell
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2015-06-18 14:10:49 +0200
committerOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>2015-06-18 13:53:24 +0000
commit813fbf95af77a531c57a8c497345ad2c61d475b3 (patch)
tree821b2c8de8365f21b6c9ba17a236fb3006a1d506 /chromium/mojo/shell
parentaf6588f8d723931a298c995fa97259bb7f7deb55 (diff)
downloadqtwebengine-chromium-813fbf95af77a531c57a8c497345ad2c61d475b3.tar.gz
BASELINE: Update chromium to 44.0.2403.47
Change-Id: Ie056fedba95cf5e5c76b30c4b2c80fca4764aa2f Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/mojo/shell')
-rw-r--r--chromium/mojo/shell/BUILD.gn78
-rw-r--r--chromium/mojo/shell/DEPS5
-rw-r--r--chromium/mojo/shell/application_loader.h38
-rw-r--r--chromium/mojo/shell/application_manager.cc531
-rw-r--r--chromium/mojo/shell/application_manager.h261
-rw-r--r--chromium/mojo/shell/application_manager_unittest.cc824
-rw-r--r--chromium/mojo/shell/data_pipe_peek.cc160
-rw-r--r--chromium/mojo/shell/data_pipe_peek.h38
-rw-r--r--chromium/mojo/shell/fetcher.cc38
-rw-r--r--chromium/mojo/shell/fetcher.h76
-rw-r--r--chromium/mojo/shell/identity.cc29
-rw-r--r--chromium/mojo/shell/identity.h31
-rw-r--r--chromium/mojo/shell/local_fetcher.cc96
-rw-r--r--chromium/mojo/shell/local_fetcher.h49
-rw-r--r--chromium/mojo/shell/native_runner.h63
-rw-r--r--chromium/mojo/shell/network_fetcher.cc251
-rw-r--r--chromium/mojo/shell/network_fetcher.h83
-rw-r--r--chromium/mojo/shell/query_util.cc31
-rw-r--r--chromium/mojo/shell/query_util.h23
-rw-r--r--chromium/mojo/shell/query_util_unittest.cc47
-rw-r--r--chromium/mojo/shell/shell_impl.cc62
-rw-r--r--chromium/mojo/shell/shell_impl.h62
-rw-r--r--chromium/mojo/shell/switches.cc26
-rw-r--r--chromium/mojo/shell/switches.h21
-rw-r--r--chromium/mojo/shell/test.mojom23
25 files changed, 2946 insertions, 0 deletions
diff --git a/chromium/mojo/shell/BUILD.gn b/chromium/mojo/shell/BUILD.gn
new file mode 100644
index 00000000000..5b10e53e60e
--- /dev/null
+++ b/chromium/mojo/shell/BUILD.gn
@@ -0,0 +1,78 @@
+# 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.
+
+import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni")
+import("//testing/test.gni")
+
+source_set("shell") {
+ output_name = "mojo_shell"
+ sources = [
+ "application_loader.h",
+ "application_manager.cc",
+ "application_manager.h",
+ "data_pipe_peek.cc",
+ "data_pipe_peek.h",
+ "fetcher.cc",
+ "fetcher.h",
+ "identity.cc",
+ "identity.h",
+ "local_fetcher.cc",
+ "local_fetcher.h",
+ "native_runner.h",
+ "network_fetcher.cc",
+ "network_fetcher.h",
+ "query_util.cc",
+ "query_util.h",
+ "shell_impl.cc",
+ "shell_impl.h",
+ "switches.cc",
+ "switches.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//mojo/application/public/interfaces",
+ "//mojo/common",
+ "//third_party/mojo/src/mojo/public/cpp/bindings",
+ "//mojo/services/network/public/interfaces",
+ "//url",
+ ]
+ deps = [
+ "//base/third_party/dynamic_annotations",
+ "//crypto:crypto",
+ "//url",
+ "//third_party/mojo/src/mojo/edk/system",
+ "//mojo/environment:chromium",
+ "//mojo/util:filename_util",
+ "//third_party/mojo_services/src/content_handler/public/interfaces",
+ ]
+}
+
+test("tests") {
+ output_name = "mojo_shell_unittests"
+ sources = [
+ "application_manager_unittest.cc",
+ "query_util_unittest.cc",
+ ]
+
+ deps = [
+ ":shell",
+ ":test_bindings",
+ "//base",
+ "//mojo/application",
+ "//mojo/application/public/cpp",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//third_party/mojo/src/mojo/edk/test:run_all_unittests",
+ "//third_party/mojo/src/mojo/public/cpp/bindings",
+ "//testing/gtest",
+ "//url",
+ ]
+}
+
+mojom("test_bindings") {
+ sources = [
+ "test.mojom",
+ ]
+}
diff --git a/chromium/mojo/shell/DEPS b/chromium/mojo/shell/DEPS
new file mode 100644
index 00000000000..7e32ecdb31e
--- /dev/null
+++ b/chromium/mojo/shell/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+crypto",
+ "+third_party/mojo_services/src/content_handler/public/interfaces",
+ "-mojo/runner",
+]
diff --git a/chromium/mojo/shell/application_loader.h b/chromium/mojo/shell/application_loader.h
new file mode 100644
index 00000000000..f464ab1305a
--- /dev/null
+++ b/chromium/mojo/shell/application_loader.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef SHELL_APPLICATION_MANAGER_APPLICATION_LOADER_H_
+#define SHELL_APPLICATION_MANAGER_APPLICATION_LOADER_H_
+
+#include "base/callback.h"
+#include "mojo/application/public/interfaces/shell.mojom.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/network/public/interfaces/url_loader.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class Application;
+
+namespace shell {
+
+class ApplicationManager;
+
+// Interface to implement special application loading behavior for a particular
+// URL or scheme.
+class ApplicationLoader {
+ public:
+ virtual ~ApplicationLoader() {}
+
+ virtual void Load(const GURL& url,
+ InterfaceRequest<Application> application_request) = 0;
+
+ protected:
+ ApplicationLoader() {}
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // SHELL_APPLICATION_MANAGER_APPLICATION_LOADER_H_
diff --git a/chromium/mojo/shell/application_manager.cc b/chromium/mojo/shell/application_manager.cc
new file mode 100644
index 00000000000..2b08442a2d9
--- /dev/null
+++ b/chromium/mojo/shell/application_manager.cc
@@ -0,0 +1,531 @@
+// 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 "mojo/shell/application_manager.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/trace_event/trace_event.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/shell/fetcher.h"
+#include "mojo/shell/local_fetcher.h"
+#include "mojo/shell/network_fetcher.h"
+#include "mojo/shell/query_util.h"
+#include "mojo/shell/shell_impl.h"
+#include "mojo/shell/switches.h"
+#include "third_party/mojo_services/src/content_handler/public/interfaces/content_handler.mojom.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+// Used by TestAPI.
+bool has_created_instance = false;
+
+} // namespace
+
+class ApplicationManager::ContentHandlerConnection : public ErrorHandler {
+ public:
+ ContentHandlerConnection(ApplicationManager* manager,
+ const GURL& content_handler_url,
+ const GURL& requestor_url,
+ const std::string& qualifier)
+ : manager_(manager),
+ content_handler_url_(content_handler_url),
+ content_handler_qualifier_(qualifier) {
+ ServiceProviderPtr services;
+ manager->ConnectToApplicationWithParameters(
+ content_handler_url, qualifier, requestor_url, GetProxy(&services),
+ nullptr, base::Closure(), std::vector<std::string>());
+ MessagePipe pipe;
+ content_handler_.Bind(
+ InterfacePtrInfo<ContentHandler>(pipe.handle0.Pass(), 0u));
+ services->ConnectToService(ContentHandler::Name_, pipe.handle1.Pass());
+ content_handler_.set_error_handler(this);
+ }
+
+ ContentHandler* content_handler() { return content_handler_.get(); }
+
+ GURL content_handler_url() { return content_handler_url_; }
+ std::string content_handler_qualifier() { return content_handler_qualifier_; }
+
+ private:
+ // ErrorHandler implementation:
+ void OnConnectionError() override { manager_->OnContentHandlerError(this); }
+
+ ApplicationManager* manager_;
+ GURL content_handler_url_;
+ std::string content_handler_qualifier_;
+ ContentHandlerPtr content_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection);
+};
+
+// static
+ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
+ : manager_(manager) {
+}
+
+ApplicationManager::TestAPI::~TestAPI() {
+}
+
+bool ApplicationManager::TestAPI::HasCreatedInstance() {
+ return has_created_instance;
+}
+
+bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const {
+ return manager_->identity_to_shell_impl_.find(Identity(url)) !=
+ manager_->identity_to_shell_impl_.end();
+}
+
+ApplicationManager::ApplicationManager(Delegate* delegate)
+ : delegate_(delegate), weak_ptr_factory_(this) {
+}
+
+ApplicationManager::~ApplicationManager() {
+ STLDeleteValues(&url_to_content_handler_);
+ TerminateShellConnections();
+ STLDeleteValues(&url_to_loader_);
+ STLDeleteValues(&scheme_to_loader_);
+}
+
+void ApplicationManager::TerminateShellConnections() {
+ STLDeleteValues(&identity_to_shell_impl_);
+}
+
+void ApplicationManager::ConnectToApplication(
+ const GURL& requested_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const base::Closure& on_application_end) {
+ ConnectToApplicationWithParameters(
+ requested_url, std::string(), requestor_url, services.Pass(),
+ exposed_services.Pass(), on_application_end, std::vector<std::string>());
+}
+
+void ApplicationManager::ConnectToApplicationWithParameters(
+ const GURL& requested_url,
+ const std::string& qualifier,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const base::Closure& on_application_end,
+ const std::vector<std::string>& pre_redirect_parameters) {
+ TRACE_EVENT_INSTANT1(
+ "mojo_shell", "ApplicationManager::ConnectToApplicationWithParameters",
+ TRACE_EVENT_SCOPE_THREAD, "requested_url", requested_url.spec());
+ DCHECK(requested_url.is_valid());
+
+ // We check both the mapped and resolved urls for existing shell_impls because
+ // external applications can be registered for the unresolved mojo:foo urls.
+
+ GURL mapped_url = delegate_->ResolveMappings(requested_url);
+ if (ConnectToRunningApplication(mapped_url, qualifier, requestor_url,
+ &services, &exposed_services)) {
+ return;
+ }
+
+ GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
+ if (ConnectToRunningApplication(resolved_url, qualifier, requestor_url,
+ &services, &exposed_services)) {
+ return;
+ }
+
+ // The application is not running, let's compute the parameters.
+ if (ConnectToApplicationWithLoader(
+ requested_url, qualifier, mapped_url, requestor_url, &services,
+ &exposed_services, on_application_end, pre_redirect_parameters,
+ GetLoaderForURL(mapped_url))) {
+ return;
+ }
+
+ if (ConnectToApplicationWithLoader(
+ requested_url, qualifier, resolved_url, requestor_url, &services,
+ &exposed_services, on_application_end, pre_redirect_parameters,
+ GetLoaderForURL(resolved_url))) {
+ return;
+ }
+
+ if (ConnectToApplicationWithLoader(
+ requested_url, qualifier, resolved_url, requestor_url, &services,
+ &exposed_services, on_application_end, pre_redirect_parameters,
+ default_loader_.get())) {
+ return;
+ }
+
+ auto callback = base::Bind(
+ &ApplicationManager::HandleFetchCallback, weak_ptr_factory_.GetWeakPtr(),
+ requested_url, qualifier, requestor_url, base::Passed(services.Pass()),
+ base::Passed(exposed_services.Pass()), on_application_end,
+ pre_redirect_parameters);
+
+ if (delegate_->CreateFetcher(
+ resolved_url,
+ base::Bind(callback, NativeApplicationCleanup::DONT_DELETE))) {
+ return;
+ }
+
+ if (resolved_url.SchemeIsFile()) {
+ new LocalFetcher(
+ resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
+ base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
+ return;
+ }
+
+ if (!network_service_)
+ ConnectToService(GURL("mojo:network_service"), &network_service_);
+
+ const NativeApplicationCleanup cleanup =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDontDeleteOnDownload)
+ ? NativeApplicationCleanup::DONT_DELETE
+ : NativeApplicationCleanup::DELETE;
+
+ new NetworkFetcher(disable_cache_, resolved_url, network_service_.get(),
+ base::Bind(callback, cleanup));
+}
+
+bool ApplicationManager::ConnectToRunningApplication(
+ const GURL& resolved_url,
+ const std::string& qualifier,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider>* services,
+ ServiceProviderPtr* exposed_services) {
+ GURL application_url = GetBaseURLAndQuery(resolved_url, nullptr);
+ ShellImpl* shell_impl = GetShellImpl(application_url, qualifier);
+ if (!shell_impl)
+ return false;
+
+ ConnectToClient(shell_impl, resolved_url, requestor_url, services->Pass(),
+ exposed_services->Pass());
+ return true;
+}
+
+bool ApplicationManager::ConnectToApplicationWithLoader(
+ const GURL& requested_url,
+ const std::string& qualifier,
+ const GURL& resolved_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider>* services,
+ ServiceProviderPtr* exposed_services,
+ const base::Closure& on_application_end,
+ const std::vector<std::string>& parameters,
+ ApplicationLoader* loader) {
+ if (!loader)
+ return false;
+
+ const GURL app_url =
+ requested_url.scheme() == "mojo" ? requested_url : resolved_url;
+
+ loader->Load(
+ resolved_url,
+ RegisterShell(app_url, qualifier, requestor_url, services->Pass(),
+ exposed_services->Pass(), on_application_end, parameters));
+ return true;
+}
+
+InterfaceRequest<Application> ApplicationManager::RegisterShell(
+ const GURL& app_url,
+ const std::string& qualifier,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const base::Closure& on_application_end,
+ const std::vector<std::string>& parameters) {
+ Identity app_identity(app_url, qualifier);
+
+ ApplicationPtr application;
+ InterfaceRequest<Application> application_request = GetProxy(&application);
+ ShellImpl* shell =
+ new ShellImpl(application.Pass(), this, app_identity, on_application_end);
+ identity_to_shell_impl_[app_identity] = shell;
+ shell->InitializeApplication(Array<String>::From(parameters));
+ ConnectToClient(shell, app_url, requestor_url, services.Pass(),
+ exposed_services.Pass());
+ return application_request.Pass();
+}
+
+ShellImpl* ApplicationManager::GetShellImpl(const GURL& url,
+ const std::string& qualifier) {
+ const auto& shell_it = identity_to_shell_impl_.find(Identity(url, qualifier));
+ if (shell_it != identity_to_shell_impl_.end())
+ return shell_it->second;
+ return nullptr;
+}
+
+void ApplicationManager::ConnectToClient(
+ ShellImpl* shell_impl,
+ const GURL& resolved_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) {
+ shell_impl->ConnectToClient(resolved_url, requestor_url, services.Pass(),
+ exposed_services.Pass());
+}
+
+void ApplicationManager::HandleFetchCallback(
+ const GURL& requested_url,
+ const std::string& qualifier,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const base::Closure& on_application_end,
+ const std::vector<std::string>& parameters,
+ NativeApplicationCleanup cleanup,
+ scoped_ptr<Fetcher> fetcher) {
+ if (!fetcher) {
+ // Network error. Drop |application_request| to tell requestor.
+ return;
+ }
+
+ GURL redirect_url = fetcher->GetRedirectURL();
+ if (!redirect_url.is_empty()) {
+ // And around we go again... Whee!
+ // TODO(sky): this loses |requested_url|.
+ ConnectToApplicationWithParameters(redirect_url, qualifier, requestor_url,
+ services.Pass(), exposed_services.Pass(),
+ on_application_end, parameters);
+ return;
+ }
+
+ // We already checked if the application was running before we fetched it, but
+ // it might have started while the fetch was outstanding. We don't want to
+ // have two copies of the app running, so check again.
+ //
+ // Also, it's possible the original URL was redirected to an app that is
+ // already running.
+ if (ConnectToRunningApplication(requested_url, qualifier, requestor_url,
+ &services, &exposed_services)) {
+ return;
+ }
+
+ const GURL app_url =
+ requested_url.scheme() == "mojo" ? requested_url : fetcher->GetURL();
+
+ InterfaceRequest<Application> request(
+ RegisterShell(app_url, qualifier, requestor_url, services.Pass(),
+ exposed_services.Pass(), on_application_end, parameters));
+
+ // If the response begins with a #!mojo <content-handler-url>, use it.
+ GURL content_handler_url;
+ std::string shebang;
+ if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
+ LoadWithContentHandler(
+ content_handler_url, requestor_url, qualifier, request.Pass(),
+ fetcher->AsURLResponse(blocking_pool_,
+ static_cast<int>(shebang.size())));
+ return;
+ }
+
+ MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
+ if (iter != mime_type_to_url_.end()) {
+ LoadWithContentHandler(iter->second, requestor_url, qualifier,
+ request.Pass(),
+ fetcher->AsURLResponse(blocking_pool_, 0));
+ return;
+ }
+
+ auto alias_iter = application_package_alias_.find(app_url);
+ if (alias_iter != application_package_alias_.end()) {
+ // We replace the qualifier with the one our package alias requested.
+ URLResponsePtr response(URLResponse::New());
+ response->url = String::From(app_url.spec());
+
+ std::string qualifier;
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableMultiprocess)) {
+ // Why can't we use this in single process mode? Because of
+ // base::AtExitManager. If you link in ApplicationRunnerChromium into
+ // your code, and then we make initialize multiple copies of the
+ // application, we end up with multiple AtExitManagers and will check on
+ // the second one being created.
+ //
+ // Why doesn't that happen when running different apps? Because
+ // your_thing.mojo!base::AtExitManager and
+ // my_thing.mojo!base::AtExitManager are different symbols.
+ qualifier = alias_iter->second.second;
+ }
+
+ LoadWithContentHandler(alias_iter->second.first, requestor_url, qualifier,
+ request.Pass(), response.Pass());
+ return;
+ }
+
+ // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
+ // application. That could either mean looking for the platform-specific dll
+ // header, or looking for some specific mojo signature prepended to the
+ // library.
+ // TODO(vtl): (Maybe this should be done by the factory/runner?)
+
+ GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr);
+ NativeRunnerFactory::Options options;
+ if (url_to_native_options_.find(base_resolved_url) !=
+ url_to_native_options_.end()) {
+ DVLOG(2) << "Applying stored native options to resolved URL "
+ << fetcher->GetURL();
+ options = url_to_native_options_[base_resolved_url];
+ }
+
+ fetcher->AsPath(
+ blocking_pool_,
+ base::Bind(&ApplicationManager::RunNativeApplication,
+ weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()),
+ options, cleanup, base::Passed(fetcher.Pass())));
+}
+
+void ApplicationManager::RunNativeApplication(
+ InterfaceRequest<Application> application_request,
+ const NativeRunnerFactory::Options& options,
+ NativeApplicationCleanup cleanup,
+ scoped_ptr<Fetcher> fetcher,
+ const base::FilePath& path,
+ bool path_exists) {
+ // We only passed fetcher to keep it alive. Done with it now.
+ fetcher.reset();
+
+ DCHECK(application_request.is_pending());
+
+ if (!path_exists) {
+ LOG(ERROR) << "Library not started because library path '" << path.value()
+ << "' does not exist.";
+ return;
+ }
+
+ TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
+ path.AsUTF8Unsafe());
+ NativeRunner* runner = native_runner_factory_->Create(options).release();
+ native_runners_.push_back(runner);
+ runner->Start(path, cleanup, application_request.Pass(),
+ base::Bind(&ApplicationManager::CleanupRunner,
+ weak_ptr_factory_.GetWeakPtr(), runner));
+}
+
+void ApplicationManager::RegisterContentHandler(
+ const std::string& mime_type,
+ const GURL& content_handler_url) {
+ DCHECK(content_handler_url.is_valid())
+ << "Content handler URL is invalid for mime type " << mime_type;
+ mime_type_to_url_[mime_type] = content_handler_url;
+}
+
+void ApplicationManager::RegisterApplicationPackageAlias(
+ const GURL& alias,
+ const GURL& content_handler_package,
+ const std::string& qualifier) {
+ application_package_alias_[alias] =
+ std::make_pair(content_handler_package, qualifier);
+}
+
+void ApplicationManager::LoadWithContentHandler(
+ const GURL& content_handler_url,
+ const GURL& requestor_url,
+ const std::string& qualifier,
+ InterfaceRequest<Application> application_request,
+ URLResponsePtr url_response) {
+ ContentHandlerConnection* connection = nullptr;
+ std::pair<GURL, std::string> key(content_handler_url, qualifier);
+ URLToContentHandlerMap::iterator iter = url_to_content_handler_.find(key);
+ if (iter != url_to_content_handler_.end()) {
+ connection = iter->second;
+ } else {
+ connection = new ContentHandlerConnection(this, content_handler_url,
+ requestor_url, qualifier);
+ url_to_content_handler_[key] = connection;
+ }
+
+ connection->content_handler()->StartApplication(application_request.Pass(),
+ url_response.Pass());
+}
+
+void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
+ const GURL& url) {
+ URLToLoaderMap::iterator it = url_to_loader_.find(url);
+ if (it != url_to_loader_.end())
+ delete it->second;
+ url_to_loader_[url] = loader.release();
+}
+
+void ApplicationManager::SetLoaderForScheme(
+ scoped_ptr<ApplicationLoader> loader,
+ const std::string& scheme) {
+ SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
+ if (it != scheme_to_loader_.end())
+ delete it->second;
+ scheme_to_loader_[scheme] = loader.release();
+}
+
+void ApplicationManager::SetNativeOptionsForURL(
+ const NativeRunnerFactory::Options& options,
+ const GURL& url) {
+ DCHECK(!url.has_query()); // Precondition.
+ // Apply mappings and resolution to get the resolved URL.
+ GURL resolved_url =
+ delegate_->ResolveMojoURL(delegate_->ResolveMappings(url));
+ DCHECK(!resolved_url.has_query()); // Still shouldn't have query.
+ // TODO(vtl): We should probably also remove/disregard the query string (and
+ // maybe canonicalize in other ways).
+ DVLOG(2) << "Storing native options for resolved URL " << resolved_url
+ << " (original URL " << url << ")";
+ url_to_native_options_[resolved_url] = options;
+}
+
+ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
+ auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
+ if (url_it != url_to_loader_.end())
+ return url_it->second;
+ auto scheme_it = scheme_to_loader_.find(url.scheme());
+ if (scheme_it != scheme_to_loader_.end())
+ return scheme_it->second;
+ return nullptr;
+}
+
+void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) {
+ // Called from ~ShellImpl, so we do not need to call Destroy here.
+ const Identity identity = shell_impl->identity();
+ base::Closure on_application_end = shell_impl->on_application_end();
+ // Remove the shell.
+ auto it = identity_to_shell_impl_.find(identity);
+ DCHECK(it != identity_to_shell_impl_.end());
+ delete it->second;
+ identity_to_shell_impl_.erase(it);
+ if (!on_application_end.is_null())
+ on_application_end.Run();
+}
+
+void ApplicationManager::OnContentHandlerError(
+ ContentHandlerConnection* content_handler) {
+ // Remove the mapping to the content handler.
+ auto it = url_to_content_handler_.find(
+ std::make_pair(content_handler->content_handler_url(),
+ content_handler->content_handler_qualifier()));
+ DCHECK(it != url_to_content_handler_.end());
+ delete it->second;
+ url_to_content_handler_.erase(it);
+}
+
+ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
+ const GURL& application_url,
+ const std::string& interface_name) {
+ ServiceProviderPtr services;
+ ConnectToApplication(application_url, GURL(), GetProxy(&services), nullptr,
+ base::Closure());
+ MessagePipe pipe;
+ services->ConnectToService(interface_name, pipe.handle1.Pass());
+ return pipe.handle0.Pass();
+}
+
+void ApplicationManager::CleanupRunner(NativeRunner* runner) {
+ native_runners_.erase(
+ std::find(native_runners_.begin(), native_runners_.end(), runner));
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/application_manager.h b/chromium/mojo/shell/application_manager.h
new file mode 100644
index 00000000000..a5ebda31844
--- /dev/null
+++ b/chromium/mojo/shell/application_manager.h
@@ -0,0 +1,261 @@
+// 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.
+
+#ifndef SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
+#define SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/application/public/interfaces/application.mojom.h"
+#include "mojo/application/public/interfaces/service_provider.mojom.h"
+#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/services/network/public/interfaces/network_service.mojom.h"
+#include "mojo/shell/application_loader.h"
+#include "mojo/shell/fetcher.h"
+#include "mojo/shell/identity.h"
+#include "mojo/shell/native_runner.h"
+#include "url/gurl.h"
+
+namespace base {
+class FilePath;
+class SequencedWorkerPool;
+}
+
+namespace mojo {
+namespace shell {
+
+class ShellImpl;
+
+class ApplicationManager {
+ public:
+ class Delegate {
+ public:
+ // Gives the delegate a chance to apply any mappings for the specified url.
+ // This should not resolve 'mojo' urls, that is done by ResolveMojoURL().
+ virtual GURL ResolveMappings(const GURL& url) = 0;
+
+ // Used to map a url with the scheme 'mojo' to the appropriate url. Return
+ // |url| if the scheme is not 'mojo'.
+ virtual GURL ResolveMojoURL(const GURL& url) = 0;
+
+ // Asks the delegate to create a Fetcher for the specified url. Return
+ // true on success, false if the default fetcher should be created.
+ virtual bool CreateFetcher(
+ const GURL& url,
+ const Fetcher::FetchCallback& loader_callback) = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ // API for testing.
+ class TestAPI {
+ public:
+ explicit TestAPI(ApplicationManager* manager);
+ ~TestAPI();
+
+ // Returns true if the shared instance has been created.
+ static bool HasCreatedInstance();
+ // Returns true if there is a ShellImpl for this URL.
+ bool HasFactoryForURL(const GURL& url) const;
+
+ private:
+ ApplicationManager* manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAPI);
+ };
+
+ explicit ApplicationManager(Delegate* delegate);
+ ~ApplicationManager();
+
+ // Loads a service if necessary and establishes a new client connection.
+ void ConnectToApplication(const GURL& application_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const base::Closure& on_application_end);
+
+ template <typename Interface>
+ inline void ConnectToService(const GURL& application_url,
+ InterfacePtr<Interface>* ptr) {
+ ScopedMessagePipeHandle service_handle =
+ ConnectToServiceByName(application_url, Interface::Name_);
+ ptr->Bind(InterfacePtrInfo<Interface>(service_handle.Pass(), 0u));
+ }
+
+ ScopedMessagePipeHandle ConnectToServiceByName(
+ const GURL& application_url,
+ const std::string& interface_name);
+
+ void RegisterContentHandler(const std::string& mime_type,
+ const GURL& content_handler_url);
+
+ // Registers a package alias. When attempting to load |alias|, it will
+ // instead redirect to |content_handler_package|, which is a content handler
+ // which will be passed the |alias| as the URLResponse::url. Different values
+ // of |alias| with the same |qualifier| that are in the same
+ // |content_handler_package| will run in the same process in multi-process
+ // mode.
+ void RegisterApplicationPackageAlias(const GURL& alias,
+ const GURL& content_handler_package,
+ const std::string& qualifier);
+
+ // Sets the default Loader to be used if not overridden by SetLoaderForURL()
+ // or SetLoaderForScheme().
+ void set_default_loader(scoped_ptr<ApplicationLoader> loader) {
+ default_loader_ = loader.Pass();
+ }
+ void set_native_runner_factory(
+ scoped_ptr<NativeRunnerFactory> runner_factory) {
+ native_runner_factory_ = runner_factory.Pass();
+ }
+ void set_blocking_pool(base::SequencedWorkerPool* blocking_pool) {
+ blocking_pool_ = blocking_pool;
+ }
+ void set_disable_cache(bool disable_cache) { disable_cache_ = disable_cache; }
+ // Sets a Loader to be used for a specific url.
+ void SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, const GURL& url);
+ // Sets a Loader to be used for a specific url scheme.
+ void SetLoaderForScheme(scoped_ptr<ApplicationLoader> loader,
+ const std::string& scheme);
+ // These options will be used in running any native application at |url|
+ // (which shouldn't contain a query string). (|url| will be mapped and
+ // resolved, and any application whose base resolved URL matches it will have
+ // |options| applied.)
+ // TODO(vtl): This may not do what's desired if the resolved URL results in an
+ // HTTP redirect. Really, we want options to be identified with a particular
+ // implementation, maybe via a signed manifest or something like that.
+ void SetNativeOptionsForURL(const NativeRunnerFactory::Options& options,
+ const GURL& url);
+
+ // Destroys all Shell-ends of connections established with Applications.
+ // Applications connected by this ApplicationManager will observe pipe errors
+ // and have a chance to shutdown.
+ void TerminateShellConnections();
+
+ // Removes a ShellImpl when it encounters an error.
+ void OnShellImplError(ShellImpl* shell_impl);
+
+ private:
+ class ContentHandlerConnection;
+
+ using ApplicationPackagedAlias = std::map<GURL, std::pair<GURL, std::string>>;
+ using IdentityToShellImplMap = std::map<Identity, ShellImpl*>;
+ using MimeTypeToURLMap = std::map<std::string, GURL>;
+ using SchemeToLoaderMap = std::map<std::string, ApplicationLoader*>;
+ using URLToContentHandlerMap =
+ std::map<std::pair<GURL, std::string>, ContentHandlerConnection*>;
+ using URLToLoaderMap = std::map<GURL, ApplicationLoader*>;
+ using URLToNativeOptionsMap = std::map<GURL, NativeRunnerFactory::Options>;
+
+ void ConnectToApplicationWithParameters(
+ const GURL& application_url,
+ const std::string& qualifier,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const base::Closure& on_application_end,
+ const std::vector<std::string>& pre_redirect_parameters);
+
+ bool ConnectToRunningApplication(const GURL& resolved_url,
+ const std::string& qualifier,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider>* services,
+ ServiceProviderPtr* exposed_services);
+
+ bool ConnectToApplicationWithLoader(
+ const GURL& requested_url,
+ const std::string& qualifier,
+ const GURL& resolved_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider>* services,
+ ServiceProviderPtr* exposed_services,
+ const base::Closure& on_application_end,
+ const std::vector<std::string>& parameters,
+ ApplicationLoader* loader);
+
+ InterfaceRequest<Application> RegisterShell(
+ const GURL& app_url,
+ const std::string& qualifier,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const base::Closure& on_application_end,
+ const std::vector<std::string>& parameters);
+
+ ShellImpl* GetShellImpl(const GURL& url, const std::string& qualifier);
+
+ void ConnectToClient(ShellImpl* shell_impl,
+ const GURL& resolved_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services);
+
+ // Called once |fetcher| has found app. |requested_url| is the url of the
+ // requested application before any mappings/resolution have been applied.
+ void HandleFetchCallback(const GURL& requested_url,
+ const std::string& qualifier,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ const base::Closure& on_application_end,
+ const std::vector<std::string>& parameters,
+ NativeApplicationCleanup cleanup,
+ scoped_ptr<Fetcher> fetcher);
+
+ void RunNativeApplication(InterfaceRequest<Application> application_request,
+ const NativeRunnerFactory::Options& options,
+ NativeApplicationCleanup cleanup,
+ scoped_ptr<Fetcher> fetcher,
+ const base::FilePath& file_path,
+ bool path_exists);
+
+ void LoadWithContentHandler(const GURL& content_handler_url,
+ const GURL& requestor_url,
+ const std::string& qualifier,
+ InterfaceRequest<Application> application_request,
+ URLResponsePtr url_response);
+
+ // Returns the appropriate loader for |url|, or null if there is no loader
+ // configured for the URL.
+ ApplicationLoader* GetLoaderForURL(const GURL& url);
+
+ // Removes a ContentHandler when it encounters an error.
+ void OnContentHandlerError(ContentHandlerConnection* content_handler);
+
+ void CleanupRunner(NativeRunner* runner);
+
+ Delegate* const delegate_;
+ // Loader management.
+ // Loaders are chosen in the order they are listed here.
+ URLToLoaderMap url_to_loader_;
+ SchemeToLoaderMap scheme_to_loader_;
+ scoped_ptr<ApplicationLoader> default_loader_;
+ scoped_ptr<NativeRunnerFactory> native_runner_factory_;
+
+ ApplicationPackagedAlias application_package_alias_;
+ IdentityToShellImplMap identity_to_shell_impl_;
+ URLToContentHandlerMap url_to_content_handler_;
+ // Note: The keys are URLs after mapping and resolving.
+ URLToNativeOptionsMap url_to_native_options_;
+
+ base::SequencedWorkerPool* blocking_pool_;
+ NetworkServicePtr network_service_;
+ MimeTypeToURLMap mime_type_to_url_;
+ ScopedVector<NativeRunner> native_runners_;
+ bool disable_cache_;
+ base::WeakPtrFactory<ApplicationManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ApplicationManager);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
diff --git a/chromium/mojo/shell/application_manager_unittest.cc b/chromium/mojo/shell/application_manager_unittest.cc
new file mode 100644
index 00000000000..e08420dd979
--- /dev/null
+++ b/chromium/mojo/shell/application_manager_unittest.cc
@@ -0,0 +1,824 @@
+// 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 "base/at_exit.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/application/public/cpp/application_connection.h"
+#include "mojo/application/public/cpp/application_delegate.h"
+#include "mojo/application/public/cpp/application_impl.h"
+#include "mojo/application/public/cpp/interface_factory.h"
+#include "mojo/application/public/interfaces/service_provider.mojom.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/shell/application_loader.h"
+#include "mojo/shell/application_manager.h"
+#include "mojo/shell/fetcher.h"
+#include "mojo/shell/test.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+const char kTestURLString[] = "test:testService";
+const char kTestAURLString[] = "test:TestA";
+const char kTestBURLString[] = "test:TestB";
+
+const char kTestMimeType[] = "test/mime-type";
+
+class TestMimeTypeFetcher : public Fetcher {
+ public:
+ explicit TestMimeTypeFetcher(const FetchCallback& fetch_callback)
+ : Fetcher(fetch_callback), url_("xxx") {
+ loader_callback_.Run(make_scoped_ptr(this));
+ }
+ ~TestMimeTypeFetcher() override {}
+
+ // Fetcher:
+ const GURL& GetURL() const override { return url_; }
+ GURL GetRedirectURL() const override { return GURL("yyy"); }
+ URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
+ uint32_t skip) override {
+ return URLResponse::New().Pass();
+ }
+ void AsPath(
+ base::TaskRunner* task_runner,
+ base::Callback<void(const base::FilePath&, bool)> callback) override {}
+ std::string MimeType() override { return kTestMimeType; }
+ bool HasMojoMagic() override { return false; }
+ bool PeekFirstLine(std::string* line) override { return false; }
+
+ private:
+ const GURL url_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMimeTypeFetcher);
+};
+
+struct TestContext {
+ TestContext() : num_impls(0), num_loader_deletes(0) {}
+ std::string last_test_string;
+ int num_impls;
+ int num_loader_deletes;
+};
+
+void QuitClosure(bool* value) {
+ *value = true;
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+class QuitMessageLoopErrorHandler : public ErrorHandler {
+ public:
+ QuitMessageLoopErrorHandler() {}
+ ~QuitMessageLoopErrorHandler() override {}
+
+ // |ErrorHandler| implementation:
+ void OnConnectionError() override {
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler);
+};
+
+class TestServiceImpl : public TestService {
+ public:
+ TestServiceImpl(TestContext* context, InterfaceRequest<TestService> request)
+ : context_(context), binding_(this, request.Pass()) {
+ ++context_->num_impls;
+ }
+
+ ~TestServiceImpl() override {
+ --context_->num_impls;
+ if (!base::MessageLoop::current()->is_running())
+ return;
+ base::MessageLoop::current()->Quit();
+ }
+
+ // TestService implementation:
+ void Test(const String& test_string,
+ const Callback<void()>& callback) override {
+ context_->last_test_string = test_string;
+ callback.Run();
+ }
+
+ private:
+ TestContext* context_;
+ StrongBinding<TestService> binding_;
+};
+
+class TestClient {
+ public:
+ explicit TestClient(TestServicePtr service)
+ : service_(service.Pass()), quit_after_ack_(false) {}
+
+ void AckTest() {
+ if (quit_after_ack_)
+ base::MessageLoop::current()->Quit();
+ }
+
+ void Test(const std::string& test_string) {
+ quit_after_ack_ = true;
+ service_->Test(test_string,
+ base::Bind(&TestClient::AckTest, base::Unretained(this)));
+ }
+
+ private:
+ TestServicePtr service_;
+ bool quit_after_ack_;
+ DISALLOW_COPY_AND_ASSIGN(TestClient);
+};
+
+class TestApplicationLoader : public ApplicationLoader,
+ public ApplicationDelegate,
+ public InterfaceFactory<TestService> {
+ public:
+ TestApplicationLoader() : context_(nullptr), num_loads_(0) {}
+
+ ~TestApplicationLoader() override {
+ if (context_)
+ ++context_->num_loader_deletes;
+ test_app_.reset();
+ }
+
+ void set_context(TestContext* context) { context_ = context; }
+ int num_loads() const { return num_loads_; }
+ const std::vector<std::string>& GetArgs() const { return test_app_->args(); }
+ const GURL& last_requestor_url() const { return last_requestor_url_; }
+
+ private:
+ // ApplicationLoader implementation.
+ void Load(const GURL& url,
+ InterfaceRequest<Application> application_request) override {
+ ++num_loads_;
+ test_app_.reset(new ApplicationImpl(this, application_request.Pass()));
+ }
+
+ // ApplicationDelegate implementation.
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+ connection->AddService(this);
+ last_requestor_url_ = GURL(connection->GetRemoteApplicationURL());
+ return true;
+ }
+
+ // InterfaceFactory implementation.
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestService> request) override {
+ new TestServiceImpl(context_, request.Pass());
+ }
+
+ scoped_ptr<ApplicationImpl> test_app_;
+ TestContext* context_;
+ int num_loads_;
+ GURL last_requestor_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader);
+};
+
+class ClosingApplicationLoader : public ApplicationLoader {
+ private:
+ // ApplicationLoader implementation.
+ void Load(const GURL& url,
+ InterfaceRequest<Application> application_request) override {}
+};
+
+class TesterContext {
+ public:
+ explicit TesterContext(base::MessageLoop* loop)
+ : num_b_calls_(0),
+ num_c_calls_(0),
+ num_a_deletes_(0),
+ num_b_deletes_(0),
+ num_c_deletes_(0),
+ tester_called_quit_(false),
+ a_called_quit_(false),
+ loop_(loop) {}
+
+ void IncrementNumBCalls() {
+ base::AutoLock lock(lock_);
+ num_b_calls_++;
+ }
+
+ void IncrementNumCCalls() {
+ base::AutoLock lock(lock_);
+ num_c_calls_++;
+ }
+
+ void IncrementNumADeletes() {
+ base::AutoLock lock(lock_);
+ num_a_deletes_++;
+ }
+
+ void IncrementNumBDeletes() {
+ base::AutoLock lock(lock_);
+ num_b_deletes_++;
+ }
+
+ void IncrementNumCDeletes() {
+ base::AutoLock lock(lock_);
+ num_c_deletes_++;
+ }
+
+ void set_tester_called_quit() {
+ base::AutoLock lock(lock_);
+ tester_called_quit_ = true;
+ }
+
+ void set_a_called_quit() {
+ base::AutoLock lock(lock_);
+ a_called_quit_ = true;
+ }
+
+ int num_b_calls() {
+ base::AutoLock lock(lock_);
+ return num_b_calls_;
+ }
+ int num_c_calls() {
+ base::AutoLock lock(lock_);
+ return num_c_calls_;
+ }
+ int num_a_deletes() {
+ base::AutoLock lock(lock_);
+ return num_a_deletes_;
+ }
+ int num_b_deletes() {
+ base::AutoLock lock(lock_);
+ return num_b_deletes_;
+ }
+ int num_c_deletes() {
+ base::AutoLock lock(lock_);
+ return num_c_deletes_;
+ }
+ bool tester_called_quit() {
+ base::AutoLock lock(lock_);
+ return tester_called_quit_;
+ }
+ bool a_called_quit() {
+ base::AutoLock lock(lock_);
+ return a_called_quit_;
+ }
+
+ void QuitSoon() {
+ loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+ }
+
+ private:
+ // lock_ protects all members except for loop_ which must be unchanged for the
+ // lifetime of this class.
+ base::Lock lock_;
+ int num_b_calls_;
+ int num_c_calls_;
+ int num_a_deletes_;
+ int num_b_deletes_;
+ int num_c_deletes_;
+ bool tester_called_quit_;
+ bool a_called_quit_;
+
+ base::MessageLoop* loop_;
+};
+
+// Used to test that the requestor url will be correctly passed.
+class TestAImpl : public TestA {
+ public:
+ TestAImpl(ApplicationImpl* app_impl,
+ TesterContext* test_context,
+ InterfaceRequest<TestA> request)
+ : test_context_(test_context), binding_(this, request.Pass()) {
+ app_impl->ConnectToApplication(kTestBURLString)->ConnectToService(&b_);
+ }
+
+ ~TestAImpl() override {
+ test_context_->IncrementNumADeletes();
+ if (base::MessageLoop::current()->is_running())
+ Quit();
+ }
+
+ private:
+ void CallB() override {
+ b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this)));
+ }
+
+ void CallCFromB() override {
+ b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this)));
+ }
+
+ void Quit() {
+ base::MessageLoop::current()->Quit();
+ test_context_->set_a_called_quit();
+ test_context_->QuitSoon();
+ }
+
+ TesterContext* test_context_;
+ TestBPtr b_;
+ StrongBinding<TestA> binding_;
+};
+
+class TestBImpl : public TestB {
+ public:
+ TestBImpl(ApplicationConnection* connection,
+ TesterContext* test_context,
+ InterfaceRequest<TestB> request)
+ : test_context_(test_context), binding_(this, request.Pass()) {
+ connection->ConnectToService(&c_);
+ }
+
+ ~TestBImpl() override {
+ test_context_->IncrementNumBDeletes();
+ if (base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ test_context_->QuitSoon();
+ }
+
+ private:
+ void B(const Callback<void()>& callback) override {
+ test_context_->IncrementNumBCalls();
+ callback.Run();
+ }
+
+ void CallC(const Callback<void()>& callback) override {
+ test_context_->IncrementNumBCalls();
+ c_->C(callback);
+ }
+
+ TesterContext* test_context_;
+ TestCPtr c_;
+ StrongBinding<TestB> binding_;
+};
+
+class TestCImpl : public TestC {
+ public:
+ TestCImpl(ApplicationConnection* connection,
+ TesterContext* test_context,
+ InterfaceRequest<TestC> request)
+ : test_context_(test_context), binding_(this, request.Pass()) {}
+
+ ~TestCImpl() override { test_context_->IncrementNumCDeletes(); }
+
+ private:
+ void C(const Callback<void()>& callback) override {
+ test_context_->IncrementNumCCalls();
+ callback.Run();
+ }
+
+ TesterContext* test_context_;
+ StrongBinding<TestC> binding_;
+};
+
+class Tester : public ApplicationDelegate,
+ public ApplicationLoader,
+ public InterfaceFactory<TestA>,
+ public InterfaceFactory<TestB>,
+ public InterfaceFactory<TestC> {
+ public:
+ Tester(TesterContext* context, const std::string& requestor_url)
+ : context_(context), requestor_url_(requestor_url) {}
+ ~Tester() override {}
+
+ private:
+ void Load(const GURL& url,
+ InterfaceRequest<Application> application_request) override {
+ app_.reset(new ApplicationImpl(this, application_request.Pass()));
+ }
+
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+ if (!requestor_url_.empty() &&
+ requestor_url_ != connection->GetRemoteApplicationURL()) {
+ context_->set_tester_called_quit();
+ context_->QuitSoon();
+ base::MessageLoop::current()->Quit();
+ return false;
+ }
+ // If we're coming from A, then add B, otherwise A.
+ if (connection->GetRemoteApplicationURL() == kTestAURLString)
+ connection->AddService<TestB>(this);
+ else
+ connection->AddService<TestA>(this);
+ return true;
+ }
+
+ bool ConfigureOutgoingConnection(ApplicationConnection* connection) override {
+ // If we're connecting to B, then add C.
+ if (connection->GetRemoteApplicationURL() == kTestBURLString)
+ connection->AddService<TestC>(this);
+ return true;
+ }
+
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestA> request) override {
+ a_bindings_.push_back(new TestAImpl(app_.get(), context_, request.Pass()));
+ }
+
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestB> request) override {
+ new TestBImpl(connection, context_, request.Pass());
+ }
+
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestC> request) override {
+ new TestCImpl(connection, context_, request.Pass());
+ }
+
+ TesterContext* context_;
+ scoped_ptr<ApplicationImpl> app_;
+ std::string requestor_url_;
+ ScopedVector<TestAImpl> a_bindings_;
+};
+
+class TestDelegate : public ApplicationManager::Delegate {
+ public:
+ TestDelegate() : create_test_fetcher_(false) {}
+ ~TestDelegate() override {}
+
+ void AddMapping(const GURL& from, const GURL& to) { mappings_[from] = to; }
+
+ void set_create_test_fetcher(bool create_test_fetcher) {
+ create_test_fetcher_ = create_test_fetcher;
+ }
+
+ // ApplicationManager::Delegate
+ GURL ResolveMappings(const GURL& url) override {
+ auto it = mappings_.find(url);
+ if (it != mappings_.end())
+ return it->second;
+ return url;
+ }
+ GURL ResolveMojoURL(const GURL& url) override {
+ GURL mapped_url = ResolveMappings(url);
+ // The shell automatically map mojo URLs.
+ if (mapped_url.scheme() == "mojo") {
+ url::Replacements<char> replacements;
+ replacements.SetScheme("file", url::Component(0, 4));
+ mapped_url = mapped_url.ReplaceComponents(replacements);
+ }
+ return mapped_url;
+ }
+ bool CreateFetcher(const GURL& url,
+ const Fetcher::FetchCallback& loader_callback) override {
+ if (!create_test_fetcher_)
+ return false;
+ new TestMimeTypeFetcher(loader_callback);
+ return true;
+ }
+
+ private:
+ std::map<GURL, GURL> mappings_;
+ bool create_test_fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDelegate);
+};
+
+class ApplicationManagerTest : public testing::Test {
+ public:
+ ApplicationManagerTest() : tester_context_(&loop_) {}
+
+ ~ApplicationManagerTest() override {}
+
+ void SetUp() override {
+ application_manager_.reset(new ApplicationManager(&test_delegate_));
+ test_loader_ = new TestApplicationLoader;
+ test_loader_->set_context(&context_);
+ application_manager_->set_default_loader(
+ scoped_ptr<ApplicationLoader>(test_loader_));
+
+ TestServicePtr service_proxy;
+ application_manager_->ConnectToService(GURL(kTestURLString),
+ &service_proxy);
+ test_client_.reset(new TestClient(service_proxy.Pass()));
+ }
+
+ void TearDown() override {
+ test_client_.reset();
+ application_manager_.reset();
+ }
+
+ void AddLoaderForURL(const GURL& url, const std::string& requestor_url) {
+ application_manager_->SetLoaderForURL(
+ make_scoped_ptr(new Tester(&tester_context_, requestor_url)), url);
+ }
+
+ bool HasFactoryForURL(const GURL& url) {
+ ApplicationManager::TestAPI manager_test_api(application_manager_.get());
+ return manager_test_api.HasFactoryForURL(url);
+ }
+
+ protected:
+ base::ShadowingAtExitManager at_exit_;
+ TestDelegate test_delegate_;
+ TestApplicationLoader* test_loader_;
+ TesterContext tester_context_;
+ TestContext context_;
+ base::MessageLoop loop_;
+ scoped_ptr<TestClient> test_client_;
+ scoped_ptr<ApplicationManager> application_manager_;
+ DISALLOW_COPY_AND_ASSIGN(ApplicationManagerTest);
+};
+
+TEST_F(ApplicationManagerTest, Basic) {
+ test_client_->Test("test");
+ loop_.Run();
+ EXPECT_EQ(std::string("test"), context_.last_test_string);
+}
+
+// Confirm that no arguments are sent to an application by default.
+TEST_F(ApplicationManagerTest, NoArgs) {
+ ApplicationManager am(&test_delegate_);
+ GURL test_url("test:test");
+ TestApplicationLoader* loader = new TestApplicationLoader;
+ loader->set_context(&context_);
+ am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url);
+ TestServicePtr test_service;
+ am.ConnectToService(test_url, &test_service);
+ TestClient test_client(test_service.Pass());
+ test_client.Test("test");
+ loop_.Run();
+ std::vector<std::string> app_args = loader->GetArgs();
+ EXPECT_EQ(0U, app_args.size());
+}
+
+// Confirm that url mappings are respected.
+TEST_F(ApplicationManagerTest, URLMapping) {
+ ApplicationManager am(&test_delegate_);
+ GURL test_url("test:test");
+ GURL test_url2("test:test2");
+ test_delegate_.AddMapping(test_url, test_url2);
+ TestApplicationLoader* loader = new TestApplicationLoader;
+ loader->set_context(&context_);
+ am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url2);
+ {
+ // Connext to the mapped url
+ TestServicePtr test_service;
+ am.ConnectToService(test_url, &test_service);
+ TestClient test_client(test_service.Pass());
+ test_client.Test("test");
+ loop_.Run();
+ }
+ {
+ // Connext to the target url
+ TestServicePtr test_service;
+ am.ConnectToService(test_url2, &test_service);
+ TestClient test_client(test_service.Pass());
+ test_client.Test("test");
+ loop_.Run();
+ }
+}
+
+TEST_F(ApplicationManagerTest, ClientError) {
+ test_client_->Test("test");
+ EXPECT_TRUE(HasFactoryForURL(GURL(kTestURLString)));
+ loop_.Run();
+ EXPECT_EQ(1, context_.num_impls);
+ test_client_.reset();
+ loop_.Run();
+ EXPECT_EQ(0, context_.num_impls);
+ EXPECT_TRUE(HasFactoryForURL(GURL(kTestURLString)));
+}
+
+TEST_F(ApplicationManagerTest, Deletes) {
+ {
+ ApplicationManager am(&test_delegate_);
+ TestApplicationLoader* default_loader = new TestApplicationLoader;
+ default_loader->set_context(&context_);
+ TestApplicationLoader* url_loader1 = new TestApplicationLoader;
+ TestApplicationLoader* url_loader2 = new TestApplicationLoader;
+ url_loader1->set_context(&context_);
+ url_loader2->set_context(&context_);
+ TestApplicationLoader* scheme_loader1 = new TestApplicationLoader;
+ TestApplicationLoader* scheme_loader2 = new TestApplicationLoader;
+ scheme_loader1->set_context(&context_);
+ scheme_loader2->set_context(&context_);
+ am.set_default_loader(scoped_ptr<ApplicationLoader>(default_loader));
+ am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader1),
+ GURL("test:test1"));
+ am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader2),
+ GURL("test:test1"));
+ am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader1),
+ "test");
+ am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader2),
+ "test");
+ }
+ EXPECT_EQ(5, context_.num_loader_deletes);
+}
+
+// Confirm that both urls and schemes can have their loaders explicitly set.
+TEST_F(ApplicationManagerTest, SetLoaders) {
+ TestApplicationLoader* default_loader = new TestApplicationLoader;
+ TestApplicationLoader* url_loader = new TestApplicationLoader;
+ TestApplicationLoader* scheme_loader = new TestApplicationLoader;
+ application_manager_->set_default_loader(
+ scoped_ptr<ApplicationLoader>(default_loader));
+ application_manager_->SetLoaderForURL(
+ scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1"));
+ application_manager_->SetLoaderForScheme(
+ scoped_ptr<ApplicationLoader>(scheme_loader), "test");
+
+ // test::test1 should go to url_loader.
+ TestServicePtr test_service;
+ application_manager_->ConnectToService(GURL("test:test1"), &test_service);
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(0, scheme_loader->num_loads());
+ EXPECT_EQ(0, default_loader->num_loads());
+
+ // test::test2 should go to scheme loader.
+ application_manager_->ConnectToService(GURL("test:test2"), &test_service);
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(1, scheme_loader->num_loads());
+ EXPECT_EQ(0, default_loader->num_loads());
+
+ // http::test1 should go to default loader.
+ application_manager_->ConnectToService(GURL("http:test1"), &test_service);
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(1, scheme_loader->num_loads());
+ EXPECT_EQ(1, default_loader->num_loads());
+}
+
+// Confirm that the url of a service is correctly passed to another service that
+// it loads.
+TEST_F(ApplicationManagerTest, ACallB) {
+ // Any url can load a.
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+ // Only a can load b.
+ AddLoaderForURL(GURL(kTestBURLString), kTestAURLString);
+
+ TestAPtr a;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+ a->CallB();
+ loop_.Run();
+ EXPECT_EQ(1, tester_context_.num_b_calls());
+ EXPECT_TRUE(tester_context_.a_called_quit());
+}
+
+// A calls B which calls C.
+TEST_F(ApplicationManagerTest, BCallC) {
+ // Any url can load a.
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+ // Only a can load b.
+ AddLoaderForURL(GURL(kTestBURLString), kTestAURLString);
+
+ TestAPtr a;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+ a->CallCFromB();
+ loop_.Run();
+
+ EXPECT_EQ(1, tester_context_.num_b_calls());
+ EXPECT_EQ(1, tester_context_.num_c_calls());
+ EXPECT_TRUE(tester_context_.a_called_quit());
+}
+
+// Confirm that a service impl will be deleted if the app that connected to
+// it goes away.
+TEST_F(ApplicationManagerTest, BDeleted) {
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+ AddLoaderForURL(GURL(kTestBURLString), std::string());
+
+ TestAPtr a;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+
+ a->CallB();
+ loop_.Run();
+
+ // Kills the a app.
+ application_manager_->SetLoaderForURL(scoped_ptr<ApplicationLoader>(),
+ GURL(kTestAURLString));
+ loop_.Run();
+
+ EXPECT_EQ(1, tester_context_.num_b_deletes());
+}
+
+// Confirm that the url of a service is correctly passed to another service that
+// it loads, and that it can be rejected.
+TEST_F(ApplicationManagerTest, ANoLoadB) {
+ // Any url can load a.
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+ // Only c can load b, so this will fail.
+ AddLoaderForURL(GURL(kTestBURLString), "test:TestC");
+
+ TestAPtr a;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+ a->CallB();
+ loop_.Run();
+ EXPECT_EQ(0, tester_context_.num_b_calls());
+
+ EXPECT_FALSE(tester_context_.a_called_quit());
+ EXPECT_TRUE(tester_context_.tester_called_quit());
+}
+
+TEST_F(ApplicationManagerTest, NoServiceNoLoad) {
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+ // There is no TestC service implementation registered with
+ // ApplicationManager, so this cannot succeed (but also shouldn't crash).
+ TestCPtr c;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &c);
+ QuitMessageLoopErrorHandler quitter;
+ c.set_error_handler(&quitter);
+
+ loop_.Run();
+ EXPECT_TRUE(c.encountered_error());
+}
+
+TEST_F(ApplicationManagerTest, MappedURLsShouldNotCauseDuplicateLoad) {
+ test_delegate_.AddMapping(GURL("foo:foo2"), GURL("foo:foo"));
+ // 1 because ApplicationManagerTest connects once at startup.
+ EXPECT_EQ(1, test_loader_->num_loads());
+
+ TestServicePtr test_service;
+ application_manager_->ConnectToService(GURL("foo:foo"), &test_service);
+ EXPECT_EQ(2, test_loader_->num_loads());
+
+ TestServicePtr test_service2;
+ application_manager_->ConnectToService(GURL("foo:foo2"), &test_service2);
+ EXPECT_EQ(2, test_loader_->num_loads());
+
+ TestServicePtr test_service3;
+ application_manager_->ConnectToService(GURL("bar:bar"), &test_service2);
+ EXPECT_EQ(3, test_loader_->num_loads());
+}
+
+TEST_F(ApplicationManagerTest, MappedURLsShouldWorkWithLoaders) {
+ TestApplicationLoader* custom_loader = new TestApplicationLoader;
+ TestContext context;
+ custom_loader->set_context(&context);
+ application_manager_->SetLoaderForURL(make_scoped_ptr(custom_loader),
+ GURL("mojo:foo"));
+ test_delegate_.AddMapping(GURL("mojo:foo2"), GURL("mojo:foo"));
+
+ TestServicePtr test_service;
+ application_manager_->ConnectToService(GURL("mojo:foo2"), &test_service);
+ EXPECT_EQ(1, custom_loader->num_loads());
+ custom_loader->set_context(nullptr);
+
+ EXPECT_TRUE(HasFactoryForURL(GURL("mojo:foo2")));
+ EXPECT_FALSE(HasFactoryForURL(GURL("mojo:foo")));
+}
+
+TEST_F(ApplicationManagerTest, TestQueryWithLoaders) {
+ TestApplicationLoader* url_loader = new TestApplicationLoader;
+ TestApplicationLoader* scheme_loader = new TestApplicationLoader;
+ application_manager_->SetLoaderForURL(
+ scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1"));
+ application_manager_->SetLoaderForScheme(
+ scoped_ptr<ApplicationLoader>(scheme_loader), "test");
+
+ // test::test1 should go to url_loader.
+ TestServicePtr test_service;
+ application_manager_->ConnectToService(GURL("test:test1?foo=bar"),
+ &test_service);
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(0, scheme_loader->num_loads());
+
+ // test::test2 should go to scheme loader.
+ application_manager_->ConnectToService(GURL("test:test2?foo=bar"),
+ &test_service);
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(1, scheme_loader->num_loads());
+}
+
+TEST_F(ApplicationManagerTest, TestEndApplicationClosure) {
+ ClosingApplicationLoader* loader = new ClosingApplicationLoader();
+ application_manager_->SetLoaderForScheme(
+ scoped_ptr<ApplicationLoader>(loader), "test");
+
+ bool called = false;
+ application_manager_->ConnectToApplication(
+ GURL("test:test"), GURL(), nullptr, nullptr,
+ base::Bind(&QuitClosure, base::Unretained(&called)));
+ loop_.Run();
+ EXPECT_TRUE(called);
+}
+
+TEST(ApplicationManagerTest2, ContentHandlerConnectionGetsRequestorURL) {
+ const GURL content_handler_url("http://test.content.handler");
+ const GURL requestor_url("http://requestor.url");
+ TestContext test_context;
+ base::MessageLoop loop;
+ TestDelegate test_delegate;
+ test_delegate.set_create_test_fetcher(true);
+ ApplicationManager application_manager(&test_delegate);
+ application_manager.set_default_loader(nullptr);
+ application_manager.RegisterContentHandler(kTestMimeType,
+ content_handler_url);
+
+ TestApplicationLoader* loader = new TestApplicationLoader;
+ loader->set_context(&test_context);
+ application_manager.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader),
+ content_handler_url);
+
+ bool called = false;
+ application_manager.ConnectToApplication(
+ GURL("test:test"), requestor_url, nullptr, nullptr,
+ base::Bind(&QuitClosure, base::Unretained(&called)));
+ loop.Run();
+ EXPECT_TRUE(called);
+
+ ASSERT_EQ(1, loader->num_loads());
+ EXPECT_EQ(requestor_url, loader->last_requestor_url());
+}
+
+} // namespace
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/data_pipe_peek.cc b/chromium/mojo/shell/data_pipe_peek.cc
new file mode 100644
index 00000000000..4c74c5537bb
--- /dev/null
+++ b/chromium/mojo/shell/data_pipe_peek.cc
@@ -0,0 +1,160 @@
+// 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 "mojo/shell/data_pipe_peek.h"
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/macros.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+// Sleep for as long as max_sleep_micros if the deadline hasn't been reached
+// and the number of bytes read is still increasing. Returns true if sleep
+// was actually called.
+//
+// This class is a substitute for being able to wait until N bytes are available
+// from a data pipe. The MaybeSleep method is called when num_bytes_read are
+// available but more are needed by the Peek operation. If a second
+// Peek operation finds the same number of bytes after sleeping we assume
+// that there's no point in trying again.
+// TODO(hansmuller): this heuristic is weak. crbug.com/429377
+class PeekSleeper {
+ public:
+ explicit PeekSleeper(MojoTimeTicks deadline)
+ : deadline_(deadline),
+ last_number_bytes_read_(0) {}
+
+ bool MaybeSleep(uint32_t num_bytes_read) {
+ if (num_bytes_read > 0 && last_number_bytes_read_ >= num_bytes_read)
+ return false;
+ last_number_bytes_read_ = num_bytes_read;
+
+ MojoTimeTicks now(GetTimeTicksNow());
+ if (now > deadline_)
+ return false;
+
+ MojoTimeTicks sleep_time =
+ (deadline_ == 0) ? kMaxSleepMicros
+ : std::min<int64>(deadline_ - now, kMaxSleepMicros);
+ base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(sleep_time));
+ return true;
+ }
+
+ private:
+ static const MojoTimeTicks kMaxSleepMicros = 1000 * 10; // 10 ms
+
+ const MojoTimeTicks deadline_; // 0 => MOJO_DEADLINE_INDEFINITE
+ uint32_t last_number_bytes_read_;
+
+ DISALLOW_COPY_AND_ASSIGN(PeekSleeper);
+};
+
+const MojoTimeTicks PeekSleeper::kMaxSleepMicros;
+
+enum PeekStatus { kSuccess, kFail, kKeepReading };
+typedef const base::Callback<PeekStatus(const void*, uint32_t, std::string*)>&
+ PeekFunc;
+
+// When data is available on source, call peek_func and then either return true
+// and value, continue waiting for enough data to satisfy peek_func, or fail
+// and return false. Fail if the timeout is exceeded.
+// This function is not guaranteed to work correctly if applied to a data pipe
+// that's already been read from.
+bool BlockingPeekHelper(DataPipeConsumerHandle source,
+ std::string* value,
+ MojoDeadline timeout,
+ PeekFunc peek_func) {
+ DCHECK(value);
+ value->clear();
+
+ MojoTimeTicks deadline =
+ (timeout == MOJO_DEADLINE_INDEFINITE)
+ ? 0
+ : 1 + GetTimeTicksNow() + static_cast<MojoTimeTicks>(timeout);
+ PeekSleeper sleeper(deadline);
+
+ MojoResult result;
+ do {
+ const void* buffer;
+ uint32_t num_bytes;
+ result =
+ BeginReadDataRaw(source, &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
+
+ if (result == MOJO_RESULT_OK) {
+ PeekStatus status = peek_func.Run(buffer, num_bytes, value);
+ CHECK_EQ(EndReadDataRaw(source, 0), MOJO_RESULT_OK);
+ switch (status) {
+ case PeekStatus::kSuccess:
+ return true;
+ case PeekStatus::kFail:
+ return false;
+ case PeekStatus::kKeepReading:
+ break;
+ }
+ if (!sleeper.MaybeSleep(num_bytes))
+ return false;
+ } else if (result == MOJO_RESULT_SHOULD_WAIT) {
+ MojoTimeTicks now(GetTimeTicksNow());
+ if (timeout == MOJO_DEADLINE_INDEFINITE || now < deadline)
+ result =
+ Wait(source, MOJO_HANDLE_SIGNAL_READABLE, deadline - now, nullptr);
+ }
+ } while (result == MOJO_RESULT_OK);
+
+ return false;
+}
+
+PeekStatus PeekLine(size_t max_line_length,
+ const void* buffer,
+ uint32_t buffer_num_bytes,
+ std::string* line) {
+ const char* p = static_cast<const char*>(buffer);
+ size_t max_p_index = std::min<size_t>(buffer_num_bytes, max_line_length);
+ for (size_t i = 0; i < max_p_index; i++) {
+ if (p[i] == '\n') {
+ *line = std::string(p, i + 1); // Include the trailing newline.
+ return PeekStatus::kSuccess;
+ }
+ }
+ return (buffer_num_bytes >= max_line_length) ? PeekStatus::kFail
+ : PeekStatus::kKeepReading;
+}
+
+PeekStatus PeekNBytes(size_t bytes_length,
+ const void* buffer,
+ uint32_t buffer_num_bytes,
+ std::string* bytes) {
+ if (buffer_num_bytes >= bytes_length) {
+ const char* p = static_cast<const char*>(buffer);
+ *bytes = std::string(p, bytes_length);
+ return PeekStatus::kSuccess;
+ }
+ return PeekStatus::kKeepReading;
+}
+
+} // namespace
+
+bool BlockingPeekNBytes(DataPipeConsumerHandle source,
+ std::string* bytes,
+ size_t bytes_length,
+ MojoDeadline timeout) {
+ PeekFunc peek_nbytes = base::Bind(PeekNBytes, bytes_length);
+ return BlockingPeekHelper(source, bytes, timeout, peek_nbytes);
+}
+
+bool BlockingPeekLine(DataPipeConsumerHandle source,
+ std::string* line,
+ size_t max_line_length,
+ MojoDeadline timeout) {
+ PeekFunc peek_line = base::Bind(PeekLine, max_line_length);
+ return BlockingPeekHelper(source, line, timeout, peek_line);
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/data_pipe_peek.h b/chromium/mojo/shell/data_pipe_peek.h
new file mode 100644
index 00000000000..0bcf1dd9f29
--- /dev/null
+++ b/chromium/mojo/shell/data_pipe_peek.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef MOJO_SHELL_DATA_PIPE_PEEK_H_
+#define MOJO_SHELL_DATA_PIPE_PEEK_H_
+
+#include <string>
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace shell {
+
+// The Peek functions are only intended to be used by the
+// DyanmicApplicationLoader class for discovering the type of a
+// URL response. They are a stopgap to be replaced by real support
+// in the DataPipe classes.
+
+// Return true and the first newline terminated line from source. Return false
+// if more than max_line_length bytes are scanned without seeing a newline, or
+// if the timeout is exceeded.
+bool BlockingPeekLine(DataPipeConsumerHandle source,
+ std::string* line,
+ size_t max_line_length,
+ MojoDeadline timeout);
+
+// Return true and the first bytes_length bytes from source. Return false
+// if the timeout is exceeded.
+bool BlockingPeekNBytes(DataPipeConsumerHandle source,
+ std::string* bytes,
+ size_t bytes_length,
+ MojoDeadline timeout);
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_DATA_PIPE_PEEK_H_
diff --git a/chromium/mojo/shell/fetcher.cc b/chromium/mojo/shell/fetcher.cc
new file mode 100644
index 00000000000..d9a4e60574b
--- /dev/null
+++ b/chromium/mojo/shell/fetcher.cc
@@ -0,0 +1,38 @@
+// 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 "mojo/shell/fetcher.h"
+
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+const char Fetcher::kMojoMagic[] = "#!mojo ";
+const size_t Fetcher::kMaxShebangLength = 2048;
+
+Fetcher::Fetcher(const FetchCallback& loader_callback)
+ : loader_callback_(loader_callback) {
+}
+
+Fetcher::~Fetcher() {
+}
+
+bool Fetcher::PeekContentHandler(std::string* mojo_shebang,
+ GURL* mojo_content_handler_url) {
+ // TODO(aa): I guess this should just go in ApplicationManager now.
+ std::string shebang;
+ if (HasMojoMagic() && PeekFirstLine(&shebang)) {
+ GURL url(shebang.substr(arraysize(kMojoMagic) - 1, std::string::npos));
+ if (url.is_valid()) {
+ *mojo_shebang = shebang;
+ *mojo_content_handler_url = url;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/fetcher.h b/chromium/mojo/shell/fetcher.h
new file mode 100644
index 00000000000..21cad3f2252
--- /dev/null
+++ b/chromium/mojo/shell/fetcher.h
@@ -0,0 +1,76 @@
+// 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.
+
+#ifndef MOJO_SHELL_FETCHER_H_
+#define MOJO_SHELL_FETCHER_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+
+#include "mojo/services/network/public/interfaces/url_loader.mojom.h"
+
+class GURL;
+
+namespace base {
+class FilePath;
+class TaskRunner;
+}
+
+namespace mojo {
+namespace shell {
+
+// Fetcher abstracts getting an application by either file or http[s] URL.
+//
+// Although it is possible to use the Network implementation for http[s] URLs
+// (because the underlying net library knows how to handle them), it is
+// extremely slow because network responses must be copied to disk in order to
+// get a file handle we can use with dlopen.
+//
+// Until this is solved, we use two different implementations so that
+// performance isn't completely absymal.
+class Fetcher {
+ public:
+ // The param will be null in the case where the content could not be fetched.
+ // Reasons include:
+ // - network error
+ // - 4x or 5x HTTP errors
+ typedef base::Callback<void(scoped_ptr<Fetcher>)> FetchCallback;
+
+ Fetcher(const FetchCallback& fetch_callback);
+ virtual ~Fetcher();
+
+ // Returns the original URL that was fetched.
+ virtual const GURL& GetURL() const = 0;
+
+ // If the fetch resulted in a redirect, this returns the final URL after all
+ // redirects. Otherwise, it returns an empty URL.
+ virtual GURL GetRedirectURL() const = 0;
+
+ virtual URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
+ uint32_t skip) = 0;
+
+ virtual void AsPath(
+ base::TaskRunner* task_runner,
+ base::Callback<void(const base::FilePath&, bool)> callback) = 0;
+
+ virtual std::string MimeType() = 0;
+
+ virtual bool HasMojoMagic() = 0;
+
+ virtual bool PeekFirstLine(std::string* line) = 0;
+
+ bool PeekContentHandler(std::string* mojo_shebang,
+ GURL* mojo_content_handler_url);
+
+ protected:
+ static const char kMojoMagic[];
+ static const size_t kMaxShebangLength;
+
+ FetchCallback loader_callback_;
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_FETCHER_H_
diff --git a/chromium/mojo/shell/identity.cc b/chromium/mojo/shell/identity.cc
new file mode 100644
index 00000000000..bffe0ce4c60
--- /dev/null
+++ b/chromium/mojo/shell/identity.cc
@@ -0,0 +1,29 @@
+// 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 "mojo/shell/identity.h"
+
+#include "mojo/shell/query_util.h"
+
+namespace mojo {
+namespace shell {
+
+Identity::Identity(const GURL& url, const std::string& qualifier)
+ : url(GetBaseURLAndQuery(url, nullptr)),
+ qualifier(qualifier.empty() ? url.spec() : qualifier) {
+}
+
+// explicit
+Identity::Identity(const GURL& base_url)
+ : url(GetBaseURLAndQuery(base_url, nullptr)), qualifier(url.spec()) {
+}
+
+bool Identity::operator<(const Identity& other) const {
+ if (url != other.url)
+ return url < other.url;
+ return qualifier < other.qualifier;
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/identity.h b/chromium/mojo/shell/identity.h
new file mode 100644
index 00000000000..ac897a1c33f
--- /dev/null
+++ b/chromium/mojo/shell/identity.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef MOJO_SHELL_IDENTITY_H_
+#define MOJO_SHELL_IDENTITY_H_
+
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+/**
+ * Represents the identity of an application. |url| is the url of the
+ * application. |qualifier| is a string that allows to tie a specific instance
+ * of an application to another. It is used by content handlers that need to be
+ * run in the context of another application.
+ */
+struct Identity {
+ Identity(const GURL& url, const std::string& qualifier);
+ explicit Identity(const GURL& url);
+ bool operator<(const Identity& other) const;
+
+ const GURL url;
+ const std::string qualifier;
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_IDENTITY_H_
diff --git a/chromium/mojo/shell/local_fetcher.cc b/chromium/mojo/shell/local_fetcher.cc
new file mode 100644
index 00000000000..48444839516
--- /dev/null
+++ b/chromium/mojo/shell/local_fetcher.cc
@@ -0,0 +1,96 @@
+// 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 "mojo/shell/local_fetcher.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/common/url_type_converters.h"
+#include "mojo/util/filename_util.h"
+#include "url/url_util.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+void IgnoreResult(bool result) {
+}
+
+} // namespace
+
+// A loader for local files.
+LocalFetcher::LocalFetcher(const GURL& url,
+ const GURL& url_without_query,
+ const FetchCallback& loader_callback)
+ : Fetcher(loader_callback),
+ url_(url),
+ path_(util::UrlToFilePath(url_without_query)) {
+ TRACE_EVENT1("mojo_shell", "LocalFetcher::LocalFetcher", "url", url.spec());
+ loader_callback_.Run(make_scoped_ptr(this));
+}
+
+const GURL& LocalFetcher::GetURL() const {
+ return url_;
+}
+
+GURL LocalFetcher::GetRedirectURL() const {
+ return GURL::EmptyGURL();
+}
+
+URLResponsePtr LocalFetcher::AsURLResponse(base::TaskRunner* task_runner,
+ uint32_t skip) {
+ URLResponsePtr response(URLResponse::New());
+ response->url = String::From(url_);
+ DataPipe data_pipe;
+ response->body = data_pipe.consumer_handle.Pass();
+ int64 file_size;
+ if (base::GetFileSize(path_, &file_size)) {
+ response->headers = Array<HTTPHeaderPtr>(1);
+ HTTPHeaderPtr header = HTTPHeader::New();
+ header->name = "Content-Length";
+ header->value = base::StringPrintf("%" PRId64, file_size);
+ response->headers[0] = header.Pass();
+ }
+ common::CopyFromFile(path_, data_pipe.producer_handle.Pass(), skip,
+ task_runner, base::Bind(&IgnoreResult));
+ return response.Pass();
+}
+
+void LocalFetcher::AsPath(
+ base::TaskRunner* task_runner,
+ base::Callback<void(const base::FilePath&, bool)> callback) {
+ // Async for consistency with network case.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
+}
+
+std::string LocalFetcher::MimeType() {
+ return "";
+}
+
+bool LocalFetcher::HasMojoMagic() {
+ std::string magic;
+ ReadFileToString(path_, &magic, strlen(kMojoMagic));
+ return magic == kMojoMagic;
+}
+
+bool LocalFetcher::PeekFirstLine(std::string* line) {
+ std::string start_of_file;
+ ReadFileToString(path_, &start_of_file, kMaxShebangLength);
+ size_t return_position = start_of_file.find('\n');
+ if (return_position == std::string::npos)
+ return false;
+ *line = start_of_file.substr(0, return_position + 1);
+ return true;
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/local_fetcher.h b/chromium/mojo/shell/local_fetcher.h
new file mode 100644
index 00000000000..52ad9da3e13
--- /dev/null
+++ b/chromium/mojo/shell/local_fetcher.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef MOJO_SHELL_LOCAL_FETCHER_H_
+#define MOJO_SHELL_LOCAL_FETCHER_H_
+
+#include "base/files/file_path.h"
+#include "mojo/services/network/public/interfaces/url_loader.mojom.h"
+#include "mojo/shell/fetcher.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// Implements Fetcher for file:// URLs.
+class LocalFetcher : public Fetcher {
+ public:
+ LocalFetcher(const GURL& url,
+ const GURL& url_without_query,
+ const FetchCallback& loader_callback);
+
+ private:
+ const GURL& GetURL() const override;
+ GURL GetRedirectURL() const override;
+
+ URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
+ uint32_t skip) override;
+
+ void AsPath(
+ base::TaskRunner* task_runner,
+ base::Callback<void(const base::FilePath&, bool)> callback) override;
+
+ std::string MimeType() override;
+
+ bool HasMojoMagic() override;
+
+ bool PeekFirstLine(std::string* line) override;
+
+ GURL url_;
+ base::FilePath path_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalFetcher);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_LOCAL_FETCHER_H_
diff --git a/chromium/mojo/shell/native_runner.h b/chromium/mojo/shell/native_runner.h
new file mode 100644
index 00000000000..ad89f4431e3
--- /dev/null
+++ b/chromium/mojo/shell/native_runner.h
@@ -0,0 +1,63 @@
+// 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.
+
+#ifndef MOJO_SHELL_NATIVE_RUNNER_H_
+#define MOJO_SHELL_NATIVE_RUNNER_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/application/public/interfaces/application.mojom.h"
+#include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h"
+
+#if defined(OS_WIN)
+#undef DELETE
+#endif
+
+namespace base {
+class FilePath;
+}
+
+namespace mojo {
+namespace shell {
+
+enum class NativeApplicationCleanup { DELETE, DONT_DELETE };
+
+// ApplicationManager requires implementations of NativeRunner and
+// NativeRunnerFactory to run native applications.
+class NativeRunner {
+ public:
+ virtual ~NativeRunner() {}
+
+ // Loads the app in the file at |app_path| and runs it on some other
+ // thread/process. If |cleanup| is |DELETE|, this takes ownership of the file.
+ // |app_completed_callback| is posted (to the thread on which |Start()| was
+ // called) after |MojoMain()| completes.
+ // TODO(vtl): |app_path| and |cleanup| should probably be moved to the
+ // factory's Create(). Rationale: The factory may need information from the
+ // file to decide what kind of NativeRunner to make.
+ virtual void Start(const base::FilePath& app_path,
+ NativeApplicationCleanup cleanup,
+ InterfaceRequest<Application> application_request,
+ const base::Closure& app_completed_callback) = 0;
+};
+
+class NativeRunnerFactory {
+ public:
+ // Options for running the native app. (This will contain, e.g., information
+ // about the sandbox profile, etc.)
+ struct Options {
+ // Constructs with default options.
+ Options() : force_in_process(false) {}
+
+ bool force_in_process;
+ };
+
+ virtual ~NativeRunnerFactory() {}
+ virtual scoped_ptr<NativeRunner> Create(const Options& options) = 0;
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_NATIVE_RUNNER_H_
diff --git a/chromium/mojo/shell/network_fetcher.cc b/chromium/mojo/shell/network_fetcher.cc
new file mode 100644
index 00000000000..4de4d5d2a78
--- /dev/null
+++ b/chromium/mojo/shell/network_fetcher.cc
@@ -0,0 +1,251 @@
+// 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 "mojo/shell/network_fetcher.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/process.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "crypto/secure_hash.h"
+#include "crypto/sha2.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/common/url_type_converters.h"
+#include "mojo/services/network/public/interfaces/network_service.mojom.h"
+#include "mojo/shell/data_pipe_peek.h"
+#include "mojo/shell/switches.h"
+
+namespace mojo {
+namespace shell {
+
+NetworkFetcher::NetworkFetcher(bool disable_cache,
+ const GURL& url,
+ NetworkService* network_service,
+ const FetchCallback& loader_callback)
+ : Fetcher(loader_callback),
+ disable_cache_(false),
+ url_(url),
+ weak_ptr_factory_(this) {
+ StartNetworkRequest(url, network_service);
+}
+
+NetworkFetcher::~NetworkFetcher() {
+}
+
+const GURL& NetworkFetcher::GetURL() const {
+ return url_;
+}
+
+GURL NetworkFetcher::GetRedirectURL() const {
+ if (!response_)
+ return GURL::EmptyGURL();
+
+ if (response_->redirect_url.is_null())
+ return GURL::EmptyGURL();
+
+ return GURL(response_->redirect_url);
+}
+
+URLResponsePtr NetworkFetcher::AsURLResponse(base::TaskRunner* task_runner,
+ uint32_t skip) {
+ if (skip != 0) {
+ MojoResult result = ReadDataRaw(
+ response_->body.get(), nullptr, &skip,
+ MOJO_READ_DATA_FLAG_ALL_OR_NONE | MOJO_READ_DATA_FLAG_DISCARD);
+ DCHECK_EQ(result, MOJO_RESULT_OK);
+ }
+ return response_.Pass();
+}
+
+void NetworkFetcher::RecordCacheToURLMapping(const base::FilePath& path,
+ const GURL& url) {
+ // This is used to extract symbols on android.
+ // TODO(eseidel): All users of this log should move to using the map file.
+ VLOG(1) << "Caching mojo app " << url << " at " << path.value();
+
+ base::FilePath temp_dir;
+ base::GetTempDir(&temp_dir);
+ base::ProcessId pid = base::Process::Current().Pid();
+ std::string map_name = base::StringPrintf("mojo_shell.%d.maps", pid);
+ base::FilePath map_path = temp_dir.AppendASCII(map_name);
+
+ // TODO(eseidel): Paths or URLs with spaces will need quoting.
+ std::string map_entry =
+ base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str());
+ // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696
+ if (!PathExists(map_path)) {
+ base::WriteFile(map_path, map_entry.data(),
+ static_cast<int>(map_entry.length()));
+ } else {
+ base::AppendToFile(map_path, map_entry.data(),
+ static_cast<int>(map_entry.length()));
+ }
+}
+
+// For remote debugging, GDB needs to be, a apriori, aware of the filename a
+// library will be loaded from. AppIds should be be both predictable and unique,
+// but any hash would work. Currently we use sha256 from crypto/secure_hash.h
+bool NetworkFetcher::ComputeAppId(const base::FilePath& path,
+ std::string* digest_string) {
+ scoped_ptr<crypto::SecureHash> ctx(
+ crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+ base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (!file.IsValid()) {
+ LOG(ERROR) << "Failed to open " << path.value() << " for computing AppId";
+ return false;
+ }
+ char buf[1024];
+ while (file.IsValid()) {
+ int bytes_read = file.ReadAtCurrentPos(buf, sizeof(buf));
+ if (bytes_read == 0)
+ break;
+ ctx->Update(buf, bytes_read);
+ }
+ if (!file.IsValid()) {
+ LOG(ERROR) << "Error reading " << path.value();
+ return false;
+ }
+ // The output is really a vector of unit8, we're cheating by using a string.
+ std::string output(crypto::kSHA256Length, 0);
+ ctx->Finish(string_as_array(&output), output.size());
+ output = base::HexEncode(output.c_str(), output.size());
+ // Using lowercase for compatiblity with sha256sum output.
+ *digest_string = base::StringToLowerASCII(output);
+ return true;
+}
+
+bool NetworkFetcher::RenameToAppId(const GURL& url,
+ const base::FilePath& old_path,
+ base::FilePath* new_path) {
+ std::string app_id;
+ if (!ComputeAppId(old_path, &app_id))
+ return false;
+
+ // Using a hash of the url as a directory to prevent a race when the same
+ // bytes are downloaded from 2 different urls. In particular, if the same
+ // application is connected to twice concurrently with different query
+ // parameters, the directory will be different, which will prevent the
+ // collision.
+ std::string dirname = base::HexEncode(
+ crypto::SHA256HashString(url.spec()).data(), crypto::kSHA256Length);
+
+ base::FilePath temp_dir;
+ base::GetTempDir(&temp_dir);
+ base::FilePath app_dir = temp_dir.AppendASCII(dirname);
+ // The directory is leaked, because it can be reused at any time if the same
+ // application is downloaded. Deleting it would be racy. This is only
+ // happening when --predictable-app-filenames is used.
+ bool result = base::CreateDirectoryAndGetError(app_dir, nullptr);
+ DCHECK(result);
+ std::string unique_name = base::StringPrintf("%s.mojo", app_id.c_str());
+ *new_path = app_dir.AppendASCII(unique_name);
+ return base::Move(old_path, *new_path);
+}
+
+void NetworkFetcher::CopyCompleted(
+ base::Callback<void(const base::FilePath&, bool)> callback,
+ bool success) {
+ if (success) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kPredictableAppFilenames)) {
+ // The copy completed, now move to $TMP/$APP_ID.mojo before the dlopen.
+ success = false;
+ base::FilePath new_path;
+ if (RenameToAppId(url_, path_, &new_path)) {
+ if (base::PathExists(new_path)) {
+ path_ = new_path;
+ success = true;
+ }
+ }
+ }
+ }
+
+ if (success)
+ RecordCacheToURLMapping(path_, url_);
+
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(callback, path_, success));
+}
+
+void NetworkFetcher::AsPath(
+ base::TaskRunner* task_runner,
+ base::Callback<void(const base::FilePath&, bool)> callback) {
+ if (!path_.empty() || !response_) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
+ return;
+ }
+
+ base::CreateTemporaryFile(&path_);
+ common::CopyToFile(response_->body.Pass(), path_, task_runner,
+ base::Bind(&NetworkFetcher::CopyCompleted,
+ weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+std::string NetworkFetcher::MimeType() {
+ return response_->mime_type;
+}
+
+bool NetworkFetcher::HasMojoMagic() {
+ std::string magic;
+ return BlockingPeekNBytes(response_->body.get(), &magic, strlen(kMojoMagic),
+ kPeekTimeout) &&
+ magic == kMojoMagic;
+}
+
+bool NetworkFetcher::PeekFirstLine(std::string* line) {
+ return BlockingPeekLine(response_->body.get(), line, kMaxShebangLength,
+ kPeekTimeout);
+}
+
+void NetworkFetcher::StartNetworkRequest(const GURL& url,
+ NetworkService* network_service) {
+ TRACE_EVENT_ASYNC_BEGIN1("mojo_shell", "NetworkFetcher::NetworkRequest", this,
+ "url", url.spec());
+ URLRequestPtr request(URLRequest::New());
+ request->url = String::From(url);
+ request->auto_follow_redirects = false;
+ request->bypass_cache = disable_cache_;
+
+ network_service->CreateURLLoader(GetProxy(&url_loader_));
+ url_loader_->Start(request.Pass(),
+ base::Bind(&NetworkFetcher::OnLoadComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void NetworkFetcher::OnLoadComplete(URLResponsePtr response) {
+ TRACE_EVENT_ASYNC_END0("mojo_shell", "NetworkFetcher::NetworkRequest", this);
+ scoped_ptr<Fetcher> owner(this);
+ if (response->error) {
+ LOG(ERROR) << "Error (" << response->error->code << ": "
+ << response->error->description << ") while fetching "
+ << response->url;
+ loader_callback_.Run(nullptr);
+ return;
+ }
+
+ if (response->status_code >= 400 && response->status_code < 600) {
+ LOG(ERROR) << "Error (" << response->status_code << ": "
+ << response->status_line << "): "
+ << "while fetching " << response->url;
+ loader_callback_.Run(nullptr);
+ return;
+ }
+
+ response_ = response.Pass();
+ loader_callback_.Run(owner.Pass());
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/network_fetcher.h b/chromium/mojo/shell/network_fetcher.h
new file mode 100644
index 00000000000..028ec5b2121
--- /dev/null
+++ b/chromium/mojo/shell/network_fetcher.h
@@ -0,0 +1,83 @@
+// 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.
+
+#ifndef MOJO_SHELL_NETWORK_FETCHER_H_
+#define MOJO_SHELL_NETWORK_FETCHER_H_
+
+#include "mojo/shell/fetcher.h"
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/services/network/public/interfaces/url_loader.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class NetworkService;
+
+namespace shell {
+
+// Implements Fetcher for http[s] files.
+class NetworkFetcher : public Fetcher {
+ public:
+ NetworkFetcher(bool disable_cache,
+ const GURL& url,
+ NetworkService* network_service,
+ const FetchCallback& loader_callback);
+
+ ~NetworkFetcher() override;
+
+ private:
+ // TODO(hansmuller): Revisit this when a real peek operation is available.
+ static const MojoDeadline kPeekTimeout = MOJO_DEADLINE_INDEFINITE;
+
+ const GURL& GetURL() const override;
+ GURL GetRedirectURL() const override;
+
+ URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
+ uint32_t skip) override;
+
+ static void RecordCacheToURLMapping(const base::FilePath& path,
+ const GURL& url);
+
+ // AppIds should be be both predictable and unique, but any hash would work.
+ // Currently we use sha256 from crypto/secure_hash.h
+ static bool ComputeAppId(const base::FilePath& path,
+ std::string* digest_string);
+
+ static bool RenameToAppId(const GURL& url,
+ const base::FilePath& old_path,
+ base::FilePath* new_path);
+
+ void CopyCompleted(base::Callback<void(const base::FilePath&, bool)> callback,
+ bool success);
+
+ void AsPath(
+ base::TaskRunner* task_runner,
+ base::Callback<void(const base::FilePath&, bool)> callback) override;
+
+ std::string MimeType() override;
+
+ bool HasMojoMagic() override;
+
+ bool PeekFirstLine(std::string* line) override;
+
+ void StartNetworkRequest(const GURL& url, NetworkService* network_service);
+
+ void OnLoadComplete(URLResponsePtr response);
+
+ bool disable_cache_;
+ const GURL url_;
+ URLLoaderPtr url_loader_;
+ URLResponsePtr response_;
+ base::FilePath path_;
+ base::WeakPtrFactory<NetworkFetcher> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkFetcher);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_NETWORK_FETCHER_H_
diff --git a/chromium/mojo/shell/query_util.cc b/chromium/mojo/shell/query_util.cc
new file mode 100644
index 00000000000..fbe4c56fde5
--- /dev/null
+++ b/chromium/mojo/shell/query_util.cc
@@ -0,0 +1,31 @@
+// 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 "mojo/shell/query_util.h"
+
+#include "base/strings/string_util.h"
+
+namespace mojo {
+namespace shell {
+
+GURL GetBaseURLAndQuery(const GURL& url, std::string* query) {
+ if (!url.has_query()) {
+ if (query)
+ *query = "";
+ return url;
+ }
+
+ if (query)
+ *query = "?" + url.query();
+ GURL::Replacements repl;
+ repl.SetQueryStr("");
+ std::string result = url.ReplaceComponents(repl).spec();
+
+ // Remove the dangling '?' because it's ugly.
+ base::ReplaceChars(result, "?", "", &result);
+ return GURL(result);
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/query_util.h b/chromium/mojo/shell/query_util.h
new file mode 100644
index 00000000000..a1f0ac77a7c
--- /dev/null
+++ b/chromium/mojo/shell/query_util.h
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef MOJO_SHELL_QUERY_UTIL_H_
+#define MOJO_SHELL_QUERY_UTIL_H_
+
+#include <utility>
+
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// Returns the base URL (without the query string). If |query| is not nullptr,
+// set |*query| to the query string. If the url doesn't have a query string,
+// |*query| is set to the empty string.
+GURL GetBaseURLAndQuery(const GURL& url, std::string* query);
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_QUERY_UTIL_H_
diff --git a/chromium/mojo/shell/query_util_unittest.cc b/chromium/mojo/shell/query_util_unittest.cc
new file mode 100644
index 00000000000..9b28bc50d57
--- /dev/null
+++ b/chromium/mojo/shell/query_util_unittest.cc
@@ -0,0 +1,47 @@
+// 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 "mojo/shell/query_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+TEST(QueryUtil, NoQuery) {
+ GURL url("http://www.example.com/");
+ std::string query;
+ GURL base_url = GetBaseURLAndQuery(url, &query);
+ EXPECT_EQ(base_url, url);
+ EXPECT_EQ(query, "");
+}
+
+TEST(QueryUtil, WithEmptyQuery) {
+ GURL url("http://www.example.com/?");
+ std::string query;
+ GURL base_url = GetBaseURLAndQuery(url, &query);
+ EXPECT_EQ(base_url, GURL("http://www.example.com/"));
+ EXPECT_EQ(query, "?");
+}
+
+TEST(QueryUtil, WithFullQuery) {
+ GURL url("http://www.example.com/?a=b&c=d");
+ std::string query;
+ GURL base_url = GetBaseURLAndQuery(url, &query);
+ EXPECT_EQ(base_url, GURL("http://www.example.com/"));
+ EXPECT_EQ(query, "?a=b&c=d");
+}
+
+TEST(QueryUtil, ForFileURL) {
+ GURL url("file:///tmp/file?a=b&c=d");
+ std::string query;
+ GURL base_url = GetBaseURLAndQuery(url, &query);
+ EXPECT_EQ(base_url, GURL("file:///tmp/file"));
+ EXPECT_EQ(query, "?a=b&c=d");
+}
+
+} // namespace
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/shell_impl.cc b/chromium/mojo/shell/shell_impl.cc
new file mode 100644
index 00000000000..c51668628ae
--- /dev/null
+++ b/chromium/mojo/shell/shell_impl.cc
@@ -0,0 +1,62 @@
+// 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 "mojo/shell/shell_impl.h"
+
+#include "mojo/common/common_type_converters.h"
+#include "mojo/common/url_type_converters.h"
+#include "mojo/shell/application_manager.h"
+#include "third_party/mojo_services/src/content_handler/public/interfaces/content_handler.mojom.h"
+
+namespace mojo {
+namespace shell {
+
+ShellImpl::ShellImpl(ApplicationPtr application,
+ ApplicationManager* manager,
+ const Identity& identity,
+ const base::Closure& on_application_end)
+ : manager_(manager),
+ identity_(identity),
+ on_application_end_(on_application_end),
+ application_(application.Pass()),
+ binding_(this) {
+ binding_.set_error_handler(this);
+}
+
+ShellImpl::~ShellImpl() {
+}
+
+void ShellImpl::InitializeApplication(Array<String> args) {
+ ShellPtr shell;
+ binding_.Bind(GetProxy(&shell));
+ application_->Initialize(shell.Pass(), args.Pass(), identity_.url.spec());
+}
+
+void ShellImpl::ConnectToClient(const GURL& requested_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) {
+ application_->AcceptConnection(String::From(requestor_url), services.Pass(),
+ exposed_services.Pass(), requested_url.spec());
+}
+
+// Shell implementation:
+void ShellImpl::ConnectToApplication(const String& app_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) {
+ GURL app_gurl(app_url);
+ if (!app_gurl.is_valid()) {
+ LOG(ERROR) << "Error: invalid URL: " << app_url;
+ return;
+ }
+ manager_->ConnectToApplication(app_gurl, identity_.url, services.Pass(),
+ exposed_services.Pass(), base::Closure());
+}
+
+void ShellImpl::OnConnectionError() {
+ manager_->OnShellImplError(this);
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/shell_impl.h b/chromium/mojo/shell/shell_impl.h
new file mode 100644
index 00000000000..0f2d97488ee
--- /dev/null
+++ b/chromium/mojo/shell/shell_impl.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef MOJO_SHELL_SHELL_IMPL_H_
+#define MOJO_SHELL_SHELL_IMPL_H_
+
+#include "base/callback.h"
+#include "mojo/application/public/interfaces/application.mojom.h"
+#include "mojo/application/public/interfaces/shell.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/shell/identity.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+class ApplicationManager;
+
+class ShellImpl : public Shell, public ErrorHandler {
+ public:
+ ShellImpl(ApplicationPtr application,
+ ApplicationManager* manager,
+ const Identity& resolved_identity,
+ const base::Closure& on_application_end);
+
+ ~ShellImpl() override;
+
+ void InitializeApplication(Array<String> args);
+
+ void ConnectToClient(const GURL& requested_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services);
+
+ Application* application() { return application_.get(); }
+ const Identity& identity() const { return identity_; }
+ base::Closure on_application_end() const { return on_application_end_; }
+
+ private:
+ // Shell implementation:
+ void ConnectToApplication(const String& app_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) override;
+
+ // ErrorHandler implementation:
+ void OnConnectionError() override;
+
+ ApplicationManager* const manager_;
+ const Identity identity_;
+ base::Closure on_application_end_;
+ ApplicationPtr application_;
+ Binding<Shell> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellImpl);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_SHELL_IMPL_H_
diff --git a/chromium/mojo/shell/switches.cc b/chromium/mojo/shell/switches.cc
new file mode 100644
index 00000000000..cd68ff31368
--- /dev/null
+++ b/chromium/mojo/shell/switches.cc
@@ -0,0 +1,26 @@
+// Copyright 2013 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 "mojo/shell/switches.h"
+
+#include "base/basictypes.h"
+
+namespace switches {
+
+// If set apps downloaded are not deleted.
+const char kDontDeleteOnDownload[] = "dont-delete-on-download";
+
+// Load apps in separate processes.
+// TODO(vtl): Work in progress; doesn't work. Flip this to "disable" (or maybe
+// change it to "single-process") when it works.
+const char kEnableMultiprocess[] = "enable-multiprocess";
+
+// If set apps downloaded are saved in with a predictable filename, to help
+// remote debugging: when gdb is used through gdbserver, it needs to be able to
+// find locally any loaded library. For this, gdb use the filename of the
+// library. When using this flag, the application are named with the sha256 of
+// their content.
+const char kPredictableAppFilenames[] = "predictable-app-filenames";
+
+} // namespace switches
diff --git a/chromium/mojo/shell/switches.h b/chromium/mojo/shell/switches.h
new file mode 100644
index 00000000000..f774b3550b9
--- /dev/null
+++ b/chromium/mojo/shell/switches.h
@@ -0,0 +1,21 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SHELL_SWITCHES_H_
+#define MOJO_SHELL_SWITCHES_H_
+
+#include <set>
+#include <string>
+
+namespace switches {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+extern const char kDontDeleteOnDownload[];
+extern const char kEnableMultiprocess[];
+extern const char kPredictableAppFilenames[];
+
+} // namespace switches
+
+#endif // MOJO_SHELL_SWITCHES_H_
diff --git a/chromium/mojo/shell/test.mojom b/chromium/mojo/shell/test.mojom
new file mode 100644
index 00000000000..25700b59b6f
--- /dev/null
+++ b/chromium/mojo/shell/test.mojom
@@ -0,0 +1,23 @@
+// 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.
+
+module mojo.shell;
+
+interface TestService {
+ Test(string test_string) => ();
+};
+
+interface TestA {
+ CallB();
+ CallCFromB();
+};
+
+interface TestB {
+ B() => ();
+ CallC() => ();
+};
+
+interface TestC {
+ C() => ();
+};