Commit 84f58fd6 authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

Initial commit of a basic Content Service

The Content Service is a new s13n project intended to servicify
the bulk of the content layer's functionality, namely the ability for
applications to embed navigable web content views backed by isolated,
sandboxed content handlers. See https://tinyurl.com/ycrx5tgq for
a high-level overview and (eventually) links to more detailed
documentation.

This CL lands a trivial client View API with some tiny mojom surface
between it and the Content Service. The API as-is may be completely
throw-away, but this change gets all the necessary boilerplate and
plumbing landed.

The ContentServiceDelegate and ContentViewDelegate C++ interfaces are
exposed by the Content Service impl so that it can delegate to private
src/content implementation details without a dependency on src/content.

The browser itself is granted access to connect to Content Service for
now so that basic integration testing can be done via
content_browsertests. One such test is added here to verify a
functioning end-to-end connection between a client's View instance
and an actual backing WebContentsImpl.

Bug: 854365
Change-Id: Idd8e4d0e5f2922ffc48686a4057e6e7c262c3cc3
Reviewed-on: https://chromium-review.googlesource.com/1107012
Commit-Queue: Ken Rockot <rockot@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569797}
parent ccb5d3f8
...@@ -126,6 +126,8 @@ jumbo_source_set("browser") { ...@@ -126,6 +126,8 @@ jumbo_source_set("browser") {
"//services/catalog:constants", "//services/catalog:constants",
"//services/catalog/public/cpp", "//services/catalog/public/cpp",
"//services/catalog/public/mojom:constants", "//services/catalog/public/mojom:constants",
"//services/content:impl",
"//services/content/public/mojom",
"//services/data_decoder/public/cpp", "//services/data_decoder/public/cpp",
"//services/data_decoder/public/mojom", "//services/data_decoder/public/mojom",
"//services/device:lib", "//services/device:lib",
...@@ -581,6 +583,8 @@ jumbo_source_set("browser") { ...@@ -581,6 +583,8 @@ jumbo_source_set("browser") {
# needed on all platforms. # needed on all platforms.
"compositor/surface_utils.cc", "compositor/surface_utils.cc",
"compositor/surface_utils.h", "compositor/surface_utils.h",
"content_service_delegate_impl.cc",
"content_service_delegate_impl.h",
"cookie_store/cookie_change_subscription.cc", "cookie_store/cookie_change_subscription.cc",
"cookie_store/cookie_change_subscription.h", "cookie_store/cookie_change_subscription.h",
"cookie_store/cookie_store_context.cc", "cookie_store/cookie_store_context.cc",
......
...@@ -61,6 +61,13 @@ include_rules = [ ...@@ -61,6 +61,13 @@ include_rules = [
"+chromeos", "+chromeos",
"+third_party/cros_system_api", "+third_party/cros_system_api",
# Limit visibility into Content Service internals.
"-services/content",
"+services/content/public",
"+services/content/service.h",
"+services/content/service_delegate.h",
"+services/content/view_delegate.h",
# No inclusion of WebKit from the browser, other than the ones in # No inclusion of WebKit from the browser, other than the ones in
# WebKit/public/{mojom,common}, or the ones that are strictly enum/POD, # WebKit/public/{mojom,common}, or the ones that are strictly enum/POD,
# header-only types, and some selected common code. # header-only types, and some selected common code.
......
...@@ -23,11 +23,13 @@ ...@@ -23,11 +23,13 @@
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/rand_util.h" #include "base/rand_util.h"
#include "base/task_scheduler/post_task.h" #include "base/task_scheduler/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/unguessable_token.h" #include "base/unguessable_token.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/browsing_data/browsing_data_remover_impl.h" #include "content/browser/browsing_data/browsing_data_remover_impl.h"
#include "content/browser/content_service_delegate_impl.h"
#include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_manager_impl.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h" #include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_dispatcher_host_impl.h"
...@@ -51,6 +53,8 @@ ...@@ -51,6 +53,8 @@
#include "net/ssl/channel_id_store.h" #include "net/ssl/channel_id_store.h"
#include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_context_getter.h"
#include "services/content/public/mojom/constants.mojom.h"
#include "services/content/service.h"
#include "services/file/file_service.h" #include "services/file/file_service.h"
#include "services/file/public/mojom/constants.mojom.h" #include "services/file/public/mojom/constants.mojom.h"
#include "services/file/user_id_map.h" #include "services/file/user_id_map.h"
...@@ -84,8 +88,23 @@ class ServiceUserIdHolder : public base::SupportsUserData::Data { ...@@ -84,8 +88,23 @@ class ServiceUserIdHolder : public base::SupportsUserData::Data {
DISALLOW_COPY_AND_ASSIGN(ServiceUserIdHolder); DISALLOW_COPY_AND_ASSIGN(ServiceUserIdHolder);
}; };
class ContentServiceDelegateHolder : public base::SupportsUserData::Data {
public:
explicit ContentServiceDelegateHolder(BrowserContext* browser_context)
: delegate_(browser_context) {}
~ContentServiceDelegateHolder() override = default;
ContentServiceDelegateImpl* delegate() { return &delegate_; }
private:
ContentServiceDelegateImpl delegate_;
DISALLOW_COPY_AND_ASSIGN(ContentServiceDelegateHolder);
};
// Key names on BrowserContext. // Key names on BrowserContext.
const char kBrowsingDataRemoverKey[] = "browsing-data-remover"; const char kBrowsingDataRemoverKey[] = "browsing-data-remover";
const char kContentServiceDelegateKey[] = "content-service-delegate";
const char kDownloadManagerKeyName[] = "download_manager"; const char kDownloadManagerKeyName[] = "download_manager";
const char kMojoWasInitialized[] = "mojo-was-initialized"; const char kMojoWasInitialized[] = "mojo-was-initialized";
const char kServiceManagerConnection[] = "service-manager-connection"; const char kServiceManagerConnection[] = "service-manager-connection";
...@@ -511,9 +530,32 @@ void BrowserContext::Initialize( ...@@ -511,9 +530,32 @@ void BrowserContext::Initialize(
// New embedded service factories should be added to |connection| here. // New embedded service factories should be added to |connection| here.
service_manager::EmbeddedServiceInfo info; {
info.factory = base::BindRepeating(&file::CreateFileService); service_manager::EmbeddedServiceInfo info;
connection->AddEmbeddedService(file::mojom::kServiceName, info); info.factory = base::BindRepeating(&file::CreateFileService);
connection->AddEmbeddedService(file::mojom::kServiceName, info);
}
browser_context->SetUserData(
kContentServiceDelegateKey,
std::make_unique<ContentServiceDelegateHolder>(browser_context));
{
service_manager::EmbeddedServiceInfo info;
info.task_runner = base::SequencedTaskRunnerHandle::Get();
info.factory = base::BindRepeating(
[](BrowserContext* context)
-> std::unique_ptr<service_manager::Service> {
auto* holder = static_cast<ContentServiceDelegateHolder*>(
context->GetUserData(kContentServiceDelegateKey));
auto* delegate = holder->delegate();
auto service = std::make_unique<content::Service>(delegate);
delegate->AddService(service.get());
return service;
},
browser_context);
connection->AddEmbeddedService(content::mojom::kServiceName, info);
}
ContentBrowserClient::StaticServiceMap services; ContentBrowserClient::StaticServiceMap services;
browser_context->RegisterInProcessServices(&services); browser_context->RegisterInProcessServices(&services);
......
// Copyright 2018 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/files/file_path.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_paths.h"
#include "content/public/test/content_browser_test.h"
#include "content/shell/browser/shell.h"
#include "services/content/public/cpp/view.h"
#include "services/content/public/mojom/constants.mojom.h"
#include "services/content/public/mojom/view_factory.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace content {
namespace {
class ContentServiceBrowserTest : public ContentBrowserTest {
public:
ContentServiceBrowserTest() = default;
~ContentServiceBrowserTest() override = default;
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
base::FilePath test_data_path;
CHECK(base::PathService::Get(DIR_TEST_DATA, &test_data_path));
embedded_test_server()->ServeFilesFromDirectory(test_data_path);
CHECK(embedded_test_server()->Start());
}
private:
DISALLOW_COPY_AND_ASSIGN(ContentServiceBrowserTest);
};
// Verifies that the embedded Content Service is reachable. Does a basic
// end-to-end sanity check to also verify that a ContentView client is backed
// by a WebContents instance in the browser.
IN_PROC_BROWSER_TEST_F(ContentServiceBrowserTest, EmbeddedContentService) {
auto* browser_context = shell()->web_contents()->GetBrowserContext();
auto* connector = BrowserContext::GetConnectorFor(browser_context);
content::mojom::ViewFactoryPtr factory;
connector->BindInterface(content::mojom::kServiceName, &factory);
auto view = std::make_unique<content::View>(factory.get());
base::RunLoop loop;
view->set_did_stop_loading_callback_for_testing(loop.QuitClosure());
view->Navigate(embedded_test_server()->GetURL("/hello.html"));
loop.Run();
}
} // namespace
} // namespace content
// Copyright 2018 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 "content/browser/content_service_delegate_impl.h"
#include "base/macros.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "services/content/service.h"
#include "services/content/view_delegate.h"
namespace content {
namespace {
// Bridge between Content Service view delegation API and a WebContentsImpl.
class ViewDelegateImpl : public content::ViewDelegate,
public WebContentsObserver {
public:
explicit ViewDelegateImpl(BrowserContext* browser_context,
mojom::ViewClient* client)
: client_(client) {
WebContents::CreateParams params(browser_context);
web_contents_ = WebContents::Create(params);
WebContentsObserver::Observe(web_contents_.get());
}
~ViewDelegateImpl() override { WebContentsObserver::Observe(nullptr); }
private:
// content::ViewDelegate:
void Navigate(const GURL& url) override {
NavigationController::LoadURLParams params(url);
web_contents_->GetController().LoadURLWithParams(params);
}
// WebContentsObserver:
void DidStopLoading() override { client_->DidStopLoading(); }
std::unique_ptr<WebContents> web_contents_;
mojom::ViewClient* const client_;
DISALLOW_COPY_AND_ASSIGN(ViewDelegateImpl);
};
} // namespace
ContentServiceDelegateImpl::ContentServiceDelegateImpl(
BrowserContext* browser_context)
: browser_context_(browser_context) {}
ContentServiceDelegateImpl::~ContentServiceDelegateImpl() {
// This delegate is destroyed immediately before |browser_context_| is
// destroyed. We force-kill any Content Service instances which depend on
// |this|, since they will no longer be functional anyway.
std::set<content::Service*> instances;
std::swap(instances, service_instances_);
for (content::Service* service : instances) {
// Eventually destroys |service|. Ensures that no more calls into |this|
// will occur.
service->ForceQuit();
}
}
void ContentServiceDelegateImpl::AddService(content::Service* service) {
service_instances_.insert(service);
}
void ContentServiceDelegateImpl::WillDestroyServiceInstance(
content::Service* service) {
service_instances_.erase(service);
}
std::unique_ptr<content::ViewDelegate>
ContentServiceDelegateImpl::CreateViewDelegate(mojom::ViewClient* client) {
return std::make_unique<ViewDelegateImpl>(browser_context_, client);
}
} // namespace content
// Copyright 2018 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 CONTENT_BROWSER_CONTENT_SERVICE_DELEGATE_IMPL_H_
#define CONTENT_BROWSER_CONTENT_SERVICE_DELEGATE_IMPL_H_
#include <memory>
#include <set>
#include "base/macros.h"
#include "services/content/service_delegate.h"
namespace content {
class BrowserContext;
// Implementation of the main delegate interface for the Content Service. This
// is used to support the Content Service implementation with content/browser
// details, without the Content Service having any build dependencies on
// src/content. There is one instance of this delegate per BrowserContext,
// shared by any ContentService instance instantiated on behalf of that
// BrowserContext.
class ContentServiceDelegateImpl : public content::ServiceDelegate {
public:
// Constructs a new ContentServiceDelegateImpl for |browser_context|.
// |browser_context| must outlive |this|.
explicit ContentServiceDelegateImpl(BrowserContext* browser_context);
~ContentServiceDelegateImpl() override;
// Registers |service| with this delegate. Must be called for any |service|
// using |this| as its ContentServiceDelegate. Automatically balanced by
// |WillDestroyServiceInstance()|.
void AddService(content::Service* service);
private:
// content::ContentServiceDelegate:
void WillDestroyServiceInstance(content::Service* service) override;
std::unique_ptr<ViewDelegate> CreateViewDelegate(
mojom::ViewClient* client) override;
BrowserContext* const browser_context_;
// Tracks ContentService instances currently using this delegate. Necessary
// because the lifetime of |this| is tied to the lifetime of
// |browser_context_|; on destruction of |this|, we need to force all of these
// ContentService instances to terminate, since they cannot operate without
// their delegate.
std::set<content::Service*> service_instances_;
DISALLOW_COPY_AND_ASSIGN(ContentServiceDelegateImpl);
};
} // namespace content
#endif // CONTENT_BROWSER_CONTENT_SERVICE_DELEGATE_IMPL_H_
...@@ -202,7 +202,10 @@ service_manifest("packaged_services_manifest") { ...@@ -202,7 +202,10 @@ service_manifest("packaged_services_manifest") {
service_manifest("browser_manifest") { service_manifest("browser_manifest") {
name = "content_browser" name = "content_browser"
source = "mojo/content_browser_manifest.json" source = "mojo/content_browser_manifest.json"
packaged_services = [ "//services/file:manifest" ] packaged_services = [
"//services/content:manifest",
"//services/file:manifest",
]
} }
service_manifest("gpu_manifest") { service_manifest("gpu_manifest") {
......
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
"testing_api" "testing_api"
], ],
"cdm": [ "media:cdm" ], "cdm": [ "media:cdm" ],
"content": [ "view" ],
"content_gpu": [ "browser" ], "content_gpu": [ "browser" ],
"content_plugin": [ "browser" ], "content_plugin": [ "browser" ],
"content_renderer": [ "browser" ], "content_renderer": [ "browser" ],
......
...@@ -712,6 +712,7 @@ test("content_browsertests") { ...@@ -712,6 +712,7 @@ test("content_browsertests") {
"../browser/browsing_data/conditional_cache_deletion_helper_browsertest.cc", "../browser/browsing_data/conditional_cache_deletion_helper_browsertest.cc",
"../browser/child_process_launcher_browsertest.cc", "../browser/child_process_launcher_browsertest.cc",
"../browser/child_process_security_policy_browsertest.cc", "../browser/child_process_security_policy_browsertest.cc",
"../browser/content_service_browsertest.cc",
"../browser/cross_site_transfer_browsertest.cc", "../browser/cross_site_transfer_browsertest.cc",
"../browser/database_browsertest.cc", "../browser/database_browsertest.cc",
"../browser/device_sensors/device_sensor_browsertest.cc", "../browser/device_sensors/device_sensor_browsertest.cc",
...@@ -933,6 +934,8 @@ test("content_browsertests") { ...@@ -933,6 +934,8 @@ test("content_browsertests") {
"//ppapi/buildflags", "//ppapi/buildflags",
"//services/audio/public/cpp", "//services/audio/public/cpp",
"//services/catalog:lib", "//services/catalog:lib",
"//services/content/public/cpp",
"//services/content/public/mojom",
"//services/device/public/cpp:device_features", "//services/device/public/cpp:device_features",
"//services/device/public/cpp/generic_sensor", "//services/device/public/cpp/generic_sensor",
"//services/device/public/cpp/test:test_support", "//services/device/public/cpp/test:test_support",
......
...@@ -19,6 +19,7 @@ service_test("services_unittests") { ...@@ -19,6 +19,7 @@ service_test("services_unittests") {
# If your service does not run on iOS, its tests should go in the !iOS # If your service does not run on iOS, its tests should go in the !iOS
# section below. If you are unsure, contact blundell@chromium.org. # section below. If you are unsure, contact blundell@chromium.org.
deps = [ deps = [
"//services/content:tests",
"//services/identity:tests", "//services/identity:tests",
"//services/metrics/public/cpp:tests", "//services/metrics/public/cpp:tests",
"//services/network:tests", "//services/network:tests",
......
# Copyright 2018 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("//services/service_manager/public/service_manifest.gni")
source_set("impl") {
visibility = [
":tests",
"//content/browser",
]
public = [
"service.h",
"service_delegate.h",
"view_delegate.h",
]
sources = [
"service.cc",
"view_factory_impl.cc",
"view_factory_impl.h",
"view_impl.cc",
"view_impl.h",
]
public_deps = [
"//base",
"//services/service_manager/public/cpp",
]
deps = [
"//mojo/public/cpp/bindings",
"//services/content/public/cpp",
"//services/content/public/mojom",
]
}
source_set("tests") {
testonly = true
sources = [
"service_unittest.cc",
]
deps = [
":impl",
"//base",
"//base/test:test_support",
"//services/content/public/cpp",
"//services/content/public/mojom",
"//services/service_manager/public/cpp/test:test_support",
"//testing/gtest",
"//url",
]
}
service_manifest("manifest") {
name = "content"
source = "manifest.json"
}
alexmos@chromium.org
clamy@chromium.org
jam@chromium.org
rockot@chromium.org
per-file manifest.json=set noparent
per-file manifest.json=file://ipc/SECURITY_OWNERS
{
"name": "content",
"display_name": "Content Service",
"interface_provider_specs": {
"service_manager:connector": {
"provides": {
// The |view| capability allows a service to access embeddable, navigable content views.
// For now, access to this capability should be restricted only to services which are
// at least as trusted as the Chrome browser process (e.g., Ash).
"view": [
"content.mojom.ViewFactory"
]
}
}
}
}
# Copyright 2018 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.
component("cpp") {
output_name = "content_service_cpp"
public = [
"view.h",
]
sources = [
"view.cc",
]
defines = [ "IS_CONTENT_SERVICE_CPP_IMPL" ]
public_deps = [
"//base",
"//services/content/public/mojom",
"//url",
]
}
// Copyright 2018 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 "services/content/public/cpp/view.h"
namespace content {
View::View(mojom::ViewFactory* factory) : client_binding_(this) {
mojom::ViewClientPtr client;
client_binding_.Bind(mojo::MakeRequest(&client));
factory->CreateView(mojom::ViewParams::New(), mojo::MakeRequest(&view_),
std::move(client));
}
View::~View() = default;
void View::Navigate(const GURL& url) {
view_->Navigate(url);
}
void View::DidStopLoading() {
if (did_stop_loading_callback_)
did_stop_loading_callback_.Run();
}
} // namespace content
// Copyright 2018 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 SERVICES_CONTENT_PUBLIC_CPP_VIEW_H_
#define SERVICES_CONTENT_PUBLIC_CPP_VIEW_H_
#include <memory>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/content/public/mojom/view.mojom.h"
#include "services/content/public/mojom/view_factory.mojom.h"
namespace content {
// A View is a navigable, top-level view of web content which applications can
// embed within their own UI.
//
// TODO(https://crbug.com/855092): Actually support UI embedding.
class COMPONENT_EXPORT(CONTENT_SERVICE_CPP) View : public mojom::ViewClient {
public:
// Constructs a new View using |factory|.
explicit View(mojom::ViewFactory* factory);
~View() override;
// Begins an attempt to asynchronously navigate this View to |url|.
void Navigate(const GURL& url);
void set_did_stop_loading_callback_for_testing(
base::RepeatingClosure callback) {
did_stop_loading_callback_ = std::move(callback);
}
private:
// mojom::ViewClient:
void DidStopLoading() override;
mojom::ViewPtr view_;
mojo::Binding<mojom::ViewClient> client_binding_;
base::RepeatingClosure did_stop_loading_callback_;
DISALLOW_COPY_AND_ASSIGN(View);
};
} // namespace content
#endif // SERVICES_CONTENT_PUBLIC_CPP_VIEW_H_
# Copyright 2018 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("//mojo/public/tools/bindings/mojom.gni")
mojom_component("mojom") {
# We don't want Blink variants of these bindings to be generated, because they
# will end up having a dependency on KURL and thus be required to link into
# the same Blink component target. This should be removed once it's possible
# for a non-Blink target to depend on KURL.
disable_variants = true
sources = [
"constants.mojom",
"view.mojom",
"view_factory.mojom",
]
public_deps = [
"//url/mojom:url_mojom_gurl",
]
output_prefix = "content_service_mojom"
macro_prefix = "CONTENT_SERVICE_MOJOM"
}
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
// Copyright 2018 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 content.mojom;
const string kServiceName = "content";
// Copyright 2018 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 content.mojom;
import "url/mojom/url.mojom";
// The primary interface driving a navigable content view embedded by an
// application which wants to display web contents.
interface View {
// Initiates a navigation to |url|.
Navigate(url.mojom.Url url);
};
// A client interface used by the Content Service to push view-scoped events
// back to the application.
interface ViewClient {
// Notifies the client that the View has stopped loading resources pertaining
// to a navigation.
DidStopLoading();
};
// Copyright 2018 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 content.mojom;
import "services/content/public/mojom/view.mojom";
// Parameters used to configure a newly created View.
struct ViewParams {};
// ViewFactory is the primary interface through which a new View interface is
// bound to a new concrete content view.
interface ViewFactory {
// Creates a new content view configured according to |params|. |request| is
// bound to the view implementation, and |client| is used to push
// notifications of events relevant to the state of the new view throughout
// its lifetime.
CreateView(ViewParams params, View& request, ViewClient client);
};
// Copyright 2018 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 "services/content/service.h"
#include <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/content/public/mojom/view_factory.mojom.h"
#include "services/content/service_delegate.h"
#include "services/content/view_factory_impl.h"
#include "services/content/view_impl.h"
#include "services/service_manager/public/cpp/service_context.h"
namespace content {
Service::Service(ServiceDelegate* delegate) : delegate_(delegate) {
binders_.AddInterface(base::BindRepeating(
[](Service* service, mojom::ViewFactoryRequest request) {
service->AddViewFactory(
std::make_unique<ViewFactoryImpl>(service, std::move(request)));
},
this));
}
Service::~Service() {
delegate_->WillDestroyServiceInstance(this);
}
void Service::ForceQuit() {
// Ensure that all bound interfaces are disconnected and no further interface
// requests will be handled.
view_factories_.clear();
views_.clear();
binders_.RemoveInterface<mojom::ViewFactory>();
// Force-disconnect from the Service Mangager. Under normal circumstances
// (i.e. in non-test code), the call below destroys |this|.
context()->QuitNow();
}
void Service::AddViewFactory(std::unique_ptr<ViewFactoryImpl> factory) {
auto* raw_factory = factory.get();
view_factories_.emplace(raw_factory, std::move(factory));
}
void Service::RemoveViewFactory(ViewFactoryImpl* factory) {
view_factories_.erase(factory);
}
void Service::AddView(std::unique_ptr<ViewImpl> view) {
auto* raw_view = view.get();
views_.emplace(raw_view, std::move(view));
}
void Service::RemoveView(ViewImpl* view) {
views_.erase(view);
}
void Service::OnBindInterface(const service_manager::BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle pipe) {
binders_.BindInterface(interface_name, std::move(pipe));
}
} // namespace content
// Copyright 2018 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 SERVICES_CONTENT_SERVICE_H_
#define SERVICES_CONTENT_SERVICE_H_
#include <map>
#include "base/macros.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/service.h"
namespace content {
class ServiceDelegate;
class ViewFactoryImpl;
class ViewImpl;
// The core Service implementation of the Content Service. This takes
// responsibility for owning top-level state for an instance of the service,
// binding incoming interface requests, etc.
//
// NOTE: This type is exposed to ServiceDelegate implementations outside
// of private Content Service code. The public API surface of this class should
// therefore remain as minimal as possible.
class Service : public service_manager::Service {
public:
// |delegate| is not owned and must outlive |this|.
explicit Service(ServiceDelegate* delegate);
~Service() override;
ServiceDelegate* delegate() const { return delegate_; }
// Forces this instance of the Service to be terminated. Useful if the
// delegate implementation encounters a scenario in which it can no longer
// operate correctly. May delete |this|.
void ForceQuit();
private:
friend class ViewFactoryImpl;
friend class ViewImpl;
void AddViewFactory(std::unique_ptr<ViewFactoryImpl> factory);
void RemoveViewFactory(ViewFactoryImpl* factory);
void AddView(std::unique_ptr<ViewImpl> view);
void RemoveView(ViewImpl* view);
// service_manager::Service:
void OnBindInterface(const service_manager::BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle pipe) override;
ServiceDelegate* const delegate_;
service_manager::BinderRegistry binders_;
std::map<ViewFactoryImpl*, std::unique_ptr<ViewFactoryImpl>> view_factories_;
std::map<ViewImpl*, std::unique_ptr<ViewImpl>> views_;
DISALLOW_COPY_AND_ASSIGN(Service);
};
}; // namespace content
#endif // SERVICES_CONTENT_SERVICE_H_
// Copyright 2018 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 SERVICES_CONTENT_SERVICE_DELEGATE_H_
#define SERVICES_CONTENT_SERVICE_DELEGATE_H_
#include "services/content/public/mojom/view.mojom.h"
namespace content {
class Service;
class ViewDelegate;
// This is a delegate interface which allows the Content Service implementation
// to delegate out to private src/content code without a circular dependency
// between them.
//
// This interface is strictly intended to host transitional APIs for aiding in
// incremental conversion of src/content and its dependents into/onto the
// Content Service. As such, APIs should only be added here with a plan for
// eventual removal.
class ServiceDelegate {
public:
virtual ~ServiceDelegate() {}
// Called when an instance of Service (specifically one using this
// delegate) is about to be destroyed.
virtual void WillDestroyServiceInstance(Service* service) = 0;
// Constructs a new ViewDelegate implementation to back a new ContentViewImpl
// instance, servicing a client's ContentView. |client| is a ViewClient
// interface the implementation can use to communicate with the client of this
// view.
virtual std::unique_ptr<ViewDelegate> CreateViewDelegate(
mojom::ViewClient* client) = 0;
};
}; // namespace content
#endif // SERVICES_CONTENT_CONTENT_SERVICE_DELEGATE_H_
// Copyright 2018 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 "services/content/service.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/content/public/mojom/constants.mojom.h"
#include "services/content/public/mojom/view.mojom.h"
#include "services/content/public/mojom/view_factory.mojom.h"
#include "services/content/service_delegate.h"
#include "services/content/view_delegate.h"
#include "services/service_manager/public/cpp/test/test_connector_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace content {
namespace {
class TestViewClient : public mojom::ViewClient {
public:
TestViewClient() = default;
~TestViewClient() override = default;
private:
// mojom::ViewClient:
void DidStopLoading() override {}
DISALLOW_COPY_AND_ASSIGN(TestViewClient);
};
class TestViewDelegate : public ViewDelegate {
public:
TestViewDelegate() = default;
~TestViewDelegate() override = default;
const GURL& last_navigated_url() const { return last_navigated_url_; }
void set_navigation_callback(base::RepeatingClosure callback) {
navigation_callback_ = std::move(callback);
}
// ViewDelegate:
void Navigate(const GURL& url) override {
last_navigated_url_ = url;
if (navigation_callback_)
navigation_callback_.Run();
}
private:
GURL last_navigated_url_;
base::RepeatingClosure navigation_callback_;
DISALLOW_COPY_AND_ASSIGN(TestViewDelegate);
};
class TestServiceDelegate : public ServiceDelegate {
public:
TestServiceDelegate() = default;
~TestServiceDelegate() override = default;
void set_view_delegate_created_callback(
base::RepeatingCallback<void(TestViewDelegate*)> callback) {
view_delegate_created_callback_ = std::move(callback);
}
// ServiceDelegate:
void WillDestroyServiceInstance(Service* service) override {}
std::unique_ptr<ViewDelegate> CreateViewDelegate(
mojom::ViewClient* client) override {
auto object = std::make_unique<TestViewDelegate>();
if (view_delegate_created_callback_)
view_delegate_created_callback_.Run(object.get());
return object;
}
private:
base::RepeatingCallback<void(TestViewDelegate*)>
view_delegate_created_callback_;
DISALLOW_COPY_AND_ASSIGN(TestServiceDelegate);
};
class ContentServiceTest : public testing::Test {
public:
ContentServiceTest() = default;
~ContentServiceTest() override = default;
void SetUp() override {
connector_factory_ =
service_manager::TestConnectorFactory::CreateForUniqueService(
std::make_unique<Service>(&delegate_));
connector_ = connector_factory_->CreateConnector();
}
protected:
TestServiceDelegate& delegate() { return delegate_; }
template <typename T>
void BindInterface(mojo::InterfaceRequest<T> request) {
connector_->BindInterface(content::mojom::kServiceName, std::move(request));
}
private:
base::test::ScopedTaskEnvironment task_environment_;
std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_;
std::unique_ptr<service_manager::Connector> connector_;
TestServiceDelegate delegate_;
DISALLOW_COPY_AND_ASSIGN(ContentServiceTest);
};
TEST_F(ContentServiceTest, ViewCreation) {
mojom::ViewFactoryPtr factory;
BindInterface(mojo::MakeRequest(&factory));
base::RunLoop loop;
TestViewDelegate* view_delegate = nullptr;
delegate().set_view_delegate_created_callback(
base::BindLambdaForTesting([&](TestViewDelegate* delegate) {
EXPECT_FALSE(view_delegate);
view_delegate = delegate;
loop.Quit();
}));
mojom::ViewPtr view;
TestViewClient client_impl;
mojom::ViewClientPtr client;
mojo::Binding<mojom::ViewClient> client_binding(&client_impl,
mojo::MakeRequest(&client));
factory->CreateView(mojom::ViewParams::New(), mojo::MakeRequest(&view),
std::move(client));
loop.Run();
base::RunLoop navigation_loop;
ASSERT_TRUE(view_delegate);
view_delegate->set_navigation_callback(navigation_loop.QuitClosure());
const GURL kTestUrl("https://example.com/");
view->Navigate(kTestUrl);
navigation_loop.Run();
EXPECT_EQ(kTestUrl, view_delegate->last_navigated_url());
}
} // namespace
} // namespace content
// Copyright 2018 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 SERVICES_CONTENT_VIEW_DELEGATE_H_
#define SERVICES_CONTENT_VIEW_DELEGATE_H_
class GURL;
namespace content {
// A virtual interface which must be implemented as a backing for ViewImpl
// instances.
//
// This is the primary interface by which the Content Service delegates ViewImpl
// behavior out to WebContentsImpl in src/content. As such it is a transitional
// API which will be removed as soon as WebContentsImpl itself can be fully
// migrated into Content Service.
//
// Each instance of this interface is constructed by the ContentServiceDelegate
// implementation and owned by a ViewImpl.
class ViewDelegate {
public:
virtual ~ViewDelegate() {}
// Navigates the content object to a new URL.
virtual void Navigate(const GURL& url) = 0;
};
} // namespace content
#endif // SERVICES_CONTENT_VIEW_DELEGATE_H_
// Copyright 2018 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 "services/content/view_factory_impl.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "services/content/service.h"
#include "services/content/view_impl.h"
namespace content {
ViewFactoryImpl::ViewFactoryImpl(Service* service,
mojom::ViewFactoryRequest request)
: service_(service), binding_(this, std::move(request)) {
binding_.set_connection_error_handler(base::BindOnce(
&Service::RemoveViewFactory, base::Unretained(service_), this));
}
ViewFactoryImpl::~ViewFactoryImpl() = default;
void ViewFactoryImpl::CreateView(mojom::ViewParamsPtr params,
mojom::ViewRequest request,
mojom::ViewClientPtr client) {
service_->AddView(std::make_unique<ViewImpl>(
service_, std::move(params), std::move(request), std::move(client)));
}
} // namespace content
// Copyright 2018 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 SERVICES_CONTENT_VIEW_FACTORY_IMPL_H_
#define SERVICES_CONTENT_VIEW_FACTORY_IMPL_H_
#include "base/macros.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/content/public/mojom/view_factory.mojom.h"
namespace content {
class Service;
class ViewFactoryImpl : public mojom::ViewFactory {
public:
ViewFactoryImpl(Service* service, mojom::ViewFactoryRequest request);
~ViewFactoryImpl() override;
private:
// mojom::ViewFactory:
void CreateView(mojom::ViewParamsPtr params,
mojom::ViewRequest request,
mojom::ViewClientPtr client) override;
Service* const service_;
mojo::Binding<mojom::ViewFactory> binding_;
DISALLOW_COPY_AND_ASSIGN(ViewFactoryImpl);
};
} // namespace content
#endif // SERVICES_CONTENT_VIEW_FACTORY_IMPL_H_
// Copyright 2018 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 "services/content/view_impl.h"
#include "base/bind.h"
#include "services/content/service.h"
#include "services/content/service_delegate.h"
#include "services/content/view_delegate.h"
namespace content {
ViewImpl::ViewImpl(Service* service,
mojom::ViewParamsPtr params,
mojom::ViewRequest request,
mojom::ViewClientPtr client)
: service_(service),
binding_(this, std::move(request)),
client_(std::move(client)),
delegate_(service_->delegate()->CreateViewDelegate(client_.get())) {
binding_.set_connection_error_handler(base::BindRepeating(
&Service::RemoveView, base::Unretained(service_), this));
}
ViewImpl::~ViewImpl() = default;
void ViewImpl::Navigate(const GURL& url) {
// Ignore non-HTTP/HTTPS requests for now.
if (!url.SchemeIsHTTPOrHTTPS())
return;
delegate_->Navigate(url);
}
} // namespace content
// Copyright 2018 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 SERVICES_CONTENT_VIEW_IMPL_H_
#define SERVICES_CONTENT_VIEW_IMPL_H_
#include "base/macros.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/content/public/mojom/view.mojom.h"
#include "services/content/public/mojom/view_factory.mojom.h"
namespace content {
class Service;
class ViewDelegate;
class ViewImpl : public mojom::View {
public:
ViewImpl(Service* service,
mojom::ViewParamsPtr params,
mojom::ViewRequest request,
mojom::ViewClientPtr client);
~ViewImpl() override;
private:
// mojom::ContentView:
void Navigate(const GURL& url) override;
Service* const service_;
mojo::Binding<mojom::View> binding_;
mojom::ViewClientPtr client_;
std::unique_ptr<ViewDelegate> delegate_;
DISALLOW_COPY_AND_ASSIGN(ViewImpl);
};
} // namespace content
#endif // SERVICES_CONTENT_VIEW_IMPL_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment