diff options
author | ben <ben@chromium.org> | 2015-09-24 09:39:14 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-09-24 16:40:20 +0000 |
commit | 885517fdbc3a284501d457c9f0256ea731f54e07 (patch) | |
tree | c9b711f1d205144f2afd6a914eccf7b23fc81ec6 /mojo/package_manager | |
parent | 277f2311573c2720ee0656ac943473bc9866ebc4 (diff) | |
download | chromium_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.gn | 24 | ||||
-rw-r--r-- | mojo/package_manager/capability_filter_content_handler_unittest.cc | 158 | ||||
-rw-r--r-- | mojo/package_manager/content_handler_connection.cc | 57 | ||||
-rw-r--r-- | mojo/package_manager/content_handler_connection.h | 61 | ||||
-rw-r--r-- | mojo/package_manager/content_handler_unittest.cc | 318 | ||||
-rw-r--r-- | mojo/package_manager/package_manager_impl.cc | 137 | ||||
-rw-r--r-- | mojo/package_manager/package_manager_impl.h | 47 |
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); }; |