summaryrefslogtreecommitdiffstats
path: root/mojo/package_manager
diff options
context:
space:
mode:
authorben <ben@chromium.org>2015-09-24 09:39:14 -0700
committerCommit bot <commit-bot@chromium.org>2015-09-24 16:40:20 +0000
commit885517fdbc3a284501d457c9f0256ea731f54e07 (patch)
treec9b711f1d205144f2afd6a914eccf7b23fc81ec6 /mojo/package_manager
parent277f2311573c2720ee0656ac943473bc9866ebc4 (diff)
downloadchromium_src-885517fdbc3a284501d457c9f0256ea731f54e07.zip
chromium_src-885517fdbc3a284501d457c9f0256ea731f54e07.tar.gz
chromium_src-885517fdbc3a284501d457c9f0256ea731f54e07.tar.bz2
Move more of ContentHandler handling into PackageManager.
Dependency/#include rules required that I restructure the unit tests as part of this, moving all the content handler tests into package_manager also. R=yzshen@chromium.org http://crbug.com/533085 Review URL: https://codereview.chromium.org/1358533004 Cr-Commit-Position: refs/heads/master@{#350572}
Diffstat (limited to 'mojo/package_manager')
-rw-r--r--mojo/package_manager/BUILD.gn24
-rw-r--r--mojo/package_manager/capability_filter_content_handler_unittest.cc158
-rw-r--r--mojo/package_manager/content_handler_connection.cc57
-rw-r--r--mojo/package_manager/content_handler_connection.h61
-rw-r--r--mojo/package_manager/content_handler_unittest.cc318
-rw-r--r--mojo/package_manager/package_manager_impl.cc137
-rw-r--r--mojo/package_manager/package_manager_impl.h47
7 files changed, 758 insertions, 44 deletions
diff --git a/mojo/package_manager/BUILD.gn b/mojo/package_manager/BUILD.gn
index 913a36d..3d69ae9 100644
--- a/mojo/package_manager/BUILD.gn
+++ b/mojo/package_manager/BUILD.gn
@@ -2,8 +2,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//testing/test.gni")
+
source_set("package_manager") {
sources = [
+ "content_handler_connection.cc",
+ "content_handler_connection.h",
"package_manager_impl.cc",
"package_manager_impl.h",
]
@@ -14,3 +18,23 @@ source_set("package_manager") {
"//mojo/util:filename_util",
]
}
+
+test("unittests") {
+ output_name = "mojo_package_manager_unittests"
+
+ sources = [
+ "capability_filter_content_handler_unittest.cc",
+ "content_handler_unittest.cc",
+ ]
+
+ deps = [
+ ":package_manager",
+ "//base",
+ "//mojo/application/public/cpp",
+ "//mojo/shell",
+ "//mojo/shell:test_support",
+ "//third_party/mojo/src/mojo/edk/test:run_all_unittests",
+ "//testing/gtest",
+ "//url",
+ ]
+}
diff --git a/mojo/package_manager/capability_filter_content_handler_unittest.cc b/mojo/package_manager/capability_filter_content_handler_unittest.cc
new file mode 100644
index 0000000..1e313af
--- /dev/null
+++ b/mojo/package_manager/capability_filter_content_handler_unittest.cc
@@ -0,0 +1,158 @@
+// 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 "base/path_service.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/content_handler.mojom.h"
+#include "mojo/common/weak_binding_set.h"
+#include "mojo/package_manager/package_manager_impl.h"
+#include "mojo/shell/capability_filter_test.h"
+#include "mojo/shell/fetcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace package_manager {
+namespace test {
+namespace {
+
+const char kTestMimeType[] = "test/mime-type";
+
+// A custom Fetcher used to trigger a content handler for kTestMimeType for a
+// specific test.
+class TestFetcher : public shell::Fetcher {
+ public:
+ TestFetcher(const GURL& url, const FetchCallback& callback)
+ : shell::Fetcher(callback),
+ url_(url) {
+ loader_callback_.Run(make_scoped_ptr(this));
+ }
+ ~TestFetcher() override {}
+
+ private:
+ // Overridden from Fetcher:
+ const GURL& GetURL() const override { return url_; }
+ GURL GetRedirectURL() const override { return GURL(); }
+ GURL GetRedirectReferer() const override { return GURL(); }
+ URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
+ uint32_t skip) override {
+ URLResponsePtr response(URLResponse::New());
+ response->url = url_.spec();
+ return response.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; }
+
+ const GURL url_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFetcher);
+};
+
+class TestPackageManager : public PackageManagerImpl {
+ public:
+ TestPackageManager(const base::FilePath& package_path)
+ : PackageManagerImpl(package_path, nullptr) {}
+ ~TestPackageManager() override {}
+
+ private:
+ // Overridden from PackageManagerImpl:
+ void FetchRequest(
+ URLRequestPtr request,
+ const shell::Fetcher::FetchCallback& loader_callback) override {
+ new TestFetcher(GURL(request->url), loader_callback);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TestPackageManager);
+};
+
+class TestContentHandler : public ApplicationDelegate,
+ public InterfaceFactory<ContentHandler>,
+ public ContentHandler {
+ public:
+ TestContentHandler() : app_(nullptr) {}
+ ~TestContentHandler() override {}
+
+ private:
+ // Overridden from ApplicationDelegate:
+ void Initialize(ApplicationImpl* app) override {
+ app_ = app;
+ }
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+ connection->AddService<ContentHandler>(this);
+ return true;
+ }
+
+ // Overridden from InterfaceFactory<ContentHandler>:
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<ContentHandler> request) override {
+ bindings_.AddBinding(this, request.Pass());
+ }
+
+ // Overridden from ContentHandler:
+ void StartApplication(InterfaceRequest<Application> application,
+ URLResponsePtr response) override {
+ scoped_ptr<ApplicationDelegate> delegate(new shell::test::TestApplication);
+ embedded_apps_.push_back(
+ new ApplicationImpl(delegate.get(), application.Pass()));
+ embedded_app_delegates_.push_back(delegate.Pass());
+ }
+
+ ApplicationImpl* app_;
+ WeakBindingSet<ContentHandler> bindings_;
+ ScopedVector<ApplicationDelegate> embedded_app_delegates_;
+ ScopedVector<ApplicationImpl> embedded_apps_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestContentHandler);
+};
+
+} // namespace
+
+class CapabilityFilterContentHandlerTest
+ : public shell::test::CapabilityFilterTest {
+ public:
+ CapabilityFilterContentHandlerTest()
+ : package_manager_(nullptr) {
+ base::FilePath shell_dir;
+ PathService::Get(base::DIR_MODULE, &shell_dir);
+ package_manager_ = new TestPackageManager(shell_dir);
+ }
+ ~CapabilityFilterContentHandlerTest() override {}
+
+ private:
+ // Overridden from CapabilityFilterTest:
+ shell::PackageManager* CreatePackageManager() override {
+ return package_manager_;
+ }
+ void SetUp() override {
+ shell::test::CapabilityFilterTest::SetUp();
+
+ GURL content_handler_url("test:content_handler");
+ package_manager_->RegisterContentHandler(kTestMimeType,
+ content_handler_url);
+ CreateLoader<TestContentHandler>(content_handler_url.spec());
+ }
+
+ // Owned by ApplicationManager in base class.
+ PackageManagerImpl* package_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(CapabilityFilterContentHandlerTest);
+};
+
+TEST_F(CapabilityFilterContentHandlerTest, Blocking) {
+ RunBlockingTest();
+}
+
+TEST_F(CapabilityFilterContentHandlerTest, Wildcards) {
+ RunWildcardTest();
+}
+
+} // namespace test
+} // namespace package_manager
+} // namespace mojo \ No newline at end of file
diff --git a/mojo/package_manager/content_handler_connection.cc b/mojo/package_manager/content_handler_connection.cc
new file mode 100644
index 0000000..e778c4d
--- /dev/null
+++ b/mojo/package_manager/content_handler_connection.cc
@@ -0,0 +1,57 @@
+// 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/package_manager/content_handler_connection.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/shell/application_manager.h"
+#include "mojo/shell/connect_to_application_params.h"
+#include "mojo/shell/identity.h"
+
+namespace mojo {
+namespace package_manager {
+
+ContentHandlerConnection::ContentHandlerConnection(
+ shell::ApplicationManager* manager,
+ const shell::Identity& source,
+ const shell::Identity& content_handler,
+ uint32_t id,
+ const ClosedCallback& connection_closed_callback)
+ : connection_closed_callback_(connection_closed_callback),
+ identity_(content_handler),
+ connection_closed_(false),
+ id_(id) {
+ ServiceProviderPtr services;
+
+ scoped_ptr<shell::ConnectToApplicationParams> params(
+ new shell::ConnectToApplicationParams);
+ params->set_source(source);
+ params->SetTarget(identity_);
+ params->set_services(GetProxy(&services));
+ manager->ConnectToApplication(params.Pass());
+
+ MessagePipe pipe;
+ content_handler_.Bind(
+ InterfacePtrInfo<ContentHandler>(pipe.handle0.Pass(), 0u));
+ services->ConnectToService(ContentHandler::Name_, pipe.handle1.Pass());
+ content_handler_.set_connection_error_handler(
+ [this]() { CloseConnection(); });
+}
+
+void ContentHandlerConnection::CloseConnection() {
+ if (connection_closed_)
+ return;
+ connection_closed_ = true;
+ connection_closed_callback_.Run(this);
+ delete this;
+}
+
+ContentHandlerConnection::~ContentHandlerConnection() {
+ // If this DCHECK fails then something has tried to delete this object without
+ // calling CloseConnection.
+ DCHECK(connection_closed_);
+}
+
+} // namespace package_manager
+} // namespace mojo
diff --git a/mojo/package_manager/content_handler_connection.h b/mojo/package_manager/content_handler_connection.h
new file mode 100644
index 0000000..9bc10797
--- /dev/null
+++ b/mojo/package_manager/content_handler_connection.h
@@ -0,0 +1,61 @@
+// 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_PACKAGE_MANAGER_CONTENT_HANDLER_CONNECTION_H_
+#define MOJO_PACKAGE_MANAGER_CONTENT_HANDLER_CONNECTION_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "mojo/application/public/interfaces/content_handler.mojom.h"
+#include "mojo/shell/identity.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+class ApplicationManager;
+}
+namespace package_manager {
+
+// A ContentHandlerConnection is responsible for creating and maintaining a
+// connection to an app which provides the ContentHandler service.
+// A ContentHandlerConnection can only be destroyed via CloseConnection.
+// A ContentHandlerConnection manages its own lifetime and cannot be used with
+// a scoped_ptr to avoid reentrant calls into ApplicationManager late in
+// destruction.
+class ContentHandlerConnection {
+ public:
+ using ClosedCallback = base::Callback<void(ContentHandlerConnection*)>;
+ // |id| is a unique identifier for this content handler.
+ ContentHandlerConnection(shell::ApplicationManager* manager,
+ const shell::Identity& source,
+ const shell::Identity& content_handler,
+ uint32_t id,
+ const ClosedCallback& connection_closed_callback);
+
+ // Closes the connection and destroys |this| object.
+ void CloseConnection();
+
+ ContentHandler* content_handler() { return content_handler_.get(); }
+ const shell::Identity& identity() const { return identity_; }
+ uint32_t id() const { return id_; }
+
+ private:
+ ~ContentHandlerConnection();
+
+ ClosedCallback connection_closed_callback_;
+ shell::Identity identity_;
+
+ ContentHandlerPtr content_handler_;
+ bool connection_closed_;
+ // The id for this content handler.
+ const uint32_t id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection);
+};
+
+} // namespace package_manager
+} // namespace mojo
+
+#endif // MOJO_PACKAGE_MANAGER_CONTENT_HANDLER_CONNECTION_H_
diff --git a/mojo/package_manager/content_handler_unittest.cc b/mojo/package_manager/content_handler_unittest.cc
new file mode 100644
index 0000000..fa94a36
--- /dev/null
+++ b/mojo/package_manager/content_handler_unittest.cc
@@ -0,0 +1,318 @@
+// 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 "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 "base/path_service.h"
+#include "base/run_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/content_handler.mojom.h"
+#include "mojo/application/public/interfaces/service_provider.mojom.h"
+#include "mojo/package_manager/package_manager_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/shell/application_loader.h"
+#include "mojo/shell/application_manager.h"
+#include "mojo/shell/connect_util.h"
+#include "mojo/shell/fetcher.h"
+#include "mojo/shell/test_package_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace package_manager {
+namespace test {
+namespace {
+
+const char kTestMimeType[] = "test/mime-type";
+
+class TestFetcher : public shell::Fetcher {
+ public:
+ TestFetcher(const FetchCallback& fetch_callback,
+ const GURL& url,
+ const std::string& mime_type)
+ : Fetcher(fetch_callback), url_(url), mime_type_(mime_type) {
+ loader_callback_.Run(make_scoped_ptr(this));
+ }
+ ~TestFetcher() override {}
+
+ // Fetcher:
+ const GURL& GetURL() const override { return url_; }
+ GURL GetRedirectURL() const override { return GURL("yyy"); }
+ GURL GetRedirectReferer() const override { return GURL(); }
+ 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 mime_type_; }
+ bool HasMojoMagic() override { return false; }
+ bool PeekFirstLine(std::string* line) override { return false; }
+
+ private:
+ const GURL url_;
+ const std::string mime_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFetcher);
+};
+
+void QuitClosure(bool* value) {
+ *value = true;
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+class TestContentHandler : public ContentHandler, public ApplicationDelegate {
+ public:
+ TestContentHandler(ApplicationConnection* connection,
+ InterfaceRequest<ContentHandler> request)
+ : binding_(this, request.Pass()) {}
+
+ // ContentHandler:
+ void StartApplication(InterfaceRequest<Application> application_request,
+ URLResponsePtr response) override {
+ apps_.push_back(new ApplicationImpl(this, application_request.Pass()));
+ }
+
+ private:
+ StrongBinding<ContentHandler> binding_;
+ ScopedVector<ApplicationImpl> apps_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestContentHandler);
+};
+
+class TestApplicationLoader : public shell::ApplicationLoader,
+ public ApplicationDelegate,
+ public InterfaceFactory<ContentHandler> {
+ public:
+ TestApplicationLoader() : num_loads_(0) {}
+ ~TestApplicationLoader() override {}
+
+ int num_loads() const { return num_loads_; }
+ 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<ContentHandler>(this);
+ last_requestor_url_ = GURL(connection->GetRemoteApplicationURL());
+ return true;
+ }
+ // InterfaceFactory<ContentHandler> implementation.
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<ContentHandler> request) override {
+ new TestContentHandler(connection, request.Pass());
+ }
+
+ scoped_ptr<ApplicationImpl> test_app_;
+ int num_loads_;
+ GURL last_requestor_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader);
+};
+
+class TestPackageManager : public PackageManagerImpl {
+ public:
+ explicit TestPackageManager(const base::FilePath& package_path)
+ : PackageManagerImpl(package_path, nullptr),
+ mime_type_(kTestMimeType) {}
+ ~TestPackageManager() override {}
+
+ void set_mime_type(const std::string& mime_type) {
+ mime_type_ = mime_type;
+ }
+
+ // PackageManagerImpl:
+ void FetchRequest(
+ URLRequestPtr request,
+ const shell::Fetcher::FetchCallback& loader_callback) override {
+ new TestFetcher(loader_callback, GURL(request->url), mime_type_);
+ }
+
+ private:
+ std::string mime_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPackageManager);
+};
+
+} // namespace
+
+class ContentHandlerTest : public testing::Test {
+ public:
+ ContentHandlerTest()
+ : content_handler_url_("http://test.content.handler"),
+ requestor_url_("http://requestor.url") {}
+ ~ContentHandlerTest() override {}
+
+ void SetUp() override {
+ base::FilePath shell_dir;
+ PathService::Get(base::DIR_MODULE, &shell_dir);
+ test_package_manager_ = new TestPackageManager(shell_dir);
+ test_package_manager_->RegisterContentHandler(kTestMimeType,
+ content_handler_url_);
+ application_manager_.reset(new shell::ApplicationManager(
+ make_scoped_ptr(test_package_manager_)));
+ }
+
+ void TearDown() override {
+ test_package_manager_ = nullptr;
+ application_manager_.reset();
+ }
+
+ protected:
+ const GURL content_handler_url_;
+ const GURL requestor_url_;
+
+ base::MessageLoop loop_;
+ scoped_ptr<shell::ApplicationManager> application_manager_;
+ // Owned by ApplicationManager.
+ TestPackageManager* test_package_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentHandlerTest);
+};
+
+TEST_F(ContentHandlerTest, ContentHandlerConnectionGetsRequestorURL) {
+ TestApplicationLoader* loader = new TestApplicationLoader;
+ application_manager_->SetLoaderForURL(
+ scoped_ptr<shell::ApplicationLoader>(loader),
+ content_handler_url_);
+
+ bool called = false;
+ scoped_ptr<shell::ConnectToApplicationParams> params(
+ new shell::ConnectToApplicationParams);
+ params->set_source(shell::Identity(requestor_url_));
+ params->SetTargetURL(GURL("test:test"));
+ params->set_on_application_end(
+ base::Bind(&QuitClosure, base::Unretained(&called)));
+ application_manager_->ConnectToApplication(params.Pass());
+ loop_.Run();
+ EXPECT_TRUE(called);
+
+ ASSERT_EQ(1, loader->num_loads());
+ EXPECT_EQ(requestor_url_, loader->last_requestor_url());
+}
+
+TEST_F(ContentHandlerTest,
+ MultipleConnectionsToContentHandlerGetSameContentHandlerId) {
+ TestApplicationLoader* content_handler_loader = new TestApplicationLoader;
+ application_manager_->SetLoaderForURL(
+ scoped_ptr<shell::ApplicationLoader>(content_handler_loader),
+ content_handler_url_);
+
+ uint32_t content_handler_id;
+ {
+ base::RunLoop run_loop;
+ scoped_ptr<shell::ConnectToApplicationParams> params(
+ new shell::ConnectToApplicationParams);
+ params->set_source(shell::Identity(requestor_url_));
+ params->SetTargetURL(GURL("test:test"));
+ params->set_connect_callback([&content_handler_id, &run_loop](uint32_t t) {
+ content_handler_id = t;
+ run_loop.Quit();
+ });
+ application_manager_->ConnectToApplication(params.Pass());
+ run_loop.Run();
+ EXPECT_NE(Shell::kInvalidContentHandlerID, content_handler_id);
+ }
+
+ uint32_t content_handler_id2;
+ {
+ base::RunLoop run_loop;
+ scoped_ptr<shell::ConnectToApplicationParams> params(
+ new shell::ConnectToApplicationParams);
+ params->set_source(shell::Identity(requestor_url_));
+ params->SetTargetURL(GURL("test:test"));
+ params->set_connect_callback([&content_handler_id2, &run_loop](uint32_t t) {
+ content_handler_id2 = t;
+ run_loop.Quit();
+ });
+ application_manager_->ConnectToApplication(params.Pass());
+ run_loop.Run();
+ EXPECT_NE(Shell::kInvalidContentHandlerID, content_handler_id2);
+ }
+ EXPECT_EQ(content_handler_id, content_handler_id2);
+}
+
+TEST_F(ContentHandlerTest, DifferedContentHandlersGetDifferentIDs) {
+ TestApplicationLoader* content_handler_loader = new TestApplicationLoader;
+ application_manager_->SetLoaderForURL(
+ scoped_ptr<shell::ApplicationLoader>(content_handler_loader),
+ content_handler_url_);
+
+ uint32_t content_handler_id;
+ {
+ base::RunLoop run_loop;
+ scoped_ptr<shell::ConnectToApplicationParams> params(
+ new shell::ConnectToApplicationParams);
+ params->set_source(shell::Identity(requestor_url_));
+ params->SetTargetURL(GURL("test:test"));
+ params->set_connect_callback([&content_handler_id, &run_loop](uint32_t t) {
+ content_handler_id = t;
+ run_loop.Quit();
+ });
+ application_manager_->ConnectToApplication(params.Pass());
+ run_loop.Run();
+ EXPECT_NE(Shell::kInvalidContentHandlerID, content_handler_id);
+ }
+
+ const std::string mime_type2 = "test/mime-type2";
+ const GURL content_handler_url2("http://test.content.handler2");
+ test_package_manager_->set_mime_type(mime_type2);
+ test_package_manager_->RegisterContentHandler(mime_type2,
+ content_handler_url2);
+
+ TestApplicationLoader* content_handler_loader2 = new TestApplicationLoader;
+ application_manager_->SetLoaderForURL(
+ scoped_ptr<shell::ApplicationLoader>(content_handler_loader2),
+ content_handler_url2);
+
+ uint32_t content_handler_id2;
+ {
+ base::RunLoop run_loop;
+ scoped_ptr<shell::ConnectToApplicationParams> params(
+ new shell::ConnectToApplicationParams);
+ params->set_source(shell::Identity(requestor_url_));
+ params->SetTargetURL(GURL("test2:test2"));
+ params->set_connect_callback([&content_handler_id2, &run_loop](uint32_t t) {
+ content_handler_id2 = t;
+ run_loop.Quit();
+ });
+ application_manager_->ConnectToApplication(params.Pass());
+ run_loop.Run();
+ EXPECT_NE(Shell::kInvalidContentHandlerID, content_handler_id2);
+ }
+ EXPECT_NE(content_handler_id, content_handler_id2);
+}
+
+TEST_F(ContentHandlerTest,
+ ConnectWithNoContentHandlerGetsInvalidContentHandlerId) {
+ application_manager_->SetLoaderForURL(
+ scoped_ptr<shell::ApplicationLoader>(new TestApplicationLoader),
+ GURL("test:test"));
+
+ uint32_t content_handler_id = 1u;
+ scoped_ptr<shell::ConnectToApplicationParams> params(
+ new shell::ConnectToApplicationParams);
+ params->SetTargetURL(GURL("test:test"));
+ params->set_connect_callback(
+ [&content_handler_id](uint32_t t) { content_handler_id = t; });
+ application_manager_->ConnectToApplication(params.Pass());
+ EXPECT_EQ(0u, content_handler_id);
+}
+
+} // namespace test
+} // namespace package_manager
+} // namespace mojo
diff --git a/mojo/package_manager/package_manager_impl.cc b/mojo/package_manager/package_manager_impl.cc
index b85e9de..73f2c75 100644
--- a/mojo/package_manager/package_manager_impl.cc
+++ b/mojo/package_manager/package_manager_impl.cc
@@ -4,12 +4,15 @@
#include "mojo/package_manager/package_manager_impl.h"
+#include "base/bind.h"
+#include "mojo/application/public/interfaces/content_handler.mojom.h"
#include "mojo/fetcher/about_fetcher.h"
#include "mojo/fetcher/data_fetcher.h"
#include "mojo/fetcher/local_fetcher.h"
#include "mojo/fetcher/network_fetcher.h"
#include "mojo/fetcher/switches.h"
#include "mojo/fetcher/update_fetcher.h"
+#include "mojo/package_manager/content_handler_connection.h"
#include "mojo/shell/application_manager.h"
#include "mojo/shell/connect_util.h"
#include "mojo/shell/query_util.h"
@@ -21,10 +24,13 @@ namespace mojo {
namespace package_manager {
PackageManagerImpl::PackageManagerImpl(
- const base::FilePath& shell_file_root)
+ const base::FilePath& shell_file_root,
+ base::TaskRunner* task_runner)
: application_manager_(nullptr),
disable_cache_(base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableCache)) {
+ switches::kDisableCache)),
+ content_handler_id_counter_(0u),
+ task_runner_(task_runner) {
if (!shell_file_root.empty()) {
GURL mojo_root_file_url =
util::FilePathToFileURL(shell_file_root).Resolve(std::string());
@@ -33,6 +39,10 @@ PackageManagerImpl::PackageManagerImpl(
}
PackageManagerImpl::~PackageManagerImpl() {
+ IdentityToContentHandlerMap identity_to_content_handler(
+ identity_to_content_handler_);
+ for (auto& pair : identity_to_content_handler)
+ pair.second->CloseConnection();
}
void PackageManagerImpl::RegisterContentHandler(
@@ -115,64 +125,117 @@ void PackageManagerImpl::FetchRequest(
url_loader_factory_.get(), loader_callback);
}
-bool PackageManagerImpl::HandleWithContentHandler(shell::Fetcher* fetcher,
- const GURL& url,
- base::TaskRunner* task_runner,
- URLResponsePtr* new_response,
- GURL* content_handler_url,
- std::string* qualifier) {
+uint32_t PackageManagerImpl::HandleWithContentHandler(
+ shell::Fetcher* fetcher,
+ const shell::Identity& source,
+ const GURL& target_url,
+ const shell::CapabilityFilter& target_filter,
+ InterfaceRequest<Application>* application_request) {
+ shell::Identity content_handler_identity;
+ URLResponsePtr response;
+ if (ShouldHandleWithContentHandler(fetcher,
+ target_url,
+ target_filter,
+ &content_handler_identity,
+ &response)) {
+ ContentHandlerConnection* connection =
+ GetContentHandler(content_handler_identity, source);
+ connection->content_handler()->StartApplication(application_request->Pass(),
+ response.Pass());
+ return connection->id();
+ }
+ return Shell::kInvalidContentHandlerID;
+}
+
+GURL PackageManagerImpl::ResolveURL(const GURL& url) {
+ return url_resolver_.get() ? url_resolver_->ResolveMojoURL(url) : url;
+}
+
+bool PackageManagerImpl::ShouldHandleWithContentHandler(
+ shell::Fetcher* fetcher,
+ const GURL& target_url,
+ const shell::CapabilityFilter& target_filter,
+ shell::Identity* content_handler_identity,
+ URLResponsePtr* response) const {
// TODO(beng): it seems like some delegate should/would want to have a say in
// configuring the qualifier also.
- bool enable_multi_process = base::CommandLine::ForCurrentProcess()->HasSwitch(
+ // Why can't we use the real qualifier in single process mode? Because of
+ // base::AtExitManager. If you link in ApplicationRunner 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.
+ bool use_real_qualifier = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableMultiprocess);
+ GURL content_handler_url;
// The response begins with a #!mojo <content-handler-url>.
std::string shebang;
- if (fetcher->PeekContentHandler(&shebang, content_handler_url)) {
- *new_response = fetcher->AsURLResponse(
- task_runner, static_cast<int>(shebang.size()));
- *qualifier = enable_multi_process ? (*new_response)->site.To<std::string>()
- : std::string();
+ if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
+ *response = fetcher->AsURLResponse(task_runner_,
+ static_cast<int>(shebang.size()));
+ *content_handler_identity = shell::Identity(
+ content_handler_url,
+ use_real_qualifier ? (*response)->site.To<std::string>()
+ : std::string(),
+ target_filter);
return true;
}
// The response MIME type matches a registered content handler.
- MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
+ auto iter = mime_type_to_url_.find(fetcher->MimeType());
if (iter != mime_type_to_url_.end()) {
- *new_response = fetcher->AsURLResponse(task_runner, 0);
- *content_handler_url = iter->second;
- *qualifier = enable_multi_process ? (*new_response)->site.To<std::string>()
- : std::string();
+ *response = fetcher->AsURLResponse(task_runner_, 0);
+ *content_handler_identity = shell::Identity(
+ iter->second,
+ use_real_qualifier ? (*response)->site.To<std::string>()
+ : std::string(),
+ target_filter);
return true;
}
// The response URL matches a registered content handler.
- auto alias_iter = application_package_alias_.find(url);
+ auto alias_iter = application_package_alias_.find(target_url);
if (alias_iter != application_package_alias_.end()) {
// We replace the qualifier with the one our package alias requested.
- *new_response = URLResponse::New();
- (*new_response)->url = url.spec();
-
- // Why can't we use this in single process mode? Because of
- // base::AtExitManager. If you link in ApplicationRunner 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 = enable_multi_process ? alias_iter->second.second
- : std::string();
- *content_handler_url = alias_iter->second.first;
+ *response = URLResponse::New();
+ (*response)->url = target_url.spec();
+ *content_handler_identity = shell::Identity(
+ alias_iter->second.first,
+ use_real_qualifier ? alias_iter->second.second : std::string(),
+ target_filter);
return true;
}
return false;
}
-GURL PackageManagerImpl::ResolveURL(const GURL& url) {
- return url_resolver_.get() ? url_resolver_->ResolveMojoURL(url) : url;
+ContentHandlerConnection* PackageManagerImpl::GetContentHandler(
+ const shell::Identity& content_handler_identity,
+ const shell::Identity& source_identity) {
+ auto it = identity_to_content_handler_.find(content_handler_identity);
+ if (it != identity_to_content_handler_.end())
+ return it->second;
+
+ ContentHandlerConnection* connection = new ContentHandlerConnection(
+ application_manager_, source_identity,
+ content_handler_identity,
+ ++content_handler_id_counter_,
+ base::Bind(&PackageManagerImpl::OnContentHandlerConnectionClosed,
+ base::Unretained(this)));
+ identity_to_content_handler_[content_handler_identity] = connection;
+ return connection;
+}
+
+void PackageManagerImpl::OnContentHandlerConnectionClosed(
+ ContentHandlerConnection* connection) {
+ // Remove the mapping.
+ auto it = identity_to_content_handler_.find(connection->identity());
+ DCHECK(it != identity_to_content_handler_.end());
+ identity_to_content_handler_.erase(it);
}
} // namespace package_manager
diff --git a/mojo/package_manager/package_manager_impl.h b/mojo/package_manager/package_manager_impl.h
index 19620cf..b44429f 100644
--- a/mojo/package_manager/package_manager_impl.h
+++ b/mojo/package_manager/package_manager_impl.h
@@ -11,11 +11,18 @@
#include "mojo/services/network/public/interfaces/url_loader_factory.mojom.h"
#include "mojo/shell/package_manager.h"
+namespace base {
+class TaskRunner;
+}
+
namespace mojo {
+class ContentHandler;
namespace shell {
class Fetcher;
+class Identity;
}
namespace package_manager {
+class ContentHandlerConnection;
// This is the default implementation of shell::PackageManager. It loads
// http/s urls off the network as well as providing special handling for mojo:
@@ -23,7 +30,12 @@ namespace package_manager {
class PackageManagerImpl : public shell::PackageManager {
public:
// mojo: urls are only supported if |shell_file_root| is non-empty.
- explicit PackageManagerImpl(const base::FilePath& shell_file_root);
+ // |task_runner| is used by Fetchers created by the PackageManager to complete
+ // file copies needed to obtain library paths that the ApplicationManager can
+ // load. This can be null only in tests where application loading is handled
+ // by custom ApplicationLoader implementations.
+ PackageManagerImpl(const base::FilePath& shell_file_root,
+ base::TaskRunner* task_runner);
~PackageManagerImpl() override;
// Register a content handler to handle content of |mime_type|.
@@ -44,20 +56,37 @@ class PackageManagerImpl : public shell::PackageManager {
private:
using ApplicationPackagedAlias = std::map<GURL, std::pair<GURL, std::string>>;
using MimeTypeToURLMap = std::map<std::string, GURL>;
+ using IdentityToContentHandlerMap =
+ std::map<shell::Identity, ContentHandlerConnection*>;
// Overridden from shell::PackageManager:
void SetApplicationManager(shell::ApplicationManager* manager) override;
void FetchRequest(
URLRequestPtr request,
const shell::Fetcher::FetchCallback& loader_callback) override;
- bool HandleWithContentHandler(shell::Fetcher* fetcher,
- const GURL& url,
- base::TaskRunner* task_runner,
- URLResponsePtr* new_response,
- GURL* content_handler_url,
- std::string* qualifier) override;
+ uint32_t HandleWithContentHandler(
+ shell::Fetcher* fetcher,
+ const shell::Identity& source,
+ const GURL& target_url,
+ const shell::CapabilityFilter& target_filter,
+ InterfaceRequest<Application>* application_request) override;
GURL ResolveURL(const GURL& url);
+ bool ShouldHandleWithContentHandler(
+ shell::Fetcher* fetcher,
+ const GURL& target_url,
+ const shell::CapabilityFilter& target_filter,
+ shell::Identity* content_handler_identity,
+ URLResponsePtr* response) const;
+
+ // Returns a running ContentHandler for |content_handler_identity|, if there
+ // is not one running one is started for |source_identity|.
+ ContentHandlerConnection* GetContentHandler(
+ const shell::Identity& content_handler_identity,
+ const shell::Identity& source_identity);
+
+ void OnContentHandlerConnectionClosed(
+ ContentHandlerConnection* content_handler);
shell::ApplicationManager* application_manager_;
scoped_ptr<fetcher::URLResolver> url_resolver_;
@@ -66,6 +95,10 @@ class PackageManagerImpl : public shell::PackageManager {
URLLoaderFactoryPtr url_loader_factory_;
ApplicationPackagedAlias application_package_alias_;
MimeTypeToURLMap mime_type_to_url_;
+ IdentityToContentHandlerMap identity_to_content_handler_;
+ // Counter used to assign ids to content handlers.
+ uint32_t content_handler_id_counter_;
+ base::TaskRunner* task_runner_;
DISALLOW_COPY_AND_ASSIGN(PackageManagerImpl);
};