Commit 54fe8266 authored by Pavel Feldman's avatar Pavel Feldman

Headless: deprecate and remove the tab socket transport.

Change-Id: Ifc9831e573697d3a75370cdee487b1d3238f0a5d
Reviewed-on: https://chromium-review.googlesource.com/1056055Reviewed-by: default avatarEric Seckler <eseckler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558573}
parent 271ca272
...@@ -108,24 +108,6 @@ action("embed_resources") { ...@@ -108,24 +108,6 @@ action("embed_resources") {
] ]
} }
mojom("tab_socket") {
sources = [
"lib/tab_socket.mojom",
]
public_deps = [
"//mojo/public/mojom/base",
]
}
mojom("headless_render_frame_controller") {
sources = [
"lib/headless_render_frame_controller.mojom",
]
public_deps = [
"//mojo/public/mojom/base",
]
}
service_manifest("headless_browser_manifest_overlay") { service_manifest("headless_browser_manifest_overlay") {
source = "lib/browser/headless_browser_manifest_overlay.json" source = "lib/browser/headless_browser_manifest_overlay.json"
} }
...@@ -150,8 +132,6 @@ grit("resources") { ...@@ -150,8 +132,6 @@ grit("resources") {
deps = [ deps = [
":headless_browser_manifest_overlay", ":headless_browser_manifest_overlay",
":headless_packaged_services_manifest_overlay", ":headless_packaged_services_manifest_overlay",
":headless_render_frame_controller_js",
":tab_socket_js",
] ]
} }
...@@ -338,8 +318,6 @@ component("headless") { ...@@ -338,8 +318,6 @@ component("headless") {
"lib/browser/headless_resource_dispatcher_host_delegate.h", "lib/browser/headless_resource_dispatcher_host_delegate.h",
"lib/browser/headless_shell_application_mac.h", "lib/browser/headless_shell_application_mac.h",
"lib/browser/headless_shell_application_mac.mm", "lib/browser/headless_shell_application_mac.mm",
"lib/browser/headless_tab_socket_impl.cc",
"lib/browser/headless_tab_socket_impl.h",
"lib/browser/headless_url_request_context_getter.cc", "lib/browser/headless_url_request_context_getter.cc",
"lib/browser/headless_url_request_context_getter.h", "lib/browser/headless_url_request_context_getter.h",
"lib/browser/headless_window_tree_host.h", "lib/browser/headless_window_tree_host.h",
...@@ -453,9 +431,7 @@ component("headless") { ...@@ -453,9 +431,7 @@ component("headless") {
deps = [ deps = [
":gen_devtools_client_api", ":gen_devtools_client_api",
":headless_render_frame_controller",
":protocol_sources", ":protocol_sources",
":tab_socket",
":version_header", ":version_header",
"//components/cookie_config", "//components/cookie_config",
"//components/security_state/core", "//components/security_state/core",
...@@ -483,10 +459,6 @@ component("headless") { ...@@ -483,10 +459,6 @@ component("headless") {
"lib/headless_content_main_delegate.h", "lib/headless_content_main_delegate.h",
"lib/renderer/headless_content_renderer_client.cc", "lib/renderer/headless_content_renderer_client.cc",
"lib/renderer/headless_content_renderer_client.h", "lib/renderer/headless_content_renderer_client.h",
"lib/renderer/headless_render_frame_controller_impl.cc",
"lib/renderer/headless_render_frame_controller_impl.h",
"lib/renderer/headless_tab_socket_bindings.cc",
"lib/renderer/headless_tab_socket_bindings.h",
"lib/utility/headless_content_utility_client.cc", "lib/utility/headless_content_utility_client.cc",
"lib/utility/headless_content_utility_client.h", "lib/utility/headless_content_utility_client.h",
] ]
...@@ -569,10 +541,6 @@ if (!is_component_build) { ...@@ -569,10 +541,6 @@ if (!is_component_build) {
"lib/headless_content_main_delegate.h", "lib/headless_content_main_delegate.h",
"lib/renderer/headless_content_renderer_client.cc", "lib/renderer/headless_content_renderer_client.cc",
"lib/renderer/headless_content_renderer_client.h", "lib/renderer/headless_content_renderer_client.h",
"lib/renderer/headless_render_frame_controller_impl.cc",
"lib/renderer/headless_render_frame_controller_impl.h",
"lib/renderer/headless_tab_socket_bindings.cc",
"lib/renderer/headless_tab_socket_bindings.h",
] ]
deps = [ deps = [
...@@ -792,8 +760,6 @@ test("headless_browsertests") { ...@@ -792,8 +760,6 @@ test("headless_browsertests") {
"test/headless_browser_test.cc", "test/headless_browser_test.cc",
"test/headless_browser_test.h", "test/headless_browser_test.h",
"test/headless_test_launcher.cc", "test/headless_test_launcher.cc",
"test/tab_socket_test.cc",
"test/tab_socket_test.h",
"test/test_protocol_handler.cc", "test/test_protocol_handler.cc",
"test/test_protocol_handler.h", "test/test_protocol_handler.h",
"test/test_url_request_job.cc", "test/test_url_request_job.cc",
......
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
/** /**
* @fileoverview Contains a class which marshals DevTools protocol messages over * @fileoverview Contains a class which marshals DevTools protocol messages over
* a provided low level message transport. This transport might be a headless * a provided low level message transport.
* TabSocket, or a WebSocket or a mock for testing.
*/ */
'use strict'; 'use strict';
......
...@@ -506,16 +506,6 @@ HeadlessBrowserContext::Builder::SetAllowCookies(bool allow_cookies) { ...@@ -506,16 +506,6 @@ HeadlessBrowserContext::Builder::SetAllowCookies(bool allow_cookies) {
return *this; return *this;
} }
HeadlessBrowserContext::Builder&
HeadlessBrowserContext::Builder::AddTabSocketMojoBindings() {
std::string js_bindings =
ui::ResourceBundle::GetSharedInstance()
.GetRawDataResource(IDR_HEADLESS_TAB_SOCKET_MOJOM_JS)
.as_string();
mojo_bindings_.emplace_back("headless/lib/tab_socket.mojom", js_bindings);
return *this;
}
HeadlessBrowserContext::Builder& HeadlessBrowserContext::Builder&
HeadlessBrowserContext::Builder::EnableUnsafeNetworkAccessWithMojoBindings( HeadlessBrowserContext::Builder::EnableUnsafeNetworkAccessWithMojoBindings(
bool enable_http_and_https_if_mojo_used) { bool enable_http_and_https_if_mojo_used) {
......
...@@ -53,8 +53,6 @@ ...@@ -53,8 +53,6 @@
namespace headless { namespace headless {
namespace { namespace {
const char kCapabilityPath[] =
"interface_provider_specs.navigation:frame.provides.renderer";
#if defined(HEADLESS_USE_BREAKPAD) #if defined(HEADLESS_USE_BREAKPAD)
breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost( breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost(
...@@ -174,21 +172,7 @@ HeadlessContentBrowserClient::GetBrowserServiceManifestOverlay() { ...@@ -174,21 +172,7 @@ HeadlessContentBrowserClient::GetBrowserServiceManifestOverlay() {
base::StringPiece manifest_template = base::StringPiece manifest_template =
ui::ResourceBundle::GetSharedInstance().GetRawDataResource( ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_HEADLESS_BROWSER_MANIFEST_OVERLAY); IDR_HEADLESS_BROWSER_MANIFEST_OVERLAY);
std::unique_ptr<base::Value> manifest = return base::JSONReader::Read(manifest_template);
base::JSONReader::Read(manifest_template);
// Add mojo_service_names to renderer capability specified in options.
base::DictionaryValue* manifest_dictionary = nullptr;
CHECK(manifest->GetAsDictionary(&manifest_dictionary));
base::ListValue* capability_list = nullptr;
CHECK(manifest_dictionary->GetList(kCapabilityPath, &capability_list));
for (std::string service_name : browser_->options()->mojo_service_names) {
capability_list->AppendString(service_name);
}
return manifest;
} }
std::unique_ptr<base::Value> std::unique_ptr<base::Value>
......
// 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 "headless/lib/browser/headless_tab_socket_impl.h"
#include "base/stl_util.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/interface_provider.h"
namespace headless {
HeadlessTabSocketImpl::HeadlessTabSocketImpl(content::WebContents* web_contents)
: web_contents_(web_contents),
listener_(nullptr),
weak_ptr_factory_(this) {}
HeadlessTabSocketImpl::~HeadlessTabSocketImpl() = default;
// Wrangles the async responses to
// HeadlessRenderFrameControllerImpl::InstallTabSocket for which at most one
// should succeed.
class TabSocketInstallationController
: public base::RefCounted<TabSocketInstallationController> {
public:
TabSocketInstallationController(
int v8_execution_context_id,
size_t render_frame_count,
base::WeakPtr<HeadlessTabSocketImpl> headless_tab_socket_impl,
base::OnceCallback<void(bool)> callback)
: v8_execution_context_id_(v8_execution_context_id),
render_frame_count_(render_frame_count),
headless_tab_socket_impl_(headless_tab_socket_impl),
callback_(std::move(callback)),
success_(false) {}
void InstallTabSocketCallback(content::RenderFrameHost* render_frame_host,
bool success) {
render_frame_count_--;
// It's possible the HeadlessTabSocketImpl went away, if that happened we
// don't want to pretend TabSocket installation succeeded.
if (!headless_tab_socket_impl_)
success = false;
if (success) {
CHECK(!success_) << "At most one InstallTabSocket call should succeed!";
success_ = true;
headless_tab_socket_impl_->v8_execution_context_id_to_render_frame_host_
.insert(std::make_pair(v8_execution_context_id_, render_frame_host));
std::move(callback_).Run(true);
} else if (render_frame_count_ == 0 && !success_) {
std::move(callback_).Run(false);
}
}
private:
int v8_execution_context_id_;
size_t render_frame_count_;
base::WeakPtr<HeadlessTabSocketImpl> headless_tab_socket_impl_;
base::OnceCallback<void(bool)> callback_;
bool success_;
friend class base::RefCounted<TabSocketInstallationController>;
~TabSocketInstallationController() = default;
};
void HeadlessTabSocketImpl::InstallHeadlessTabSocketBindings(
int v8_execution_context_id,
base::OnceCallback<void(bool)> callback) {
// We need to find the right RenderFrameHost to install the bindings on but
// the browser doesn't know which RenderFrameHost |v8_execution_context_id|
// corresponds to if any. So we try all of them.
scoped_refptr<TabSocketInstallationController>
tab_socket_installation_controller = new TabSocketInstallationController(
v8_execution_context_id, render_frame_hosts_.size(),
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
for (content::RenderFrameHost* render_frame_host : render_frame_hosts_) {
HeadlessRenderFrameControllerPtr& headless_render_frame_controller =
render_frame_controllers_[render_frame_host];
if (!headless_render_frame_controller.is_bound()) {
render_frame_host->GetRemoteInterfaces()->GetInterface(
&headless_render_frame_controller);
}
// This will only succeed if the |render_frame_host_controller| contains
// |v8_execution_context_id|. The TabSocketInstallationController keeps
// track of how many callbacks have been received and if all of them have
// been unsuccessful it runs |callback| with false. If one of them succeeds
// it runs |callback| with true.
headless_render_frame_controller->InstallTabSocket(
v8_execution_context_id,
base::BindOnce(
&TabSocketInstallationController::InstallTabSocketCallback,
tab_socket_installation_controller, render_frame_host));
}
}
void HeadlessTabSocketImpl::InstallMainFrameMainWorldHeadlessTabSocketBindings(
base::OnceCallback<void(base::Optional<int>)> callback) {
content::RenderFrameHost* main_frame = web_contents_->GetMainFrame();
HeadlessRenderFrameControllerPtr& headless_render_frame_controller =
render_frame_controllers_[main_frame];
if (!headless_render_frame_controller.is_bound()) {
main_frame->GetRemoteInterfaces()->GetInterface(
&headless_render_frame_controller);
}
headless_render_frame_controller->InstallMainWorldTabSocket(
base::BindOnce(&HeadlessTabSocketImpl::OnInstallMainWorldTabSocket,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void HeadlessTabSocketImpl::OnInstallMainWorldTabSocket(
base::OnceCallback<void(base::Optional<int>)> callback,
int v8_execution_context_id) {
if (v8_execution_context_id == -1) {
std::move(callback).Run(base::nullopt);
} else {
v8_execution_context_id_to_render_frame_host_.insert(
std::make_pair(v8_execution_context_id, web_contents_->GetMainFrame()));
std::move(callback).Run(v8_execution_context_id);
}
}
void HeadlessTabSocketImpl::SendMessageToContext(
const std::string& message,
int32_t v8_execution_context_id) {
auto render_frame_host = v8_execution_context_id_to_render_frame_host_.find(
v8_execution_context_id);
if (render_frame_host ==
v8_execution_context_id_to_render_frame_host_.end()) {
LOG(WARNING) << "Unknown v8_execution_context_id "
<< v8_execution_context_id;
return;
}
auto render_frame_controller =
render_frame_controllers_.find(render_frame_host->second);
if (render_frame_controller == render_frame_controllers_.end()) {
LOG(WARNING) << "Unknown RenderFrameHist " << render_frame_host->second;
return;
}
render_frame_controller->second->SendMessageToTabSocket(
message, v8_execution_context_id);
}
void HeadlessTabSocketImpl::SetListener(Listener* listener) {
MessageQueue messages;
{
base::AutoLock lock(lock_);
listener_ = listener;
if (!listener)
return;
std::swap(messages, from_tab_message_queue_);
}
for (const Message& message : messages) {
listener_->OnMessageFromContext(message.first, message.second);
}
}
void HeadlessTabSocketImpl::SendMessageToEmbedder(
const std::string& message,
int32_t v8_execution_context_id) {
Listener* listener = nullptr;
{
base::AutoLock lock(lock_);
CHECK(v8_execution_context_id_to_render_frame_host_.find(
v8_execution_context_id) !=
v8_execution_context_id_to_render_frame_host_.end())
<< "Unknown v8_execution_context_id " << v8_execution_context_id;
if (listener_) {
listener = listener_;
} else {
from_tab_message_queue_.emplace_back(message, v8_execution_context_id);
return;
}
}
listener->OnMessageFromContext(message, v8_execution_context_id);
}
void HeadlessTabSocketImpl::CreateMojoService(
mojo::InterfaceRequest<TabSocket> request) {
mojo_bindings_.AddBinding(this, std::move(request));
}
void HeadlessTabSocketImpl::RenderFrameCreated(
content::RenderFrameHost* render_frame_host) {
render_frame_hosts_.insert(render_frame_host);
}
void HeadlessTabSocketImpl::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
// Remove all entries from |v8_execution_context_id_to_render_frame_host_|
// where the mapped value is |render_frame_host|.
base::EraseIf(v8_execution_context_id_to_render_frame_host_,
[render_frame_host](
const std::pair<int, content::RenderFrameHost*>& pair) {
return render_frame_host == pair.second;
});
render_frame_controllers_.erase(render_frame_host);
render_frame_hosts_.erase(render_frame_host);
}
} // namespace headless
// 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 HEADLESS_LIB_BROWSER_HEADLESS_TAB_SOCKET_IMPL_H_
#define HEADLESS_LIB_BROWSER_HEADLESS_TAB_SOCKET_IMPL_H_
#include <list>
#include "base/synchronization/lock.h"
#include "headless/lib/headless_render_frame_controller.mojom.h"
#include "headless/lib/tab_socket.mojom.h"
#include "headless/public/headless_tab_socket.h"
#include "mojo/public/cpp/bindings/binding_set.h"
namespace content {
class RenderFrameHost;
class WebContents;
} // namespace content
namespace headless {
class HeadlessTabSocketImpl : public HeadlessTabSocket, public TabSocket {
public:
explicit HeadlessTabSocketImpl(content::WebContents* web_contents);
~HeadlessTabSocketImpl() override;
// HeadlessTabSocket implementation:
void InstallHeadlessTabSocketBindings(
int v8_execution_context_id,
base::OnceCallback<void(bool)> callback) override;
void InstallMainFrameMainWorldHeadlessTabSocketBindings(
base::OnceCallback<void(base::Optional<int>)> callback) override;
void SendMessageToContext(const std::string& message,
int v8_execution_context_id) override;
void SetListener(Listener* listener) override;
// TabSocket implementation:
void SendMessageToEmbedder(const std::string& message,
int32_t v8_execution_context_id) override;
void CreateMojoService(mojo::InterfaceRequest<TabSocket> request);
void RenderFrameCreated(content::RenderFrameHost* render_frame_host);
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host);
private:
friend class TabSocketInstallationController;
void OnInstallMainWorldTabSocket(
base::OnceCallback<void(base::Optional<int>)> callback,
int world_id);
base::Lock lock_; // Protects everything below.
using Message = std::pair<std::string, int>;
using MessageQueue = std::list<Message>;
MessageQueue from_tab_message_queue_;
content::WebContents* web_contents_; // NOT OWNED
Listener* listener_; // NOT OWNED
mojo::BindingSet<TabSocket> mojo_bindings_;
std::set<content::RenderFrameHost*> render_frame_hosts_;
std::map<int, content::RenderFrameHost*>
v8_execution_context_id_to_render_frame_host_;
std::map<content::RenderFrameHost*, HeadlessRenderFrameControllerPtr>
render_frame_controllers_;
base::WeakPtrFactory<HeadlessTabSocketImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HeadlessTabSocketImpl);
};
} // namespace headless
#endif // HEADLESS_LIB_BROWSER_HEADLESS_TAB_SOCKET_IMPL_H_
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include "headless/lib/browser/headless_browser_context_impl.h" #include "headless/lib/browser/headless_browser_context_impl.h"
#include "headless/lib/browser/headless_browser_impl.h" #include "headless/lib/browser/headless_browser_impl.h"
#include "headless/lib/browser/headless_browser_main_parts.h" #include "headless/lib/browser/headless_browser_main_parts.h"
#include "headless/lib/browser/headless_tab_socket_impl.h"
#include "headless/lib/browser/protocol/headless_handler.h" #include "headless/lib/browser/protocol/headless_handler.h"
#include "headless/public/internal/headless_devtools_client_impl.h" #include "headless/public/internal/headless_devtools_client_impl.h"
#include "printing/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h"
...@@ -139,8 +138,6 @@ class HeadlessWebContentsImpl::Delegate : public content::WebContentsDelegate { ...@@ -139,8 +138,6 @@ class HeadlessWebContentsImpl::Delegate : public content::WebContentsDelegate {
HeadlessWebContentsImpl* child_contents = HeadlessWebContentsImpl::From( HeadlessWebContentsImpl* child_contents = HeadlessWebContentsImpl::From(
headless_web_contents_->browser_context() headless_web_contents_->browser_context()
->CreateWebContentsBuilder() ->CreateWebContentsBuilder()
.SetAllowTabSockets(
!!headless_web_contents_->GetHeadlessTabSocket())
.SetWindowSize(source->GetContainerBounds().size()) .SetWindowSize(source->GetContainerBounds().size())
.Build()); .Build());
headless_web_contents_->browser_context()->NotifyChildContentsCreated( headless_web_contents_->browser_context()->NotifyChildContentsCreated(
...@@ -204,17 +201,6 @@ class HeadlessWebContentsImpl::Delegate : public content::WebContentsDelegate { ...@@ -204,17 +201,6 @@ class HeadlessWebContentsImpl::Delegate : public content::WebContentsDelegate {
DISALLOW_COPY_AND_ASSIGN(Delegate); DISALLOW_COPY_AND_ASSIGN(Delegate);
}; };
namespace {
void CreateTabSocketMojoServiceForContents(
HeadlessWebContents* web_contents,
mojo::ScopedMessagePipeHandle handle) {
HeadlessWebContentsImpl::From(web_contents)
->CreateTabSocketMojoService(std::move(handle));
}
} // namespace
struct HeadlessWebContentsImpl::PendingFrame { struct HeadlessWebContentsImpl::PendingFrame {
public: public:
PendingFrame() = default; PendingFrame() = default;
...@@ -250,17 +236,6 @@ std::unique_ptr<HeadlessWebContentsImpl> HeadlessWebContentsImpl::Create( ...@@ -250,17 +236,6 @@ std::unique_ptr<HeadlessWebContentsImpl> HeadlessWebContentsImpl::Create(
content::WebContents::Create(create_params), content::WebContents::Create(create_params),
builder->browser_context_)); builder->browser_context_));
if (builder->tab_sockets_allowed_) {
headless_web_contents->headless_tab_socket_ =
std::make_unique<HeadlessTabSocketImpl>(
headless_web_contents->web_contents_.get());
headless_web_contents->inject_mojo_services_into_isolated_world_ = true;
builder->mojo_services_.emplace_back(
TabSocket::Name_, base::Bind(&CreateTabSocketMojoServiceForContents));
}
headless_web_contents->mojo_services_ = std::move(builder->mojo_services_);
headless_web_contents->begin_frame_control_enabled_ = headless_web_contents->begin_frame_control_enabled_ =
builder->enable_begin_frame_control_ || builder->enable_begin_frame_control_ ||
headless_web_contents->browser()->options()->enable_begin_frame_control; headless_web_contents->browser()->options()->enable_begin_frame_control;
...@@ -283,15 +258,6 @@ HeadlessWebContentsImpl::CreateForChildContents( ...@@ -283,15 +258,6 @@ HeadlessWebContentsImpl::CreateForChildContents(
child->begin_frame_control_enabled_ = parent->begin_frame_control_enabled_; child->begin_frame_control_enabled_ = parent->begin_frame_control_enabled_;
child->InitializeWindow(child->web_contents_->GetContainerBounds()); child->InitializeWindow(child->web_contents_->GetContainerBounds());
// Copy mojo services and tab socket settings from parent.
child->mojo_services_ = parent->mojo_services_;
if (parent->headless_tab_socket_) {
child->headless_tab_socket_ =
std::make_unique<HeadlessTabSocketImpl>(child->web_contents_.get());
child->inject_mojo_services_into_isolated_world_ =
parent->inject_mojo_services_into_isolated_world_;
}
// There may already be frames, so make sure they also have our services. // There may already be frames, so make sure they also have our services.
for (content::RenderFrameHost* frame_host : for (content::RenderFrameHost* frame_host :
child->web_contents_->GetAllFrames()) child->web_contents_->GetAllFrames())
...@@ -328,7 +294,6 @@ HeadlessWebContentsImpl::HeadlessWebContentsImpl( ...@@ -328,7 +294,6 @@ HeadlessWebContentsImpl::HeadlessWebContentsImpl(
web_contents_(std::move(web_contents)), web_contents_(std::move(web_contents)),
agent_host_( agent_host_(
content::DevToolsAgentHost::GetOrCreateFor(web_contents_.get())), content::DevToolsAgentHost::GetOrCreateFor(web_contents_.get())),
inject_mojo_services_into_isolated_world_(false),
browser_context_(browser_context), browser_context_(browser_context),
render_process_host_(web_contents_->GetMainFrame()->GetProcess()), render_process_host_(web_contents_->GetMainFrame()->GetProcess()),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
...@@ -356,48 +321,17 @@ HeadlessWebContentsImpl::~HeadlessWebContentsImpl() { ...@@ -356,48 +321,17 @@ HeadlessWebContentsImpl::~HeadlessWebContentsImpl() {
} }
} }
void HeadlessWebContentsImpl::CreateTabSocketMojoService(
mojo::ScopedMessagePipeHandle handle) {
headless_tab_socket_->CreateMojoService(TabSocketRequest(std::move(handle)));
}
void HeadlessWebContentsImpl::CreateMojoService(
const MojoService::ServiceFactoryCallback& service_factory,
mojo::ScopedMessagePipeHandle handle) {
service_factory.Run(this, std::move(handle));
}
void HeadlessWebContentsImpl::RenderFrameCreated( void HeadlessWebContentsImpl::RenderFrameCreated(
content::RenderFrameHost* render_frame_host) { content::RenderFrameHost* render_frame_host) {
for (const MojoService& service : mojo_services_) {
registry_.AddInterface(
service.service_name,
base::Bind(&HeadlessWebContentsImpl::CreateMojoService,
base::Unretained(this), service.service_factory),
browser()->BrowserMainThread());
}
browser_context_->SetDevToolsFrameToken( browser_context_->SetDevToolsFrameToken(
render_frame_host->GetProcess()->GetID(), render_frame_host->GetProcess()->GetID(),
render_frame_host->GetRoutingID(), render_frame_host->GetRoutingID(),
render_frame_host->GetDevToolsFrameToken(), render_frame_host->GetDevToolsFrameToken(),
render_frame_host->GetFrameTreeNodeId()); render_frame_host->GetFrameTreeNodeId());
if (headless_tab_socket_)
headless_tab_socket_->RenderFrameCreated(render_frame_host);
}
void HeadlessWebContentsImpl::OnInterfaceRequestFromFrame(
content::RenderFrameHost* render_frame_host,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle* interface_pipe) {
registry_.TryBindInterface(interface_name, interface_pipe);
} }
void HeadlessWebContentsImpl::RenderFrameDeleted( void HeadlessWebContentsImpl::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) { content::RenderFrameHost* render_frame_host) {
if (headless_tab_socket_)
headless_tab_socket_->RenderFrameDeleted(render_frame_host);
browser_context_->RemoveDevToolsFrameToken( browser_context_->RemoveDevToolsFrameToken(
render_frame_host->GetProcess()->GetID(), render_frame_host->GetProcess()->GetID(),
render_frame_host->GetRoutingID(), render_frame_host->GetRoutingID(),
...@@ -540,10 +474,6 @@ HeadlessBrowserContextImpl* HeadlessWebContentsImpl::browser_context() const { ...@@ -540,10 +474,6 @@ HeadlessBrowserContextImpl* HeadlessWebContentsImpl::browser_context() const {
return browser_context_; return browser_context_;
} }
HeadlessTabSocket* HeadlessWebContentsImpl::GetHeadlessTabSocket() const {
return headless_tab_socket_.get();
}
void HeadlessWebContentsImpl::OnDisplayDidFinishFrame( void HeadlessWebContentsImpl::OnDisplayDidFinishFrame(
const viz::BeginFrameAck& ack) { const viz::BeginFrameAck& ack) {
TRACE_EVENT2("headless", "HeadlessWebContentsImpl::OnDisplayDidFinishFrame", TRACE_EVENT2("headless", "HeadlessWebContentsImpl::OnDisplayDidFinishFrame",
...@@ -669,12 +599,6 @@ HeadlessWebContents::Builder& HeadlessWebContents::Builder::SetWindowSize( ...@@ -669,12 +599,6 @@ HeadlessWebContents::Builder& HeadlessWebContents::Builder::SetWindowSize(
return *this; return *this;
} }
HeadlessWebContents::Builder& HeadlessWebContents::Builder::SetAllowTabSockets(
bool tab_sockets_allowed) {
tab_sockets_allowed_ = tab_sockets_allowed;
return *this;
}
HeadlessWebContents::Builder& HeadlessWebContents::Builder&
HeadlessWebContents::Builder::SetEnableBeginFrameControl( HeadlessWebContents::Builder::SetEnableBeginFrameControl(
bool enable_begin_frame_control) { bool enable_begin_frame_control) {
...@@ -686,16 +610,4 @@ HeadlessWebContents* HeadlessWebContents::Builder::Build() { ...@@ -686,16 +610,4 @@ HeadlessWebContents* HeadlessWebContents::Builder::Build() {
return browser_context_->CreateWebContents(this); return browser_context_->CreateWebContents(this);
} }
HeadlessWebContents::Builder::MojoService::MojoService() = default;
HeadlessWebContents::Builder::MojoService::MojoService(
const MojoService& other) = default;
HeadlessWebContents::Builder::MojoService::MojoService(
const std::string& service_name,
const ServiceFactoryCallback& service_factory)
: service_name(service_name), service_factory(service_factory) {}
HeadlessWebContents::Builder::MojoService::~MojoService() = default;
} // namespace headless } // namespace headless
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include "headless/public/headless_devtools_target.h" #include "headless/public/headless_devtools_target.h"
#include "headless/public/headless_export.h" #include "headless/public/headless_export.h"
#include "headless/public/headless_web_contents.h" #include "headless/public/headless_web_contents.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "ui/compositor/external_begin_frame_client.h" #include "ui/compositor/external_begin_frame_client.h"
class SkBitmap; class SkBitmap;
...@@ -36,7 +35,6 @@ class Rect; ...@@ -36,7 +35,6 @@ class Rect;
namespace headless { namespace headless {
class HeadlessBrowser; class HeadlessBrowser;
class HeadlessBrowserImpl; class HeadlessBrowserImpl;
class HeadlessTabSocketImpl;
// Exported for tests. // Exported for tests.
class HEADLESS_EXPORT HeadlessWebContentsImpl class HEADLESS_EXPORT HeadlessWebContentsImpl
...@@ -65,7 +63,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl ...@@ -65,7 +63,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl
void AddObserver(Observer* observer) override; void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override; void RemoveObserver(Observer* observer) override;
HeadlessDevToolsTarget* GetDevToolsTarget() override; HeadlessDevToolsTarget* GetDevToolsTarget() override;
HeadlessTabSocket* GetHeadlessTabSocket() const override;
int GetMainFrameRenderProcessId() const override; int GetMainFrameRenderProcessId() const override;
int GetMainFrameTreeNodeId() const override; int GetMainFrameTreeNodeId() const override;
std::string GetMainFrameDevToolsId() const override; std::string GetMainFrameDevToolsId() const override;
...@@ -91,10 +88,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl ...@@ -91,10 +88,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl
void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
void RenderViewReady() override; void RenderViewReady() override;
void OnInterfaceRequestFromFrame(
content::RenderFrameHost* render_frame_host,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle* interface_pipe) override;
// ui::ExternalBeginFrameClient implementation: // ui::ExternalBeginFrameClient implementation:
void OnDisplayDidFinishFrame(const viz::BeginFrameAck& ack) override; void OnDisplayDidFinishFrame(const viz::BeginFrameAck& ack) override;
...@@ -129,8 +122,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl ...@@ -129,8 +122,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl
// Set bounds of WebContent's platform window. // Set bounds of WebContent's platform window.
void SetBounds(const gfx::Rect& bounds); void SetBounds(const gfx::Rect& bounds);
void CreateTabSocketMojoService(mojo::ScopedMessagePipeHandle handle);
bool begin_frame_control_enabled() const { bool begin_frame_control_enabled() const {
return begin_frame_control_enabled_; return begin_frame_control_enabled_;
} }
...@@ -159,11 +150,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl ...@@ -159,11 +150,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl
void InitializeWindow(const gfx::Rect& initial_bounds); void InitializeWindow(const gfx::Rect& initial_bounds);
using MojoService = HeadlessWebContents::Builder::MojoService;
void CreateMojoService(
const MojoService::ServiceFactoryCallback& service_factory,
mojo::ScopedMessagePipeHandle handle);
void PendingFrameReadbackComplete(PendingFrame* pending_frame, void PendingFrameReadbackComplete(PendingFrame* pending_frame,
const SkBitmap& bitmap); const SkBitmap& bitmap);
...@@ -179,11 +165,8 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl ...@@ -179,11 +165,8 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl
std::unique_ptr<HeadlessWindowTreeHost> window_tree_host_; std::unique_ptr<HeadlessWindowTreeHost> window_tree_host_;
int window_id_ = 0; int window_id_ = 0;
std::string window_state_; std::string window_state_;
std::unique_ptr<HeadlessTabSocketImpl> headless_tab_socket_;
std::unique_ptr<content::WebContents> web_contents_; std::unique_ptr<content::WebContents> web_contents_;
scoped_refptr<content::DevToolsAgentHost> agent_host_; scoped_refptr<content::DevToolsAgentHost> agent_host_;
std::list<MojoService> mojo_services_;
bool inject_mojo_services_into_isolated_world_;
bool devtools_target_ready_notification_sent_ = false; bool devtools_target_ready_notification_sent_ = false;
bool render_process_exited_ = false; bool render_process_exited_ = false;
...@@ -196,8 +179,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl ...@@ -196,8 +179,6 @@ class HEADLESS_EXPORT HeadlessWebContentsImpl
base::Closure quit_closure_; base::Closure quit_closure_;
service_manager::BinderRegistry registry_;
base::WeakPtrFactory<HeadlessWebContentsImpl> weak_ptr_factory_; base::WeakPtrFactory<HeadlessWebContentsImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HeadlessWebContentsImpl); DISALLOW_COPY_AND_ASSIGN(HeadlessWebContentsImpl);
......
// 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.
module headless;
import "mojo/public/mojom/base/big_string.mojom";
interface HeadlessRenderFrameController {
// Installs TabSocket bindings into a specified execution context.
InstallTabSocket(int32 v8_execution_context_id) => (bool success);
// Installs TabSocket bindings into the main world. This is useful if you
// don't know the execution context id (e.g. you don't have devtools
// connected).
InstallMainWorldTabSocket() => (int32 v8_execution_context_id);
// Send a message from the C++ embedder to the Tab.
SendMessageToTabSocket(mojo_base.mojom.BigString message,
int32 v8_execution_context_id);
// To send a message from tab to the embedder use
// TabSocket::SendMessageToEmbedder.
};
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#include <memory> #include <memory>
#include "headless/lib/renderer/headless_render_frame_controller_impl.h"
#include "printing/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h"
#if BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_PRINTING)
...@@ -26,7 +25,6 @@ void HeadlessContentRendererClient::RenderFrameCreated( ...@@ -26,7 +25,6 @@ void HeadlessContentRendererClient::RenderFrameCreated(
new printing::PrintRenderFrameHelper( new printing::PrintRenderFrameHelper(
render_frame, std::make_unique<HeadlessPrintRenderFrameHelperDelegate>()); render_frame, std::make_unique<HeadlessPrintRenderFrameHelperDelegate>());
#endif #endif
new HeadlessRenderFrameControllerImpl(render_frame);
} }
} // namespace headless } // namespace headless
// 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 "headless/lib/renderer/headless_render_frame_controller_impl.h"
#include "content/public/common/isolated_world_ids.h"
#include "services/service_manager/public/cpp/binder_registry.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "v8/include/v8-inspector.h"
namespace headless {
HeadlessRenderFrameControllerImpl::HeadlessRenderFrameControllerImpl(
content::RenderFrame* render_frame)
: content::RenderFrameObserver(render_frame),
render_frame_(render_frame),
weak_ptr_factory_(this) {
registry_.AddInterface(base::Bind(
&HeadlessRenderFrameControllerImpl::OnRenderFrameControllerRequest,
base::Unretained(this)));
}
HeadlessRenderFrameControllerImpl::~HeadlessRenderFrameControllerImpl() =
default;
void HeadlessRenderFrameControllerImpl::OnRenderFrameControllerRequest(
HeadlessRenderFrameControllerRequest request) {
headless_render_frame_controller_bindings_.AddBinding(this,
std::move(request));
}
void HeadlessRenderFrameControllerImpl::InstallTabSocket(
int32_t execution_context_id,
InstallTabSocketCallback callback) {
auto find_it = tab_socket_bindings_.find(execution_context_id);
if (find_it == tab_socket_bindings_.end()) {
LOG(WARNING) << "InstallTabSocket failed, unknown execution_context_id "
<< execution_context_id;
std::move(callback).Run(false);
} else {
std::move(callback).Run(find_it->second.InitializeTabSocketBindings());
}
}
void HeadlessRenderFrameControllerImpl::InstallMainWorldTabSocket(
InstallMainWorldTabSocketCallback callback) {
// Check any pre-existing script contexts.
for (auto& pair : tab_socket_bindings_) {
if (pair.second.world_id() == content::ISOLATED_WORLD_ID_GLOBAL) {
std::move(callback).Run(
pair.second.InitializeTabSocketBindings() ? pair.first : -1);
return;
}
}
pending_install_main_world_tab_socket_callback_ = std::move(callback);
}
void HeadlessRenderFrameControllerImpl::SendMessageToTabSocket(
const std::string& message,
int32_t world_id) {
auto find_it = tab_socket_bindings_.find(world_id);
if (find_it == tab_socket_bindings_.end()) {
LOG(WARNING) << "Dropping message for " << world_id
<< " because the world doesn't exist.";
return;
}
find_it->second.OnMessageFromEmbedder(message);
}
void HeadlessRenderFrameControllerImpl::OnInterfaceRequestForFrame(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle* interface_pipe) {
registry_.TryBindInterface(interface_name, interface_pipe);
}
void HeadlessRenderFrameControllerImpl::DidCreateScriptContext(
v8::Local<v8::Context> context,
int world_id) {
int v8_execution_context_id =
v8_inspector::V8ContextInfo::executionContextId(context);
auto find_it = tab_socket_bindings_.find(v8_execution_context_id);
if (find_it != tab_socket_bindings_.end())
tab_socket_bindings_.erase(find_it);
auto emplace_result = tab_socket_bindings_.emplace(
std::piecewise_construct, std::forward_as_tuple(v8_execution_context_id),
std::forward_as_tuple(this, render_frame_, context, world_id));
// If main world tab socket bindings have been requested and this is the main
// world then install the bindings.
if (world_id == content::ISOLATED_WORLD_ID_GLOBAL &&
!pending_install_main_world_tab_socket_callback_.is_null()) {
std::move(pending_install_main_world_tab_socket_callback_)
.Run(emplace_result.first->second.InitializeTabSocketBindings()
? v8_execution_context_id
: -1);
pending_install_main_world_tab_socket_callback_ =
InstallMainWorldTabSocketCallback();
}
}
void HeadlessRenderFrameControllerImpl::WillReleaseScriptContext(
v8::Local<v8::Context> context,
int world_id) {
tab_socket_bindings_.erase(
v8_inspector::V8ContextInfo::executionContextId(context));
}
void HeadlessRenderFrameControllerImpl::OnDestruct() {
delete this;
}
TabSocketPtr& HeadlessRenderFrameControllerImpl::EnsureTabSocketPtr() {
if (!tab_socket_ptr_.is_bound()) {
render_frame_->GetRemoteInterfaces()->GetInterface(
mojo::MakeRequest(&tab_socket_ptr_));
}
return tab_socket_ptr_;
}
} // namespace headless
// 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 HEADLESS_LIB_RENDERER_HEADLESS_RENDER_FRAME_CONTROLLER_IMPL_H_
#define HEADLESS_LIB_RENDERER_HEADLESS_RENDER_FRAME_CONTROLLER_IMPL_H_
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
#include "headless/lib/headless_render_frame_controller.mojom.h"
#include "headless/lib/renderer/headless_tab_socket_bindings.h"
#include "headless/lib/tab_socket.mojom.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/service_manager/public/cpp/binder_registry.h"
namespace headless {
class HeadlessRenderFrameControllerImpl : public HeadlessRenderFrameController,
public content::RenderFrameObserver {
public:
explicit HeadlessRenderFrameControllerImpl(
content::RenderFrame* render_frame);
~HeadlessRenderFrameControllerImpl() override;
void OnRenderFrameControllerRequest(
HeadlessRenderFrameControllerRequest request);
// HeadlessRenderFrameController implementation:
void InstallTabSocket(int32_t v8_execution_context_id,
InstallTabSocketCallback callback) override;
void InstallMainWorldTabSocket(
InstallMainWorldTabSocketCallback callback) override;
void SendMessageToTabSocket(const std::string& message,
int32_t world_id) override;
// content::RenderFrameObserver implementation:
void OnInterfaceRequestForFrame(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle* interface_pipe) override;
void DidCreateScriptContext(v8::Local<v8::Context> context,
int world_id) override;
void WillReleaseScriptContext(v8::Local<v8::Context> context,
int world_id) override;
void OnDestruct() override;
TabSocketPtr& EnsureTabSocketPtr();
private:
content::RenderFrame* const render_frame_; // NOT OWNED
mojo::BindingSet<HeadlessRenderFrameController>
headless_render_frame_controller_bindings_;
std::map<int, HeadlessTabSocketBindings> tab_socket_bindings_;
TabSocketPtr tab_socket_ptr_;
InstallMainWorldTabSocketCallback
pending_install_main_world_tab_socket_callback_;
service_manager::BinderRegistry registry_;
base::WeakPtrFactory<HeadlessRenderFrameControllerImpl> weak_ptr_factory_;
};
} // namespace headless
#endif // HEADLESS_LIB_RENDERER_HEADLESS_RENDER_FRAME_CONTROLLER_IMPL_H_
// 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 "headless/lib/renderer/headless_tab_socket_bindings.h"
#include "headless/lib/renderer/headless_render_frame_controller_impl.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "v8/include/v8-inspector.h"
namespace headless {
HeadlessTabSocketBindings::HeadlessTabSocketBindings(
HeadlessRenderFrameControllerImpl* parent_controller,
content::RenderFrame* render_frame,
v8::Local<v8::Context> context,
int world_id)
: parent_controller_(parent_controller),
render_frame_(render_frame),
context_(blink::MainThreadIsolate(), context),
world_id_(world_id),
installed_(false) {}
HeadlessTabSocketBindings::~HeadlessTabSocketBindings() = default;
bool HeadlessTabSocketBindings::InitializeTabSocketBindings() {
if (installed_)
return false;
v8::Isolate* isolate = blink::MainThreadIsolate();
v8::HandleScope handle_scope(isolate);
if (context_.IsEmpty())
return false;
v8::Local<v8::Context> context = context_.Get(blink::MainThreadIsolate());
v8::Context::Scope context_scope(context);
gin::Handle<HeadlessTabSocketBindings> bindings =
gin::CreateHandle(isolate, this);
if (bindings.IsEmpty())
return false;
v8::Local<v8::Object> global = context->Global();
global->Set(gin::StringToV8(isolate, "TabSocket"), bindings.ToV8());
installed_ = true;
return true;
}
void HeadlessTabSocketBindings::OnMessageFromEmbedder(
const std::string& message) {
if (on_message_callback_.IsEmpty()) {
pending_messages_.push_back(message);
return;
}
v8::Isolate* isolate = blink::MainThreadIsolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = context_.Get(isolate);
v8::Local<v8::Value> argv[] = {
gin::Converter<std::string>::ToV8(isolate, message),
};
render_frame_->GetWebFrame()->RequestExecuteV8Function(
context, GetOnMessageCallback(), context->Global(), arraysize(argv), argv,
this);
}
gin::ObjectTemplateBuilder HeadlessTabSocketBindings::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin::Wrappable<HeadlessTabSocketBindings>::GetObjectTemplateBuilder(
isolate)
.SetMethod("send", &HeadlessTabSocketBindings::SendImpl)
.SetProperty("onmessage", &HeadlessTabSocketBindings::GetOnMessage,
&HeadlessTabSocketBindings::SetOnMessage);
}
void HeadlessTabSocketBindings::SendImpl(const std::string& message) {
v8::Local<v8::Context> context = context_.Get(blink::MainThreadIsolate());
int execution_context_id =
v8_inspector::V8ContextInfo::executionContextId(context);
parent_controller_->EnsureTabSocketPtr()->SendMessageToEmbedder(
message, execution_context_id);
}
void HeadlessTabSocketBindings::SetOnMessage(v8::Local<v8::Function> callback) {
on_message_callback_.Reset(blink::MainThreadIsolate(), callback);
for (const std::string& message : pending_messages_) {
OnMessageFromEmbedder(message);
}
pending_messages_.clear();
}
v8::Local<v8::Function> HeadlessTabSocketBindings::GetOnMessageCallback() {
return v8::Local<v8::Function>::New(blink::MainThreadIsolate(),
on_message_callback_);
}
gin::WrapperInfo HeadlessTabSocketBindings::kWrapperInfo = {
gin::kEmbedderNativeGin};
} // namespace headless
// 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 HEADLESS_LIB_RENDERER_HEADLESS_TAB_SOCKET_BINDINGS_H_
#define HEADLESS_LIB_RENDERER_HEADLESS_TAB_SOCKET_BINDINGS_H_
#include "content/public/renderer/render_frame.h"
#include "gin/handle.h"
#include "gin/object_template_builder.h"
#include "gin/wrappable.h"
#include "headless/lib/tab_socket.mojom.h"
#include "third_party/blink/public/web/web_script_execution_callback.h"
namespace headless {
class HeadlessRenderFrameControllerImpl;
class HeadlessTabSocketBindings
: public gin::Wrappable<HeadlessTabSocketBindings>,
public blink::WebScriptExecutionCallback {
public:
HeadlessTabSocketBindings(
HeadlessRenderFrameControllerImpl* parent_controller,
content::RenderFrame* render_frame,
v8::Local<v8::Context> context,
int world_id);
~HeadlessTabSocketBindings() override;
// Add TabSocket bindings to |context_|.
bool InitializeTabSocketBindings();
void OnMessageFromEmbedder(const std::string& message);
// gin::WrappableBase implementation:
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
static gin::WrapperInfo kWrapperInfo;
int world_id() const { return world_id_; }
private:
void SendImpl(const std::string& message);
v8::Local<v8::Value> GetOnMessage() { return GetOnMessageCallback(); }
void SetOnMessage(v8::Local<v8::Function> callback);
v8::Local<v8::Function> GetOnMessageCallback();
HeadlessRenderFrameControllerImpl* const parent_controller_; // NOT OWNED
content::RenderFrame* const render_frame_; // NOT OWNED
const v8::UniquePersistent<v8::Context> context_;
const int world_id_;
bool installed_;
std::list<std::string> pending_messages_;
v8::UniquePersistent<v8::Function> on_message_callback_;
};
} // namespace headless
#endif // HEADLESS_LIB_RENDERER_HEADLESS_TAB_SOCKET_BINDINGS_H_
// 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.
module headless;
import "mojo/public/mojom/base/big_string.mojom";
interface TabSocket {
// Send a message from the Tab to C++ embedder.
SendMessageToEmbedder(mojo_base.mojom.BigString message,
int32 v8_execution_context_id);
// To send a message to the tab use
// HeadlessRenderFrameController::SendMessageToTabSocket.
};
...@@ -134,11 +134,6 @@ Builder& Builder::SetGLImplementation(const std::string& gl_implementation) { ...@@ -134,11 +134,6 @@ Builder& Builder::SetGLImplementation(const std::string& gl_implementation) {
return *this; return *this;
} }
Builder& Builder::AddMojoServiceName(const std::string& mojo_service_name) {
options_.mojo_service_names.insert(mojo_service_name);
return *this;
}
Builder& Builder::SetAppendCommandLineFlagsCallback( Builder& Builder::SetAppendCommandLineFlagsCallback(
const Options::AppendCommandLineFlagsCallback& callback) { const Options::AppendCommandLineFlagsCallback& callback) {
options_.append_command_line_flags_callback = callback; options_.append_command_line_flags_callback = callback;
......
...@@ -148,10 +148,6 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options { ...@@ -148,10 +148,6 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options {
// string can be used to disable GL rendering (e.g., WebGL support). // string can be used to disable GL rendering (e.g., WebGL support).
std::string gl_implementation; std::string gl_implementation;
// Names of mojo services exposed by the browser to the renderer. These
// services will be added to the browser's service manifest.
std::unordered_set<std::string> mojo_service_names;
// Default per-context options, can be specialized on per-context basis. // Default per-context options, can be specialized on per-context basis.
std::string product_name_and_version; std::string product_name_and_version;
...@@ -251,7 +247,6 @@ class HEADLESS_EXPORT HeadlessBrowser::Options::Builder { ...@@ -251,7 +247,6 @@ class HEADLESS_EXPORT HeadlessBrowser::Options::Builder {
Builder& SetDisableSandbox(bool disable_sandbox); Builder& SetDisableSandbox(bool disable_sandbox);
Builder& SetEnableResourceScheduler(bool enable_resource_scheduler); Builder& SetEnableResourceScheduler(bool enable_resource_scheduler);
Builder& SetGLImplementation(const std::string& gl_implementation); Builder& SetGLImplementation(const std::string& gl_implementation);
Builder& AddMojoServiceName(const std::string& mojo_service_name);
Builder& SetAppendCommandLineFlagsCallback( Builder& SetAppendCommandLineFlagsCallback(
const Options::AppendCommandLineFlagsCallback& callback); const Options::AppendCommandLineFlagsCallback& callback);
#if defined(OS_WIN) #if defined(OS_WIN)
......
...@@ -120,8 +120,6 @@ class HEADLESS_EXPORT HeadlessBrowserContext::Builder { ...@@ -120,8 +120,6 @@ class HEADLESS_EXPORT HeadlessBrowserContext::Builder {
// fetching for different network schemes. // fetching for different network schemes.
Builder& SetProtocolHandlers(ProtocolHandlerMap protocol_handlers); Builder& SetProtocolHandlers(ProtocolHandlerMap protocol_handlers);
Builder& AddTabSocketMojoBindings();
// By default if you add mojo bindings, http and https are disabled because // By default if you add mojo bindings, http and https are disabled because
// its almost certinly unsafe for arbitary sites on the internet to have // its almost certinly unsafe for arbitary sites on the internet to have
// access to these bindings. If you know what you're doing it may be OK to // access to these bindings. If you know what you're doing it may be OK to
......
// 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 HEADLESS_PUBLIC_HEADLESS_TAB_SOCKET_H_
#define HEADLESS_PUBLIC_HEADLESS_TAB_SOCKET_H_
#include <string>
#include "base/macros.h"
#include "base/optional.h"
#include "headless/public/headless_export.h"
namespace headless {
// A bidirectional communications channel between C++ and JS.
class HEADLESS_EXPORT HeadlessTabSocket {
public:
class HEADLESS_EXPORT Listener {
public:
Listener() {}
virtual ~Listener() {}
// The |message| and |v8_execution_context_id| may be potentially sent by
// untrusted web content so it should be validated carefully.
virtual void OnMessageFromContext(const std::string& message,
int v8_execution_context_id) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(Listener);
};
// Installs headless tab socket bindings into the specified execution context.
// If the bindings are successfully installed then the |callback| is run with
// |success| = true, otherwise with |success| = false.
virtual void InstallHeadlessTabSocketBindings(
int v8_execution_context_id,
base::OnceCallback<void(bool success)> callback) = 0;
// Installs headless tab socket bindings into the main frame main world. If
// the bindings were installed correctly then |callback| is run with a
// non-empty base::Optional<int> containing the main world
// v8_execution_context_id, otherwise |callback| is run with an empty
// base::Optional<int>.
virtual void InstallMainFrameMainWorldHeadlessTabSocketBindings(
base::OnceCallback<void(base::Optional<int> v8_execution_context_id)>
callback) = 0;
// Note this will fail unless the bindings have been installed.
virtual void SendMessageToContext(const std::string& message,
int v8_execution_context_id) = 0;
virtual void SetListener(Listener* listener) = 0;
protected:
HeadlessTabSocket() {}
virtual ~HeadlessTabSocket() {}
private:
DISALLOW_COPY_AND_ASSIGN(HeadlessTabSocket);
};
} // namespace headless
#endif // HEADLESS_PUBLIC_HEADLESS_TAB_SOCKET_H_
...@@ -21,7 +21,6 @@ namespace headless { ...@@ -21,7 +21,6 @@ namespace headless {
class HeadlessBrowserContextImpl; class HeadlessBrowserContextImpl;
class HeadlessBrowserImpl; class HeadlessBrowserImpl;
class HeadlessDevToolsTarget; class HeadlessDevToolsTarget;
class HeadlessTabSocket;
// Class representing contents of a browser tab. Should be accessed from browser // Class representing contents of a browser tab. Should be accessed from browser
// main thread. // main thread.
...@@ -80,10 +79,6 @@ class HEADLESS_EXPORT HeadlessWebContents { ...@@ -80,10 +79,6 @@ class HEADLESS_EXPORT HeadlessWebContents {
// Close this page. |HeadlessWebContents| object will be destroyed. // Close this page. |HeadlessWebContents| object will be destroyed.
virtual void Close() = 0; virtual void Close() = 0;
// Returns the headless tab socket interface for C++ <---> JS, or null if tab
// sockets are not allowed.
virtual HeadlessTabSocket* GetHeadlessTabSocket() const = 0;
// Returns the main frame's process id or -1 if there's no main frame. // Returns the main frame's process id or -1 if there's no main frame.
virtual int GetMainFrameRenderProcessId() const = 0; virtual int GetMainFrameRenderProcessId() const = 0;
...@@ -113,9 +108,6 @@ class HEADLESS_EXPORT HeadlessWebContents::Builder { ...@@ -113,9 +108,6 @@ class HEADLESS_EXPORT HeadlessWebContents::Builder {
// Specify the initial window size (default is configured in browser options). // Specify the initial window size (default is configured in browser options).
Builder& SetWindowSize(const gfx::Size& size); Builder& SetWindowSize(const gfx::Size& size);
// Specify whether or not TabSockets are allowed.
Builder& SetAllowTabSockets(bool tab_sockets_allowed);
// Specify whether BeginFrames should be controlled via DevTools commands. // Specify whether BeginFrames should be controlled via DevTools commands.
Builder& SetEnableBeginFrameControl(bool enable_begin_frame_control); Builder& SetEnableBeginFrameControl(bool enable_begin_frame_control);
...@@ -130,27 +122,10 @@ class HEADLESS_EXPORT HeadlessWebContents::Builder { ...@@ -130,27 +122,10 @@ class HEADLESS_EXPORT HeadlessWebContents::Builder {
explicit Builder(HeadlessBrowserContextImpl* browser_context); explicit Builder(HeadlessBrowserContextImpl* browser_context);
struct MojoService {
using ServiceFactoryCallback =
base::RepeatingCallback<void(HeadlessWebContents*,
mojo::ScopedMessagePipeHandle)>;
MojoService();
MojoService(const MojoService& other);
MojoService(const std::string& service_name,
const ServiceFactoryCallback& service_factory);
~MojoService();
std::string service_name;
ServiceFactoryCallback service_factory;
};
HeadlessBrowserContextImpl* browser_context_; HeadlessBrowserContextImpl* browser_context_;
GURL initial_url_ = GURL("about:blank"); GURL initial_url_ = GURL("about:blank");
gfx::Size window_size_; gfx::Size window_size_;
std::list<MojoService> mojo_services_;
bool tab_sockets_allowed_ = false;
bool enable_begin_frame_control_ = false; bool enable_begin_frame_control_ = false;
DISALLOW_COPY_AND_ASSIGN(Builder); DISALLOW_COPY_AND_ASSIGN(Builder);
......
<html>
<script>
function tabsocketTest() {
// Unfortunately, the order in which
// HeadlessRenderFrameControllerImpl::DidCreateScriptContext and
// HeadlessRenderFrameControllerImpl::InstallMainWorldTabSocket are called
// is non-deterministic. This means the TabSocket might not have been
// installed yet. If that happens just try again a bit later.
if (window.hasOwnProperty('TabSocket')) {
window.TabSocket.send('Hello world!');
window.TabSocket.onmessage = function(message) {
window.TabSocket.send('Embedder sent us: ' + message);
};
} else {
setTimeout(tabsocketTest, 1);
}
}
tabsocketTest();
</script>
</html>
...@@ -256,10 +256,6 @@ void HeadlessAsyncDevTooledBrowserTest::RunTest() { ...@@ -256,10 +256,6 @@ void HeadlessAsyncDevTooledBrowserTest::RunTest() {
HeadlessBrowserContext::Builder builder = HeadlessBrowserContext::Builder builder =
browser()->CreateBrowserContextBuilder(); browser()->CreateBrowserContextBuilder();
builder.SetProtocolHandlers(GetProtocolHandlers()); builder.SetProtocolHandlers(GetProtocolHandlers());
if (GetAllowTabSockets()) {
builder.EnableUnsafeNetworkAccessWithMojoBindings(true);
builder.AddTabSocketMojoBindings();
}
CustomizeHeadlessBrowserContext(builder); CustomizeHeadlessBrowserContext(builder);
browser_context_ = builder.Build(); browser_context_ = builder.Build();
...@@ -268,7 +264,6 @@ void HeadlessAsyncDevTooledBrowserTest::RunTest() { ...@@ -268,7 +264,6 @@ void HeadlessAsyncDevTooledBrowserTest::RunTest() {
HeadlessWebContents::Builder web_contents_builder = HeadlessWebContents::Builder web_contents_builder =
browser_context_->CreateWebContentsBuilder(); browser_context_->CreateWebContentsBuilder();
web_contents_builder.SetAllowTabSockets(GetAllowTabSockets());
web_contents_builder.SetEnableBeginFrameControl(GetEnableBeginFrameControl()); web_contents_builder.SetEnableBeginFrameControl(GetEnableBeginFrameControl());
CustomizeHeadlessWebContents(web_contents_builder); CustomizeHeadlessWebContents(web_contents_builder);
web_contents_ = web_contents_builder.Build(); web_contents_ = web_contents_builder.Build();
...@@ -291,10 +286,6 @@ ProtocolHandlerMap HeadlessAsyncDevTooledBrowserTest::GetProtocolHandlers() { ...@@ -291,10 +286,6 @@ ProtocolHandlerMap HeadlessAsyncDevTooledBrowserTest::GetProtocolHandlers() {
return ProtocolHandlerMap(); return ProtocolHandlerMap();
} }
bool HeadlessAsyncDevTooledBrowserTest::GetAllowTabSockets() {
return false;
}
bool HeadlessAsyncDevTooledBrowserTest::GetEnableBeginFrameControl() { bool HeadlessAsyncDevTooledBrowserTest::GetEnableBeginFrameControl() {
return false; return false;
} }
......
...@@ -144,9 +144,6 @@ class HeadlessAsyncDevTooledBrowserTest : public HeadlessBrowserTest, ...@@ -144,9 +144,6 @@ class HeadlessAsyncDevTooledBrowserTest : public HeadlessBrowserTest,
// the map returned is empty. // the map returned is empty.
virtual ProtocolHandlerMap GetProtocolHandlers(); virtual ProtocolHandlerMap GetProtocolHandlers();
// Whether to allow TabSockets when creating |web_contents_|.
virtual bool GetAllowTabSockets();
// Whether to enable BeginFrameControl when creating |web_contents_|. // Whether to enable BeginFrameControl when creating |web_contents_|.
virtual bool GetEnableBeginFrameControl(); virtual bool GetEnableBeginFrameControl();
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/base64.h" #include "base/base64.h"
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
...@@ -19,10 +20,9 @@ ...@@ -19,10 +20,9 @@
#include "headless/public/devtools/domains/runtime.h" #include "headless/public/devtools/domains/runtime.h"
#include "headless/public/headless_browser.h" #include "headless/public/headless_browser.h"
#include "headless/public/headless_devtools_client.h" #include "headless/public/headless_devtools_client.h"
#include "headless/public/headless_tab_socket.h"
#include "headless/public/headless_web_contents.h" #include "headless/public/headless_web_contents.h"
#include "headless/public/util/testing/test_in_memory_protocol_handler.h" #include "headless/public/util/testing/test_in_memory_protocol_handler.h"
#include "headless/test/tab_socket_test.h" #include "headless/test/headless_browser_test.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
...@@ -30,26 +30,34 @@ ...@@ -30,26 +30,34 @@
namespace headless { namespace headless {
namespace { namespace {
static constexpr char kIndexHtml[] = R"( const char kIndexHtml[] = R"(
<html> <html>
<body> <body>
<script src="bindings.js"></script> <script src="bindings.js"></script>
</body> </body>
</html> </html>
)"; )";
const char kTabSocketScript[] = R"(
window.TabSocket = {};
window.TabSocket.onmessage = () => {};
window.TabSocket.send = (json) => console.debug(json);
)";
} // namespace } // namespace
class HeadlessJsBindingsTest class HeadlessJsBindingsTest
: public HeadlessAsyncDevTooledBrowserTest, : public HeadlessAsyncDevTooledBrowserTest,
public HeadlessDevToolsClient::RawProtocolListener, public HeadlessDevToolsClient::RawProtocolListener,
public TestInMemoryProtocolHandler::RequestDeferrer, public TestInMemoryProtocolHandler::RequestDeferrer,
public HeadlessTabSocket::Listener, public headless::runtime::Observer,
public page::ExperimentalObserver { public page::ExperimentalObserver {
public: public:
void SetUp() override { using ConsoleAPICalledParams = headless::runtime::ConsoleAPICalledParams;
options()->mojo_service_names.insert("headless::TabSocket"); using EvaluateResult = headless::runtime::EvaluateResult;
HeadlessAsyncDevTooledBrowserTest::SetUp(); using RemoteObject = headless::runtime::RemoteObject;
}
HeadlessJsBindingsTest() : weak_factory_(this) {}
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
base::ThreadRestrictions::SetIOAllowed(true); base::ThreadRestrictions::SetIOAllowed(true);
...@@ -62,7 +70,6 @@ class HeadlessJsBindingsTest ...@@ -62,7 +70,6 @@ class HeadlessJsBindingsTest
void CustomizeHeadlessBrowserContext( void CustomizeHeadlessBrowserContext(
HeadlessBrowserContext::Builder& builder) override { HeadlessBrowserContext::Builder& builder) override {
builder.AddTabSocketMojoBindings();
builder.EnableUnsafeNetworkAccessWithMojoBindings(true); builder.EnableUnsafeNetworkAccessWithMojoBindings(true);
} }
...@@ -74,8 +81,6 @@ class HeadlessJsBindingsTest ...@@ -74,8 +81,6 @@ class HeadlessJsBindingsTest
http_handler_->SetHeadlessBrowserContext(browser_context_); http_handler_->SetHeadlessBrowserContext(browser_context_);
} }
bool GetAllowTabSockets() override { return true; }
ProtocolHandlerMap GetProtocolHandlers() override { ProtocolHandlerMap GetProtocolHandlers() override {
ProtocolHandlerMap protocol_handlers; ProtocolHandlerMap protocol_handlers;
std::unique_ptr<TestInMemoryProtocolHandler> http_handler( std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
...@@ -96,28 +101,24 @@ class HeadlessJsBindingsTest ...@@ -96,28 +101,24 @@ class HeadlessJsBindingsTest
void RunDevTooledTest() override { void RunDevTooledTest() override {
devtools_client_->GetPage()->GetExperimental()->AddObserver(this); devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
devtools_client_->GetPage()->Enable(); devtools_client_->GetPage()->Enable();
headless_tab_socket_ = web_contents_->GetHeadlessTabSocket(); devtools_client_->GetPage()->AddScriptToEvaluateOnNewDocument(
CHECK(headless_tab_socket_); kTabSocketScript);
headless_tab_socket_->SetListener(this); devtools_client_->GetRuntime()->AddObserver(this);
devtools_client_->GetRuntime()->Enable();
devtools_client_->GetRuntime()->Evaluate(
kTabSocketScript,
base::BindOnce(&HeadlessJsBindingsTest::ConnectionEstablished,
weak_factory_.GetWeakPtr()));
devtools_client_->SetRawProtocolListener(this); devtools_client_->SetRawProtocolListener(this);
headless_tab_socket_->InstallMainFrameMainWorldHeadlessTabSocketBindings(
base::BindOnce(&HeadlessJsBindingsTest::OnInstalledHeadlessTabSocket,
base::Unretained(this)));
} }
void OnRequest(const GURL& url, void OnRequest(const GURL& url,
base::RepeatingClosure complete_request) override { base::RepeatingClosure complete_request) override {
if (!tab_socket_installed_ && url.spec() == "http://test.com/bindings.js") { complete_request.Run();
complete_request_ = std::move(complete_request);
} else {
complete_request.Run();
}
} }
void OnInstalledHeadlessTabSocket(base::Optional<int> context_id) { void ConnectionEstablished(std::unique_ptr<EvaluateResult>) {
main_world_execution_context_id_ = *context_id; connection_established_ = true;
tab_socket_installed_ = true;
if (complete_request_) { if (complete_request_) {
browser()->BrowserIOThread()->PostTask(FROM_HERE, complete_request_); browser()->BrowserIOThread()->PostTask(FROM_HERE, complete_request_);
complete_request_ = base::RepeatingClosure(); complete_request_ = base::RepeatingClosure();
...@@ -147,9 +148,28 @@ class HeadlessJsBindingsTest ...@@ -147,9 +148,28 @@ class HeadlessJsBindingsTest
: ""); : "");
} }
void OnMessageFromContext(const std::string& json_message, void OnConsoleAPICalled(const ConsoleAPICalledParams& params) override {
int v8_execution_context_id) override { const std::vector<std::unique_ptr<RemoteObject>>& args = *params.GetArgs();
EXPECT_EQ(main_world_execution_context_id_, v8_execution_context_id); if (args.empty())
return;
if (params.GetType() != headless::runtime::ConsoleAPICalledType::DEBUG)
return;
RemoteObject* object = args[0].get();
if (object->GetType() != headless::runtime::RemoteObjectType::STRING)
return;
OnMessageFromJS(object->GetValue()->GetString());
}
void SendMessageToJS(const std::string& message) {
std::string encoded;
base::Base64Encode(message, &encoded);
devtools_client_->GetRuntime()->Evaluate(
"window.TabSocket.onmessage(atob(\"" + encoded + "\"))");
}
void OnMessageFromJS(const std::string& json_message) {
std::unique_ptr<base::Value> message = std::unique_ptr<base::Value> message =
base::JSONReader::Read(json_message, base::JSON_PARSE_RFC); base::JSONReader::Read(json_message, base::JSON_PARSE_RFC);
const base::Value* method_value = message->FindKey("method"); const base::Value* method_value = message->FindKey("method");
...@@ -184,7 +204,7 @@ class HeadlessJsBindingsTest ...@@ -184,7 +204,7 @@ class HeadlessJsBindingsTest
bool OnProtocolMessage(const std::string& devtools_agent_host_id, bool OnProtocolMessage(const std::string& devtools_agent_host_id,
const std::string& json_message, const std::string& json_message,
const base::DictionaryValue& parsed_message) override { const base::DictionaryValue& parsed_message) override {
if (main_world_execution_context_id_ == -1) if (!connection_established_)
return false; return false;
const base::Value* id_value = parsed_message.FindKey("id"); const base::Value* id_value = parsed_message.FindKey("id");
...@@ -198,8 +218,7 @@ class HeadlessJsBindingsTest ...@@ -198,8 +218,7 @@ class HeadlessJsBindingsTest
if ((id % 2) == 0) if ((id % 2) == 0)
return false; return false;
headless_tab_socket_->SendMessageToContext( SendMessageToJS(json_message);
json_message, main_world_execution_context_id_);
return true; return true;
} }
...@@ -207,24 +226,28 @@ class HeadlessJsBindingsTest ...@@ -207,24 +226,28 @@ class HeadlessJsBindingsTest
if (!method_value) if (!method_value)
return false; return false;
headless_tab_socket_->SendMessageToContext( if (method_value->GetString() == "Runtime.consoleAPICalled") {
json_message, main_world_execution_context_id_); // console.debug is used for transport.
return false;
}
SendMessageToJS(json_message);
// Check which domain the event belongs to, if it's the DOM domain then // Check which domain the event belongs to, if it's the DOM domain then
// assume js handled it. // assume js handled it.
std::vector<base::StringPiece> sections = std::vector<base::StringPiece> sections =
SplitStringPiece(method_value->GetString(), ".", base::KEEP_WHITESPACE, SplitStringPiece(method_value->GetString(), ".", base::KEEP_WHITESPACE,
base::SPLIT_WANT_ALL); base::SPLIT_WANT_ALL);
return sections[0] == "DOM" || sections[0] == "Runtime"; return sections[0] == "DOM" || sections[0] == "Runtime";
} }
protected: protected:
TestInMemoryProtocolHandler* http_handler_; // NOT OWNED TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
HeadlessTabSocket* headless_tab_socket_; // NOT OWNED
int main_world_execution_context_id_ = -1;
std::string bindings_js_; std::string bindings_js_;
base::RepeatingClosure complete_request_; base::RepeatingClosure complete_request_;
bool tab_socket_installed_ = false; bool connection_established_ = false;
base::WeakPtrFactory<HeadlessJsBindingsTest> weak_factory_;
}; };
class SimpleCommandJsBindingsTest : public HeadlessJsBindingsTest { class SimpleCommandJsBindingsTest : public HeadlessJsBindingsTest {
...@@ -316,8 +339,6 @@ class CachedJsBindingsTest : public HeadlessJsBindingsTest, ...@@ -316,8 +339,6 @@ class CachedJsBindingsTest : public HeadlessJsBindingsTest,
EXPECT_EQ("2", result); EXPECT_EQ("2", result);
if (first_result) { if (first_result) {
tab_socket_installed_ = false;
main_world_execution_context_id_ = -1;
devtools_client_->GetPage()->Reload(); devtools_client_->GetPage()->Reload();
} else { } else {
EXPECT_TRUE(metadata_received_); EXPECT_TRUE(metadata_received_);
...@@ -326,25 +347,6 @@ class CachedJsBindingsTest : public HeadlessJsBindingsTest, ...@@ -326,25 +347,6 @@ class CachedJsBindingsTest : public HeadlessJsBindingsTest,
first_result = false; first_result = false;
} }
// Called on the IO thread.
void OnRequest(const GURL& url,
base::RepeatingClosure complete_request) override {
HeadlessJsBindingsTest::OnRequest(url, complete_request);
if (!first_result && url.spec() == "http://test.com/bindings.js") {
browser()->BrowserMainThread()->PostTask(
FROM_HERE,
base::BindOnce(&CachedJsBindingsTest::ReinstallTabSocketBindings,
base::Unretained(this)));
}
}
void ReinstallTabSocketBindings() {
headless_tab_socket_->InstallMainFrameMainWorldHeadlessTabSocketBindings(
base::BindRepeating(
&HeadlessJsBindingsTest::OnInstalledHeadlessTabSocket,
base::Unretained(this)));
}
void OnMetadataForResource(const GURL& url, void OnMetadataForResource(const GURL& url,
net::IOBuffer* buf, net::IOBuffer* buf,
int buf_len) override { int buf_len) override {
......
// 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 "headless/test/tab_socket_test.h"
#include "headless/public/headless_devtools_client.h"
#include "headless/public/headless_tab_socket.h"
namespace headless {
TabSocketTest::TabSocketTest() = default;
TabSocketTest::~TabSocketTest() = default;
void TabSocketTest::SetUp() {
options()->mojo_service_names.insert("headless::TabSocket");
HeadlessAsyncDevTooledBrowserTest::SetUp();
}
void TabSocketTest::RunDevTooledTest() {
devtools_client_->GetRuntime()->AddObserver(this);
devtools_client_->GetRuntime()->Enable();
// Note we must enable the page domain or the browser won't get told the
// devtools frame ids.
devtools_client_->GetPage()->AddObserver(this);
devtools_client_->GetPage()->Enable(
page::EnableParams::Builder().Build(),
base::BindOnce(&TabSocketTest::OnPageEnabled, base::Unretained(this)));
}
void TabSocketTest::OnPageEnabled(std::unique_ptr<page::EnableResult> result) {
devtools_client_->GetPage()->GetExperimental()->GetResourceTree(
page::GetResourceTreeParams::Builder().Build(),
base::BindOnce(&TabSocketTest::OnResourceTree, base::Unretained(this)));
}
void TabSocketTest::OnResourceTree(
std::unique_ptr<page::GetResourceTreeResult> result) {
main_frame_id_ = result->GetFrameTree()->GetFrame()->GetId();
RunTabSocketTest();
}
void TabSocketTest::OnExecutionContextCreated(
const runtime::ExecutionContextCreatedParams& params) {
const base::DictionaryValue* dictionary;
bool is_main_world;
if (params.GetContext()->HasAuxData() &&
params.GetContext()->GetAuxData()->GetAsDictionary(&dictionary)) {
if (dictionary->GetBoolean("isDefault", &is_main_world) && !is_main_world) {
world_name_to_v8_execution_context_id_[params.GetContext()->GetName()] =
params.GetContext()->GetId();
}
std::string frame_id;
if (dictionary->GetString("frameId", &frame_id)) {
frame_id_to_v8_execution_context_ids_[frame_id].insert(
params.GetContext()->GetId());
}
}
}
void TabSocketTest::ExpectJsException(
std::unique_ptr<runtime::EvaluateResult> result) {
EXPECT_TRUE(result->HasExceptionDetails());
FinishAsynchronousTest();
}
void TabSocketTest::FailOnJsEvaluateException(
std::unique_ptr<runtime::EvaluateResult> result) {
if (!result->HasExceptionDetails())
return;
FinishAsynchronousTest();
const runtime::ExceptionDetails* exception_details =
result->GetExceptionDetails();
FAIL() << exception_details->GetText()
<< (exception_details->HasException()
? exception_details->GetException()->GetDescription().c_str()
: "");
}
bool TabSocketTest::GetAllowTabSockets() {
return true;
}
int TabSocketTest::GetV8ExecutionContextIdByWorldName(const std::string& name) {
const auto find_it = world_name_to_v8_execution_context_id_.find(name);
if (find_it == world_name_to_v8_execution_context_id_.end()) {
FinishAsynchronousTest();
CHECK(false) << "Unknown isolated world: " << name;
return -1;
}
return find_it->second;
}
const std::set<int>* TabSocketTest::GetV8ExecutionContextIdsForFrame(
const std::string& devtools_frame_id) const {
const auto find_it =
frame_id_to_v8_execution_context_ids_.find(devtools_frame_id);
if (find_it == frame_id_to_v8_execution_context_ids_.end())
return nullptr;
return &find_it->second;
}
void TabSocketTest::CreateMainWorldTabSocket(
std::string devtools_frame_id,
base::OnceCallback<void(int)> callback) {
const auto find_it =
frame_id_to_v8_execution_context_ids_.find(devtools_frame_id);
CHECK(find_it != frame_id_to_v8_execution_context_ids_.end());
if (find_it->second.size() != 1u) {
FinishAsynchronousTest();
FAIL() << "More than one v8 execution context exists for the main frame!";
}
InstallHeadlessTabSocketBindings(std::move(callback),
*find_it->second.begin());
}
void TabSocketTest::CreateIsolatedWorldTabSocket(
std::string isolated_world_name,
std::string devtools_frame_id,
base::OnceCallback<void(int)> callback) {
devtools_client_->GetPage()->GetExperimental()->CreateIsolatedWorld(
page::CreateIsolatedWorldParams::Builder()
.SetFrameId(devtools_frame_id)
.SetWorldName(isolated_world_name)
.Build(),
base::BindOnce(&TabSocketTest::OnCreatedIsolatedWorld,
base::Unretained(this), std::move(callback)));
}
void TabSocketTest::OnCreatedIsolatedWorld(
base::OnceCallback<void(int)> callback,
std::unique_ptr<page::CreateIsolatedWorldResult> result) {
InstallHeadlessTabSocketBindings(std::move(callback),
result->GetExecutionContextId());
}
void TabSocketTest::InstallHeadlessTabSocketBindings(
base::OnceCallback<void(int)> callback,
int execution_context_id) {
HeadlessTabSocket* tab_socket = web_contents_->GetHeadlessTabSocket();
CHECK(tab_socket);
tab_socket->InstallHeadlessTabSocketBindings(
execution_context_id,
base::BindOnce(&TabSocketTest::OnInstalledHeadlessTabSocketBindings,
base::Unretained(this), execution_context_id,
std::move(callback)));
}
void TabSocketTest::OnInstalledHeadlessTabSocketBindings(
int execution_context_id,
base::OnceCallback<void(int)> callback,
bool success) {
if (!success) {
FinishAsynchronousTest();
CHECK(false) << "InstallHeadlessTabSocketBindings failed";
}
std::move(callback).Run(execution_context_id);
}
} // namespace headless
// 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 HEADLESS_TEST_TAB_SOCKET_TEST_H_
#define HEADLESS_TEST_TAB_SOCKET_TEST_H_
#include "headless/public/devtools/domains/page.h"
#include "headless/public/devtools/domains/runtime.h"
#include "headless/public/headless_tab_socket.h"
#include "headless/test/headless_browser_test.h"
namespace headless {
class TabSocketTest : public HeadlessAsyncDevTooledBrowserTest,
public HeadlessTabSocket::Listener,
public page::Observer,
public runtime::Observer {
public:
TabSocketTest();
~TabSocketTest() override;
// HeadlessAsyncDevTooledBrowserTest implementation:
void SetUp() override;
void RunDevTooledTest() override;
bool GetAllowTabSockets() override;
// runtime::Observer implementation:
void OnExecutionContextCreated(
const runtime::ExecutionContextCreatedParams& params) override;
void ExpectJsException(std::unique_ptr<runtime::EvaluateResult> result);
void FailOnJsEvaluateException(
std::unique_ptr<runtime::EvaluateResult> result);
// Note this will fail the test if an execution context corresponding to
// |name| doesn't exist.
int GetV8ExecutionContextIdByWorldName(const std::string& name);
// Returns a pointer to the set of v8 execution context ids corresponding to
// |devtools_frame_id| or null if none exist.
const std::set<int>* GetV8ExecutionContextIdsForFrame(
const std::string& devtools_frame_id) const;
// Attempts to install a main world TabSocket in |devtools_frame_id|.
// If successful |callback| will run with the execution context id of the
// main world tab socket.
void CreateMainWorldTabSocket(std::string devtools_frame_id,
base::OnceCallback<void(int)> callback);
// Attempts to create an isolated world in |isolated_world_name| and then
// install a world TabSocket. If successful |callback| will run with the
// execution context id of the newly created isolated world as a parameter.
// Note |isolated_world_name| must be unique.
void CreateIsolatedWorldTabSocket(std::string isolated_world_name,
std::string devtools_frame_id,
base::OnceCallback<void(int)> callback);
virtual void RunTabSocketTest() = 0;
const std::string& main_frame_id() const { return *main_frame_id_; }
private:
void OnPageEnabled(std::unique_ptr<page::EnableResult> result);
void OnResourceTree(std::unique_ptr<page::GetResourceTreeResult> result);
void CreateMainWorldTabSocketStep2(std::string devtools_frame_id,
base::OnceCallback<void(int)> callback,
int v8_execution_context_id);
void OnCreatedIsolatedWorld(
base::OnceCallback<void(int)> callback,
std::unique_ptr<page::CreateIsolatedWorldResult> result);
void InstallHeadlessTabSocketBindings(base::OnceCallback<void(int)> callback,
int execution_context_id);
void OnInstalledHeadlessTabSocketBindings(
int execution_context_id,
base::OnceCallback<void(int)> callback,
bool success);
std::map<std::string, int> world_name_to_v8_execution_context_id_;
std::map<std::string, std::set<int>> frame_id_to_v8_execution_context_ids_;
base::Optional<std::string> main_frame_id_;
};
} // namespace headless
#endif // HEADLESS_TEST_TAB_SOCKET_TEST_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