Skip to content

Commit

Permalink
Refactor Blink's ServiceConnector and add ability to mock in layout t…
Browse files Browse the repository at this point in the history
…ests

This CL refactors the implementation of Blink's ServiceConnector so that
layout tests can mock remote interfaces of arbitrary services in JS.
Specifically, it changes the organization to be roughly parallel to
Platform::interfaceProvider().

- Blink has a Connector interface.
- Platform has a connector() method.
- //content/renderer has a BlinkConnectorImpl backed directly by
  //content/renderer's Connector.
- BlinkConnectorImpl also has the ability to override interfaces with local
  implementations, similar to service_manager::InterfaceProvider.
- We add a JS wrapper of BlinkConnectorImpl to expose that ability to JS.

Review-Url: https://codereview.chromium.org/2643063002
Cr-Commit-Position: refs/heads/master@{#459064}
  • Loading branch information
colinblundell authored and Commit bot committed Mar 23, 2017
1 parent 70fc37e commit 43dd7b3
Show file tree
Hide file tree
Showing 22 changed files with 620 additions and 123 deletions.
15 changes: 0 additions & 15 deletions content/child/blink_platform_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@
#include "content/public/common/content_client.h"
#include "content/public/common/service_manager_connection.h"
#include "net/base/net_errors.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/interfaces/connector.mojom.h"
#include "third_party/WebKit/public/platform/WebData.h"
#include "third_party/WebKit/public/platform/WebFloatPoint.h"
#include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
Expand Down Expand Up @@ -786,19 +784,6 @@ bool BlinkPlatformImpl::databaseSetFileSize(
return false;
}

void BlinkPlatformImpl::bindServiceConnector(
mojo::ScopedMessagePipeHandle remote_handle) {
if (!ChildThreadImpl::current())
return;

service_manager::mojom::ConnectorRequest chromium_request;
chromium_request.Bind(std::move(remote_handle));
ChildThreadImpl::current()
->GetServiceManagerConnection()
->GetConnector()
->BindConnectorRequest(std::move(chromium_request));
}

size_t BlinkPlatformImpl::actualMemoryUsageMB() {
return GetMemoryUsageKB() >> 10;
}
Expand Down
3 changes: 0 additions & 3 deletions content/child/blink_platform_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@ class CONTENT_EXPORT BlinkPlatformImpl
size_t actualMemoryUsageMB() override;
size_t numberOfProcessors() override;

void bindServiceConnector(
mojo::ScopedMessagePipeHandle remote_handle) override;

size_t maxDecodedImageBytes() override;
uint32_t getUniqueIdForProcess() override;
blink::WebString userAgent() override;
Expand Down
4 changes: 4 additions & 0 deletions content/renderer/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ target(link_target_type, "renderer") {
"media/webmediaplayer_ms_compositor.h",
"menu_item_builder.cc",
"menu_item_builder.h",
"mojo/blink_connector_impl.cc",
"mojo/blink_connector_impl.h",
"mojo/blink_connector_js_wrapper.cc",
"mojo/blink_connector_js_wrapper.h",
"mojo/blink_interface_provider_impl.cc",
"mojo/blink_interface_provider_impl.h",
"mojo/blink_interface_registry_impl.cc",
Expand Down
90 changes: 90 additions & 0 deletions content/renderer/mojo/blink_connector_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2017 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/renderer/mojo/blink_connector_impl.h"

#include <utility>

#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "services/service_manager/public/cpp/identity.h"
#include "services/service_manager/public/cpp/interface_provider.h"

namespace content {

BlinkConnectorImpl::BlinkConnectorImpl(
std::unique_ptr<service_manager::Connector> connector)
: connector_(std::move(connector)),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
weak_ptr_factory_(this) {}

BlinkConnectorImpl::~BlinkConnectorImpl() = default;

void BlinkConnectorImpl::bindInterface(const char* service_name,
const char* interface_name,
mojo::ScopedMessagePipeHandle handle) {
// |connector_| is null in some testing contexts.
if (!connector_)
return;

if (!main_thread_task_runner_->BelongsToCurrentThread()) {
main_thread_task_runner_->PostTask(
FROM_HERE,
base::Bind(&BlinkConnectorImpl::bindInterface, GetWeakPtr(),
service_name, interface_name, base::Passed(&handle)));
return;
}

// Tests might have overridden this interface with a local implementation.
InterfaceBinderMap* overrides_for_service =
GetOverridesForService(service_name);
if (overrides_for_service) {
auto it = overrides_for_service->find(std::string(interface_name));
if (it != overrides_for_service->end()) {
it->second.Run(std::move(handle));
return;
}
}

service_manager::Identity service_identity(
service_name, service_manager::mojom::kInheritUserID);
connector_->BindInterface(service_identity, interface_name,
std::move(handle));
}

void BlinkConnectorImpl::AddOverrideForTesting(
const std::string& service_name,
const std::string& interface_name,
const base::Callback<void(mojo::ScopedMessagePipeHandle)>& binder) {
if (service_binders_.find(service_name) == service_binders_.end())
service_binders_[service_name] = base::MakeUnique<InterfaceBinderMap>();

(*(service_binders_[service_name]))[interface_name] = binder;
}

void BlinkConnectorImpl::ClearOverridesForTesting() {
service_binders_.clear();
}

base::WeakPtr<BlinkConnectorImpl> BlinkConnectorImpl::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}

BlinkConnectorImpl::InterfaceBinderMap*
BlinkConnectorImpl::GetOverridesForService(const char* service_name) {
// Short-circuit out in the case where there are no overrides (always true in
// production).
if (service_binders_.empty())
return nullptr;

auto it = service_binders_.find(std::string(service_name));

if (it != service_binders_.end())
return it->second.get();

return nullptr;
}

} // namespace content
73 changes: 73 additions & 0 deletions content/renderer/mojo/blink_connector_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2017 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_RENDERER_MOJO_BLINK_CONNECTOR_IMPL_H_
#define CONTENT_RENDERER_MOJO_BLINK_CONNECTOR_IMPL_H_

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "services/service_manager/public/cpp/connector.h"
#include "third_party/WebKit/public/platform/Connector.h"

namespace base {
class SingleThreadTaskRunner;
}

namespace content {

// An implementation of blink::Connector that forwards to a
// service_manager::Connector.
class CONTENT_EXPORT BlinkConnectorImpl : public blink::Connector {
public:
explicit BlinkConnectorImpl(
std::unique_ptr<service_manager::Connector> connector);
~BlinkConnectorImpl();

// blink::Connector override.
void bindInterface(const char* service_name,
const char* interface_name,
mojo::ScopedMessagePipeHandle handle) override;

void AddOverrideForTesting(
const std::string& service_name,
const std::string& interface_name,
const base::Callback<void(mojo::ScopedMessagePipeHandle)>& binder);

void ClearOverridesForTesting();

void SetConnector(std::unique_ptr<service_manager::Connector> connector) {
connector_ = std::move(connector);
}

base::WeakPtr<BlinkConnectorImpl> GetWeakPtr();

private:
using Binder = base::Callback<void(mojo::ScopedMessagePipeHandle)>;
using InterfaceBinderMap = std::map<std::string, Binder>;
using ServiceBinderMap =
std::map<std::string, std::unique_ptr<InterfaceBinderMap>>;

// Returns a pointer to the InterfaceBinderMap in action for |service_name|,
// or nullptr if |service_name| has no overrides in action.
InterfaceBinderMap* GetOverridesForService(const char* service_name);

// Maps service names to the per-interface overrides that have been set for
// that service.
ServiceBinderMap service_binders_;

std::unique_ptr<service_manager::Connector> connector_;

scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;

base::WeakPtrFactory<BlinkConnectorImpl> weak_ptr_factory_;

DISALLOW_COPY_AND_ASSIGN(BlinkConnectorImpl);
};

} // namespace content

#endif // CONTENT_RENDERER_MOJO_BLINK_CONNECTOR_IMPL_H_
158 changes: 158 additions & 0 deletions content/renderer/mojo/blink_connector_impl_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Copyright 2017 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/renderer/mojo/blink_connector_impl.h"

#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "services/service_manager/public/cpp/connector.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

namespace {
class MockConnector : public service_manager::Connector {
public:
MockConnector(int* foo_baz_impl_requests, int* bar_baz_impl_requests)
: foo_baz_impl_requests_(foo_baz_impl_requests),
bar_baz_impl_requests_(bar_baz_impl_requests),
weak_factory_(this) {}

// Connector:
void StartService(const service_manager::Identity& identity,
service_manager::mojom::ServicePtr service,
service_manager::mojom::PIDReceiverRequest
pid_receiver_request) override {}
std::unique_ptr<service_manager::Connection> Connect(
const std::string& name) override {
return nullptr;
}
std::unique_ptr<service_manager::Connection> Connect(
const service_manager::Identity& target) override {
return nullptr;
}
void BindInterface(const service_manager::Identity& target,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override;
std::unique_ptr<Connector> Clone() override { return nullptr; }
void BindConnectorRequest(
service_manager::mojom::ConnectorRequest request) override {}
base::WeakPtr<Connector> GetWeakPtr() override {
return weak_factory_.GetWeakPtr();
}

protected:
void OverrideBinderForTesting(const std::string& interface_name,
const TestApi::Binder& binder) override {}
void ClearBinderOverrides() override {}

private:
int* foo_baz_impl_requests_;
int* bar_baz_impl_requests_;
base::WeakPtrFactory<MockConnector> weak_factory_;
};

void MockConnector::BindInterface(
const service_manager::Identity& target,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
if (target.name() == "foo" && interface_name == "baz") {
(*foo_baz_impl_requests_)++;
return;
}

if (target.name() == "bar" && interface_name == "baz") {
(*bar_baz_impl_requests_)++;
return;
}

NOTREACHED();
}

} // namespace

class BlinkConnectorImplTest : public testing::Test {
public:
BlinkConnectorImplTest()
: foo_baz_impl_requests_(0),
bar_baz_impl_requests_(0),
foo_baz_override_requests_(0),
bar_baz_override_requests_(0),
connector_(base::MakeUnique<MockConnector>(&foo_baz_impl_requests_,
&bar_baz_impl_requests_)) {}

void OverrideFooBaz(mojo::ScopedMessagePipeHandle interface_pipe) {
foo_baz_override_requests_++;
}

void OverrideBarBaz(mojo::ScopedMessagePipeHandle interface_pipe) {
bar_baz_override_requests_++;
}

protected:
BlinkConnectorImpl* connector() { return &connector_; }
int foo_baz_impl_requests() { return foo_baz_impl_requests_; }
int bar_baz_impl_requests() { return bar_baz_impl_requests_; }
int foo_baz_override_requests() { return foo_baz_override_requests_; }
int bar_baz_override_requests() { return bar_baz_override_requests_; }

private:
base::MessageLoop loop_;
int foo_baz_impl_requests_;
int bar_baz_impl_requests_;
int foo_baz_override_requests_;
int bar_baz_override_requests_;
BlinkConnectorImpl connector_;

DISALLOW_COPY_AND_ASSIGN(BlinkConnectorImplTest);
};

TEST_F(BlinkConnectorImplTest, Basic) {
EXPECT_EQ(0, foo_baz_impl_requests());
EXPECT_EQ(0, bar_baz_impl_requests());

mojo::MessagePipe pipe1;
connector()->bindInterface("foo", "baz", std::move(pipe1.handle0));
EXPECT_EQ(1, foo_baz_impl_requests());
EXPECT_EQ(0, bar_baz_impl_requests());

mojo::MessagePipe pipe2;
connector()->bindInterface("bar", "baz", std::move(pipe2.handle0));
EXPECT_EQ(1, foo_baz_impl_requests());
EXPECT_EQ(1, bar_baz_impl_requests());
}

TEST_F(BlinkConnectorImplTest, Override) {
EXPECT_EQ(0, foo_baz_impl_requests());
EXPECT_EQ(0, foo_baz_override_requests());
EXPECT_EQ(0, bar_baz_impl_requests());

connector()->AddOverrideForTesting(
"foo", "baz",
base::Bind(&BlinkConnectorImplTest::OverrideFooBaz,
base::Unretained(this)));

mojo::MessagePipe pipe1;
connector()->bindInterface("foo", "baz", std::move(pipe1.handle0));
EXPECT_EQ(0, foo_baz_impl_requests());
EXPECT_EQ(1, foo_baz_override_requests());
EXPECT_EQ(0, bar_baz_impl_requests());

mojo::MessagePipe pipe2;
connector()->bindInterface("bar", "baz", std::move(pipe2.handle0));
EXPECT_EQ(0, foo_baz_impl_requests());
EXPECT_EQ(1, foo_baz_override_requests());
EXPECT_EQ(1, bar_baz_impl_requests());

connector()->ClearOverridesForTesting();

mojo::MessagePipe pipe3;
connector()->bindInterface("foo", "baz", std::move(pipe3.handle0));
EXPECT_EQ(1, foo_baz_impl_requests());
EXPECT_EQ(1, foo_baz_override_requests());
EXPECT_EQ(1, bar_baz_impl_requests());
}

} // namespace content
Loading

0 comments on commit 43dd7b3

Please sign in to comment.