diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-20 13:40:20 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-22 12:41:23 +0000 |
commit | 7961cea6d1041e3e454dae6a1da660b453efd238 (patch) | |
tree | c0eeb4a9ff9ba32986289c1653d9608e53ccb444 /chromium/fuchsia | |
parent | b7034d0803538058e5c9d904ef03cf5eab34f6ef (diff) | |
download | qtwebengine-chromium-7961cea6d1041e3e454dae6a1da660b453efd238.tar.gz |
BASELINE: Update Chromium to 78.0.3904.130
Change-Id: If185e0c0061b3437531c97c9c8c78f239352a68b
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/fuchsia')
103 files changed, 3430 insertions, 854 deletions
diff --git a/chromium/fuchsia/BUILD.gn b/chromium/fuchsia/BUILD.gn index ecc9681227c..7b1ad59e489 100644 --- a/chromium/fuchsia/BUILD.gn +++ b/chromium/fuchsia/BUILD.gn @@ -13,7 +13,8 @@ fidl_library("cast_fidl") { sources = [ "fidl/cast/api_bindings.fidl", "fidl/cast/application_config.fidl", - "fidl/cast/queryable_data.fidl", + "fidl/cast/application_controller.fidl", + "fidl/cast/url_request_rewriter.fidl", ] public_deps = [ @@ -100,9 +101,11 @@ group("gn_all") { "engine:web_engine_browsertests", "engine:web_engine_unittests", "http:http_service_tests", - "mojo:fuchsia_mojo_unittests", + "mojom:fuchsia_mojo_unittests", "runners:cast_runner", "runners:cast_runner_browsertests", + "runners:cast_runner_integration_tests", + "runners:cast_runner_unittests", "runners:web_runner", "//chromecast/bindings:bindings_manager_fuchsia", ] diff --git a/chromium/fuchsia/base/BUILD.gn b/chromium/fuchsia/base/BUILD.gn index f3db8b88f7d..e8ba2b608c3 100644 --- a/chromium/fuchsia/base/BUILD.gn +++ b/chromium/fuchsia/base/BUILD.gn @@ -12,9 +12,11 @@ import("//testing/test.gni") source_set("base") { sources = [ "mem_buffer_util.cc", + "string_util.cc", ] public = [ "mem_buffer_util.h", + "string_util.h", ] deps = [ "//base", @@ -37,6 +39,7 @@ source_set("modular") { ] public_deps = [ "//third_party/fuchsia-sdk/sdk:modular", + "//third_party/fuchsia-sdk/sdk:sys_cpp", ] } @@ -66,6 +69,8 @@ source_set("test_support") { "frame_test_util.cc", "frame_test_util.h", "result_receiver.h", + "test_devtools_list_fetcher.cc", + "test_devtools_list_fetcher.h", "test_navigation_listener.cc", "test_navigation_listener.h", ] @@ -73,6 +78,8 @@ source_set("test_support") { ":base", ":modular", "//base", + "//net", + "//net:test_support", "//third_party/fuchsia-sdk/sdk:modular", "//third_party/fuchsia-sdk/sdk:web", "//url", @@ -90,6 +97,7 @@ test("cr_fuchsia_base_unittests") { "//base", "//base:testfidl", "//base/test:run_all_unittests", + "//base/test:test_support", "//testing/gtest", ] } diff --git a/chromium/fuchsia/base/agent_impl.cc b/chromium/fuchsia/base/agent_impl.cc index 1d95d97e476..1e1268e988a 100644 --- a/chromium/fuchsia/base/agent_impl.cc +++ b/chromium/fuchsia/base/agent_impl.cc @@ -4,7 +4,10 @@ #include "fuchsia/base/agent_impl.h" +#include <lib/sys/cpp/component_context.h> + #include "base/bind.h" +#include "base/fuchsia/default_context.h" namespace cr_fuchsia { @@ -14,7 +17,9 @@ AgentImpl::ComponentStateBase::ComponentStateBase( base::StringPiece component_id) : component_id_(component_id) { fidl::InterfaceHandle<::fuchsia::io::Directory> directory; - service_directory_.Initialize(directory.NewRequest()); + outgoing_directory_.GetOrCreateDirectory("svc")->Serve( + fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE, + directory.NewRequest().TakeChannel()); service_provider_ = std::make_unique<base::fuchsia::ServiceProviderImpl>( std::move(directory)); @@ -41,12 +46,11 @@ void AgentImpl::ComponentStateBase::TeardownIfUnused() { } AgentImpl::AgentImpl( - base::fuchsia::ServiceDirectory* service_directory, + sys::OutgoingDirectory* outgoing_directory, CreateComponentStateCallback create_component_state_callback) : create_component_state_callback_( std::move(create_component_state_callback)), - agent_binding_(service_directory, this) { -} + agent_binding_(outgoing_directory, this) {} AgentImpl::~AgentImpl() { DCHECK(active_components_.empty()); diff --git a/chromium/fuchsia/base/agent_impl.h b/chromium/fuchsia/base/agent_impl.h index e6b7e7861a4..3165076f442 100644 --- a/chromium/fuchsia/base/agent_impl.h +++ b/chromium/fuchsia/base/agent_impl.h @@ -7,14 +7,15 @@ #include <fuchsia/modular/cpp/fidl.h> #include <lib/fidl/cpp/binding_set.h> +#include <lib/sys/cpp/component_context.h> #include <lib/zx/channel.h> #include <memory> #include <string> #include <utility> #include "base/containers/flat_map.h" +#include "base/fuchsia/default_context.h" #include "base/fuchsia/scoped_service_binding.h" -#include "base/fuchsia/service_directory.h" #include "base/fuchsia/service_provider_impl.h" #include "base/macros.h" #include "base/strings/string_piece.h" @@ -28,8 +29,8 @@ namespace cr_fuchsia { // service state as desired. class AgentImpl : public ::fuchsia::modular::Agent { public: - // Common base for per-component services and state. The base provides a - // service directory into which specializations publish their services, to + // Common base for per-component services and state. The base provides an + // outgoing directory into which specializations publish their services, to // have them made available to the client. Different specializations of the // ComponentStateBase can be created to suit different components. // @@ -39,8 +40,8 @@ class AgentImpl : public ::fuchsia::modular::Agent { // MyComponentState : public ComponentStateBase { // public: // MyComponentState(..) : ComponentStateBase(..) - // : binding1_(service_directory(), &service1_), - // binding2_(service_directory(), shared_service2_) {} + // : binding1_(outgoing_directory(), &service1_), + // binding2_(outgoing_directory(), shared_service2_) {} // private: // Service1 service1_; // ScopedServiceBinding<Service1> binding1_; @@ -59,8 +60,8 @@ class AgentImpl : public ::fuchsia::modular::Agent { // Returns the directory into which the ComponentState implementation should // publish the services that the component may use. - base::fuchsia::ServiceDirectory* service_directory() { - return &service_directory_; + sys::OutgoingDirectory* outgoing_directory() { + return &outgoing_directory_; } // Registers |service_binding| to prevent teardown of this @@ -85,7 +86,7 @@ class AgentImpl : public ::fuchsia::modular::Agent { const std::string component_id_; AgentImpl* agent_impl_ = nullptr; - base::fuchsia::ServiceDirectory service_directory_; + sys::OutgoingDirectory outgoing_directory_; std::unique_ptr<base::fuchsia::ServiceProviderImpl> service_provider_; std::vector<base::RepeatingCallback<bool()>> keepalive_callbacks_; @@ -99,10 +100,10 @@ class AgentImpl : public ::fuchsia::modular::Agent { base::RepeatingCallback<std::unique_ptr<ComponentStateBase>( base::StringPiece component_id)>; - // Binds the Agent service in the supplied |service_directory|, and invokes + // Binds the Agent service in the supplied |outgoing_directory|, and invokes // |create_component_state_callback| on each Connect() call, for the caller to // create per-component data structures and services. - AgentImpl(base::fuchsia::ServiceDirectory* service_directory, + AgentImpl(sys::OutgoingDirectory* outgoing_directory, CreateComponentStateCallback create_component_state_callback); ~AgentImpl() override; @@ -121,7 +122,7 @@ class AgentImpl : public ::fuchsia::modular::Agent { // Returns a ComponentStateBase instance for a given component-Id. const CreateComponentStateCallback create_component_state_callback_; - // Binds this Agent implementation into the |service_directory|. + // Binds this Agent implementation into the |outgoing_directory|. base::fuchsia::ScopedServiceBinding<::fuchsia::modular::Agent> agent_binding_; // Owns the ComponentState instances for each connected component. diff --git a/chromium/fuchsia/base/agent_impl_unittests.cc b/chromium/fuchsia/base/agent_impl_unittests.cc index dee70fab458..c11dfdcbb70 100644 --- a/chromium/fuchsia/base/agent_impl_unittests.cc +++ b/chromium/fuchsia/base/agent_impl_unittests.cc @@ -5,11 +5,9 @@ #include "fuchsia/base/agent_impl.h" #include "base/fuchsia/fuchsia_logging.h" -#include "base/fuchsia/service_directory.h" -#include "base/fuchsia/service_directory_client.h" #include "base/fuchsia/testfidl/cpp/fidl.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" +#include "base/test/task_environment.h" #include "fuchsia/base/fit_adapter.h" #include "fuchsia/base/result_receiver.h" #include "testing/gtest/include/gtest/gtest.h" @@ -50,7 +48,7 @@ class AccumulatorComponentState : public AgentImpl::ComponentStateBase { public: AccumulatorComponentState(base::StringPiece component) : ComponentStateBase(component), - service_binding_(service_directory(), &service_) {} + service_binding_(outgoing_directory(), &service_) {} protected: AccumulatingTestInterfaceImpl service_; @@ -70,8 +68,10 @@ class AgentImplTest : public ::testing::Test { public: AgentImplTest() { fidl::InterfaceHandle<::fuchsia::io::Directory> directory; - services_ = std::make_unique<base::fuchsia::ServiceDirectory>( - directory.NewRequest()); + services_.GetOrCreateDirectory("svc")->Serve( + fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE, + directory.NewRequest().TakeChannel()); + services_client_ = std::make_unique<base::fuchsia::ServiceDirectoryClient>( std::move(directory)); } @@ -79,8 +79,8 @@ class AgentImplTest : public ::testing::Test { fuchsia::modular::AgentPtr CreateAgentAndConnect() { DCHECK(!agent_impl_); agent_impl_ = std::make_unique<AgentImpl>( - services_.get(), base::BindRepeating(&AgentImplTest::OnComponentConnect, - base::Unretained(this))); + &services_, base::BindRepeating(&AgentImplTest::OnComponentConnect, + base::Unretained(this))); fuchsia::modular::AgentPtr agent; services_client_->ConnectToService(agent.NewRequest()); return agent; @@ -100,8 +100,10 @@ class AgentImplTest : public ::testing::Test { return nullptr; } - base::MessageLoopForIO message_loop_; - std::unique_ptr<base::fuchsia::ServiceDirectory> services_; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, + base::test::TaskEnvironment::MainThreadType::IO}; + sys::OutgoingDirectory services_; std::unique_ptr<base::fuchsia::ServiceDirectoryClient> services_client_; std::unique_ptr<AgentImpl> agent_impl_; diff --git a/chromium/fuchsia/base/agent_manager.cc b/chromium/fuchsia/base/agent_manager.cc index a777b1d5c58..730f8b7f62f 100644 --- a/chromium/fuchsia/base/agent_manager.cc +++ b/chromium/fuchsia/base/agent_manager.cc @@ -28,8 +28,9 @@ void AgentManager::ConnectToAgentServiceUnsafe(base::StringPiece agent, it->second.services.NewRequest(), it->second.controller.NewRequest()); it->second.services.set_error_handler( - [agent = agent.as_string()](zx_status_t status) { - ZX_LOG(FATAL, status) << "Agent disconnected: " << agent; + [this, agent = agent.as_string()](zx_status_t status) { + ZX_LOG(WARNING, status) << "Agent disconnected: " << agent; + agents_.erase(agent); }); } it->second.services->ConnectToService(interface.as_string(), diff --git a/chromium/fuchsia/base/fake_component_context.cc b/chromium/fuchsia/base/fake_component_context.cc index 4353c203673..b551d18b19c 100644 --- a/chromium/fuchsia/base/fake_component_context.cc +++ b/chromium/fuchsia/base/fake_component_context.cc @@ -5,6 +5,7 @@ #include "fuchsia/base/fake_component_context.h" #include <fuchsia/base/agent_impl.h> + #include <memory> #include <string> #include <utility> @@ -15,12 +16,12 @@ namespace cr_fuchsia { FakeComponentContext::FakeComponentContext( AgentImpl::CreateComponentStateCallback create_component_state_callback, - base::fuchsia::ServiceDirectory* service_directory, + sys::OutgoingDirectory* outgoing_directory, base::StringPiece component_url) - : binding_(service_directory, this), - // Publishing the Agent to |service_directory| is not necessary, but + : binding_(outgoing_directory, this), + // Publishing the Agent to |outgoing_directory| is not necessary, but // also shouldn't do any harm. - agent_impl_(service_directory, + agent_impl_(outgoing_directory, std::move(create_component_state_callback)), component_url_(component_url.as_string()) {} @@ -31,6 +32,15 @@ void FakeComponentContext::ConnectToAgent( agent_impl_.Connect(component_url_, std::move(services)); } +void FakeComponentContext::ConnectToAgentService( + fuchsia::modular::AgentServiceRequest request) { + if (!agent_services_) { + ConnectToAgent(component_url_, agent_services_.NewRequest(), nullptr); + } + agent_services_->ConnectToService(std::move(request.service_name()), + std::move(*request.mutable_channel())); +} + void FakeComponentContext::NotImplemented_(const std::string& name) { NOTIMPLEMENTED() << " API: " << name; } diff --git a/chromium/fuchsia/base/fake_component_context.h b/chromium/fuchsia/base/fake_component_context.h index da2a4924be8..c5187c2194f 100644 --- a/chromium/fuchsia/base/fake_component_context.h +++ b/chromium/fuchsia/base/fake_component_context.h @@ -18,7 +18,7 @@ namespace cr_fuchsia { // Used to test interactions with an Agent in unit-tests for a component. // |create_component_state_callback| can be used with a test-specific // ComponentStateBase to serve fake services to the component. -// |service_directory| specifies the directory into which the ComponentContext +// |outgoing_directory| specifies the directory into which the ComponentContext // should be published, alongside any other services the test wishes to provide // to the component's default service namespace. |component_url| specifies the // component identity that should be reported to the Agent @@ -27,7 +27,7 @@ class FakeComponentContext public: explicit FakeComponentContext( AgentImpl::CreateComponentStateCallback create_component_state_callback, - base::fuchsia::ServiceDirectory* service_directory, + sys::OutgoingDirectory* outgoing_directory, base::StringPiece component_url); ~FakeComponentContext() override; @@ -37,6 +37,8 @@ class FakeComponentContext fidl::InterfaceRequest<::fuchsia::sys::ServiceProvider> services, fidl::InterfaceRequest<fuchsia::modular::AgentController> controller) override; + void ConnectToAgentService( + fuchsia::modular::AgentServiceRequest request) override; void NotImplemented_(const std::string& name) override; private: @@ -44,6 +46,7 @@ class FakeComponentContext binding_; AgentImpl agent_impl_; const std::string component_url_; + fuchsia::sys::ServiceProviderPtr agent_services_; DISALLOW_COPY_AND_ASSIGN(FakeComponentContext); }; diff --git a/chromium/fuchsia/base/frame_test_util.cc b/chromium/fuchsia/base/frame_test_util.cc index 9bd182e7269..50fedd625c3 100644 --- a/chromium/fuchsia/base/frame_test_util.cc +++ b/chromium/fuchsia/base/frame_test_util.cc @@ -33,7 +33,7 @@ base::Optional<base::Value> ExecuteJavaScript(fuchsia::web::Frame* frame, base::RunLoop run_loop; ResultReceiver<fuchsia::web::Frame_ExecuteJavaScript_Result> result( run_loop.QuitClosure()); - frame->ExecuteJavaScript({"*"}, MemBufferFromString(script), + frame->ExecuteJavaScript({"*"}, MemBufferFromString(script, "test"), CallbackToFitFunction(result.GetReceiveCallback())); run_loop.Run(); diff --git a/chromium/fuchsia/base/lifecycle_impl.cc b/chromium/fuchsia/base/lifecycle_impl.cc index 2193ab0f3dd..f8f635befa4 100644 --- a/chromium/fuchsia/base/lifecycle_impl.cc +++ b/chromium/fuchsia/base/lifecycle_impl.cc @@ -4,13 +4,11 @@ #include "fuchsia/base/lifecycle_impl.h" -#include "base/fuchsia/service_directory.h" - namespace cr_fuchsia { -LifecycleImpl::LifecycleImpl(base::fuchsia::ServiceDirectory* service_directory, +LifecycleImpl::LifecycleImpl(sys::OutgoingDirectory* outgoing_directory, base::OnceClosure on_terminate) - : binding_(service_directory, this), + : binding_(outgoing_directory, this), on_terminate_(std::move(on_terminate)) {} LifecycleImpl::~LifecycleImpl() = default; diff --git a/chromium/fuchsia/base/lifecycle_impl.h b/chromium/fuchsia/base/lifecycle_impl.h index ecc7bde7dc7..ef8c79df101 100644 --- a/chromium/fuchsia/base/lifecycle_impl.h +++ b/chromium/fuchsia/base/lifecycle_impl.h @@ -10,11 +10,9 @@ #include "base/fuchsia/scoped_service_binding.h" #include "base/macros.h" -namespace base { -namespace fuchsia { -class ServiceDirectory; -} // namespace fuchsia -} // namespace base +namespace sys { +class OutgoingDirectory; +} // namespace sys namespace cr_fuchsia { @@ -23,7 +21,7 @@ namespace cr_fuchsia { // client drops the channel. class LifecycleImpl : public ::fuchsia::modular::Lifecycle { public: - LifecycleImpl(base::fuchsia::ServiceDirectory* service_directory, + LifecycleImpl(sys::OutgoingDirectory* outgoing_directory, base::OnceClosure on_terminate); ~LifecycleImpl() override; diff --git a/chromium/fuchsia/base/mem_buffer_util.cc b/chromium/fuchsia/base/mem_buffer_util.cc index c71f2180da1..4ae3ec9f547 100644 --- a/chromium/fuchsia/base/mem_buffer_util.cc +++ b/chromium/fuchsia/base/mem_buffer_util.cc @@ -26,12 +26,16 @@ bool ReadUTF8FromVMOAsUTF16(const fuchsia::mem::Buffer& buffer, return base::UTF8ToUTF16(&output_utf8.front(), output_utf8.size(), output); } -fuchsia::mem::Buffer MemBufferFromString(const base::StringPiece& data) { +fuchsia::mem::Buffer MemBufferFromString(base::StringPiece data, + base::StringPiece name) { fuchsia::mem::Buffer buffer; zx_status_t status = zx::vmo::create(data.size(), 0, &buffer.vmo); ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create"; + status = buffer.vmo.set_property(ZX_PROP_NAME, name.data(), name.size()); + ZX_DCHECK(status == ZX_OK, status); + status = buffer.vmo.write(data.data(), 0, data.size()); ZX_CHECK(status == ZX_OK, status) << "zx_vmo_write"; @@ -39,10 +43,12 @@ fuchsia::mem::Buffer MemBufferFromString(const base::StringPiece& data) { return buffer; } -fuchsia::mem::Buffer MemBufferFromString16(const base::StringPiece16& data) { +fuchsia::mem::Buffer MemBufferFromString16(const base::StringPiece16& data, + base::StringPiece name) { return MemBufferFromString( base::StringPiece(reinterpret_cast<const char*>(data.data()), - data.size() * sizeof(base::char16))); + data.size() * sizeof(base::char16)), + name); } bool StringFromMemBuffer(const fuchsia::mem::Buffer& buffer, @@ -74,12 +80,17 @@ fuchsia::mem::Buffer MemBufferFromFile(base::File file) { return output; } -fuchsia::mem::Buffer CloneBuffer(const fuchsia::mem::Buffer& buffer) { +fuchsia::mem::Buffer CloneBuffer(const fuchsia::mem::Buffer& buffer, + base::StringPiece name) { fuchsia::mem::Buffer output; output.size = buffer.size; zx_status_t status = buffer.vmo.create_child(ZX_VMO_CHILD_COPY_ON_WRITE, 0, buffer.size, &output.vmo); ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create_child"; + + status = output.vmo.set_property(ZX_PROP_NAME, name.data(), name.size()); + ZX_DCHECK(status == ZX_OK, status); + return output; } diff --git a/chromium/fuchsia/base/mem_buffer_util.h b/chromium/fuchsia/base/mem_buffer_util.h index cf204192cac..4eb39c84427 100644 --- a/chromium/fuchsia/base/mem_buffer_util.h +++ b/chromium/fuchsia/base/mem_buffer_util.h @@ -20,10 +20,12 @@ bool ReadUTF8FromVMOAsUTF16(const fuchsia::mem::Buffer& buffer, base::string16* output); // Creates a Fuchsia memory buffer from |data|. -fuchsia::mem::Buffer MemBufferFromString(const base::StringPiece& data); +fuchsia::mem::Buffer MemBufferFromString(base::StringPiece data, + base::StringPiece name); // Creates a Fuchsia memory buffer from the UTF-16 string |data|. -fuchsia::mem::Buffer MemBufferFromString16(const base::StringPiece16& data); +fuchsia::mem::Buffer MemBufferFromString16(const base::StringPiece16& data, + base::StringPiece name); // Reads the contents of |buffer| into |output|. // Returns true if the read operation succeeded. @@ -35,7 +37,8 @@ bool StringFromMemBuffer(const fuchsia::mem::Buffer& buffer, fuchsia::mem::Buffer MemBufferFromFile(base::File file); // Creates a non-resizeable, copy-on-write shared memory clone of |buffer|. -fuchsia::mem::Buffer CloneBuffer(const fuchsia::mem::Buffer& buffer); +fuchsia::mem::Buffer CloneBuffer(const fuchsia::mem::Buffer& buffer, + base::StringPiece name); } // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/message_port.cc b/chromium/fuchsia/base/message_port.cc index 55d3820e265..8485d747834 100644 --- a/chromium/fuchsia/base/message_port.cc +++ b/chromium/fuchsia/base/message_port.cc @@ -19,7 +19,7 @@ #include "mojo/public/cpp/system/message_pipe.h" #include "third_party/blink/public/common/messaging/message_port_channel.h" #include "third_party/blink/public/common/messaging/string_message_codec.h" -#include "third_party/blink/public/common/messaging/transferable_message_struct_traits.h" +#include "third_party/blink/public/common/messaging/transferable_message_mojom_traits.h" #include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h" namespace cr_fuchsia { @@ -87,7 +87,8 @@ base::Optional<fuchsia::web::WebMessage> FidlWebMessageFromMojo( base::STLClearObject(&data_utf16); - fuchsia::mem::Buffer data = cr_fuchsia::MemBufferFromString(data_utf8); + fuchsia::mem::Buffer data = + cr_fuchsia::MemBufferFromString(data_utf8, "cr-web-message-from-mojo"); if (!data.vmo) return {}; diff --git a/chromium/fuchsia/base/string_util.cc b/chromium/fuchsia/base/string_util.cc new file mode 100644 index 00000000000..7b1b242b546 --- /dev/null +++ b/chromium/fuchsia/base/string_util.cc @@ -0,0 +1,19 @@ +// Copyright 2019 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 "fuchsia/base/string_util.h" + +namespace cr_fuchsia { + +std::vector<uint8_t> StringToBytes(base::StringPiece str) { + const uint8_t* raw_data = reinterpret_cast<const uint8_t*>(str.data()); + return std::vector<uint8_t>(raw_data, raw_data + str.length()); +} + +base::StringPiece BytesAsString(const std::vector<uint8_t>& bytes) { + return base::StringPiece(reinterpret_cast<const char*>(bytes.data()), + bytes.size()); +} + +} // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/string_util.h b/chromium/fuchsia/base/string_util.h new file mode 100644 index 00000000000..377444939da --- /dev/null +++ b/chromium/fuchsia/base/string_util.h @@ -0,0 +1,24 @@ +// Copyright 2019 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 FUCHSIA_BASE_STRING_UTIL_H_ +#define FUCHSIA_BASE_STRING_UTIL_H_ + +#include <cstdint> +#include <vector> + +#include "base/containers/span.h" +#include "base/strings/string_piece.h" + +namespace cr_fuchsia { + +// Creates a byte vector from a string. +std::vector<uint8_t> StringToBytes(base::StringPiece str); + +// Creates a string from a byte vector. +base::StringPiece BytesAsString(const std::vector<uint8_t>& bytes); + +} // namespace cr_fuchsia + +#endif // FUCHSIA_BASE_STRING_UTIL_H_ diff --git a/chromium/fuchsia/engine/test_devtools_list_fetcher.cc b/chromium/fuchsia/base/test_devtools_list_fetcher.cc index 0cca8984da1..e8b0335837c 100644 --- a/chromium/fuchsia/engine/test_devtools_list_fetcher.cc +++ b/chromium/fuchsia/base/test_devtools_list_fetcher.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "fuchsia/engine/test_devtools_list_fetcher.h" +#include "fuchsia/base/test_devtools_list_fetcher.h" #include "base/callback.h" #include "base/json/json_reader.h" @@ -68,7 +68,11 @@ class DevToolsListFetcher : public net::URLFetcherDelegate { } // namespace +namespace cr_fuchsia { + base::Value GetDevToolsListFromPort(uint16_t port) { DevToolsListFetcher devtools_fetcher; return devtools_fetcher.GetDevToolsListFromPort(port); } + +} // namespace cr_fuchsia diff --git a/chromium/fuchsia/engine/test_devtools_list_fetcher.h b/chromium/fuchsia/base/test_devtools_list_fetcher.h index b133cbf4974..6971e496994 100644 --- a/chromium/fuchsia/engine/test_devtools_list_fetcher.h +++ b/chromium/fuchsia/base/test_devtools_list_fetcher.h @@ -2,13 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FUCHSIA_ENGINE_TEST_DEVTOOLS_LIST_FETCHER_H_ -#define FUCHSIA_ENGINE_TEST_DEVTOOLS_LIST_FETCHER_H_ +#ifndef FUCHSIA_BASE_TEST_DEVTOOLS_LIST_FETCHER_H_ +#define FUCHSIA_BASE_TEST_DEVTOOLS_LIST_FETCHER_H_ #include "base/values.h" +namespace cr_fuchsia { + // Returns the JSON value of the list URL for the DevTools service listening // on port |port| on localhost. Returns an empty value on error. base::Value GetDevToolsListFromPort(uint16_t port); -#endif // FUCHSIA_ENGINE_TEST_DEVTOOLS_LIST_FETCHER_H_ +} // namespace cr_fuchsia + +#endif // FUCHSIA_BASE_TEST_DEVTOOLS_LIST_FETCHER_H_ diff --git a/chromium/fuchsia/cipd/BUILD.gn b/chromium/fuchsia/cipd/BUILD.gn index 5f7c1f1bc06..ecaf66b30f9 100644 --- a/chromium/fuchsia/cipd/BUILD.gn +++ b/chromium/fuchsia/cipd/BUILD.gn @@ -57,6 +57,7 @@ process_version("build_id") { # cipd_path: The path where the package will be located inside the CIPD # repository. # cipd_description: Sets the "description" field in CIPD metadata. +# install_mode: String, should be either "symlink" or "copy". # deps: A list of targets to build prior to copying files. # sources: A list of files to copy into the staging root. template("cipd_archive") { @@ -65,15 +66,21 @@ template("cipd_archive") { "cipd_manifest_name", "cipd_path", "cipd_description", + "install_mode", "deps", "sources", ]) archive_staging_dir = "${target_gen_dir}/${target_name}" - + if (!defined(invoker.install_mode)) { + install_mode = "symlink" + } + assert(install_mode == "copy" || install_mode == "symlink", + "\"install_mode\" arg should be either \"copy\" or \"symlink\".") yaml_contents = [ "package: ${cipd_path}", "description: ${cipd_description}", "root: \${outdir}/" + rebase_path(archive_staging_dir, root_build_dir), + "install_mode: ${install_mode}", "data:", " - file: LICENSE", ] @@ -145,6 +152,49 @@ cipd_archive("http") { ] } +_stripped_chromedriver_file = "${root_out_dir}/clang_x64/stripped/chromedriver" + +action("strip_chromedriver_binary") { + testonly = true + + prog_name = "${root_out_dir}/clang_x64/chromedriver" + + deps = [ + "//chrome/test/chromedriver:chromedriver($host_toolchain)", + ] + script = "//build/gn_run_binary.py" + sources = [ + "//buildtools/third_party/eu-strip/bin/eu-strip", + prog_name, + ] + outputs = [ + _stripped_chromedriver_file, + ] + args = [ + rebase_path("//buildtools/third_party/eu-strip/bin/eu-strip", + root_build_dir), + "-o", + rebase_path(_stripped_chromedriver_file, root_build_dir), + rebase_path(prog_name, root_build_dir), + ] + + visibility = [ ":*" ] +} + +cipd_archive("chromedriver") { + cipd_manifest_name = "chromedriver.yaml" + cipd_path = "chromium/fuchsia/chromedriver/\${os}-\${arch}" + cipd_description = "Prebuilt Chromedriver binary for Fuchsia host." + install_mode = "copy" + deps = [ + ":strip_chromedriver_binary", + ] + + sources = [ + _stripped_chromedriver_file, + ] +} + cipd_archive("tests") { _manifest_path = "${target_gen_dir}/test_manifest.json" cipd_manifest_name = "tests.yaml" @@ -216,10 +266,26 @@ cipd_archive("debug_symbols") { sources = [ _symbol_manifest ] + _symbol_tarballs } +cipd_archive("clear_key_cdm") { + cipd_manifest_name = "clear_key_cdm.yaml" + cipd_path = "chromium/fuchsia/libclearkeycdm-\${targetarch}" + cipd_description = "Prebuilt libclearkeycdm.so binary for Fuchsia." + + deps = [ + "//media/cdm/library_cdm/clear_key_cdm:clear_key_cdm", + ] + + sources = [ + "${root_out_dir}/lib/libclearkeycdm.so", + ] +} + group("cipd") { testonly = true deps = [ ":castrunner", + ":chromedriver", + ":clear_key_cdm", ":debug_symbols", ":http", ":tests", diff --git a/chromium/fuchsia/engine/BUILD.gn b/chromium/fuchsia/engine/BUILD.gn index c999193fd0c..881fa15ec20 100644 --- a/chromium/fuchsia/engine/BUILD.gn +++ b/chromium/fuchsia/engine/BUILD.gn @@ -10,12 +10,6 @@ import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") import("//tools/grit/repack.gni") -declare_args() { - # Enables Vulkan in WebEngine (has effect only for context instances that have - # Vulkan loader service in the service directory). - web_engine_enable_vulkan = false -} - config("web_engine_implementation") { defines = [ "WEB_ENGINE_IMPLEMENTATION" ] } @@ -54,6 +48,7 @@ repack("web_engine_pak") { "//content/app/resources", "//content/app/strings", "//content/browser/tracing:resources", + "//gpu/command_buffer/service", "//mojo/public/js:resources", "//net:net_resources", "//third_party/blink/public:resources", @@ -71,6 +66,7 @@ component("web_engine_core") { ":mojom", ":web_engine_pak", "//base", + "//base:base_static", "//components/version_info", "//content/public/app:both", "//content/public/browser", @@ -80,13 +76,23 @@ component("web_engine_core") { "//fuchsia/base", "//fuchsia/base:message_port", "//fuchsia/base:modular", + "//gpu/command_buffer/service", + "//media/fuchsia/cdm/service", + "//media/fuchsia/mojom", "//mojo/public/cpp/bindings", "//services/network/public/cpp", + "//services/network/public/mojom", "//services/service_manager/sandbox", - "//skia/public/interfaces", + "//skia/public/mojom", "//third_party/blink/public/common", + "//third_party/fuchsia-sdk/sdk:accessibility_semantics", + "//third_party/fuchsia-sdk/sdk:math", + "//third_party/fuchsia-sdk/sdk:scenic_cpp", + "//third_party/fuchsia-sdk/sdk:sys_cpp", "//third_party/fuchsia-sdk/sdk:web", + "//third_party/widevine/cdm:headers", "//ui/aura", + "//ui/base", "//ui/base/ime", "//ui/display", "//ui/ozone", @@ -105,8 +111,14 @@ component("web_engine_core") { ] configs += [ ":web_engine_implementation" ] sources = [ + "browser/accessibility_bridge.cc", + "browser/accessibility_bridge.h", + "browser/content_directory_loader_factory.cc", + "browser/content_directory_loader_factory.h", "browser/context_impl.cc", "browser/context_impl.h", + "browser/cookie_manager_impl.cc", + "browser/cookie_manager_impl.h", "browser/discarding_event_filter.cc", "browser/discarding_event_filter.h", "browser/frame_impl.cc", @@ -119,6 +131,8 @@ component("web_engine_core") { "browser/web_engine_browser_main.h", "browser/web_engine_browser_main_parts.cc", "browser/web_engine_browser_main_parts.h", + "browser/web_engine_cdm_service.cc", + "browser/web_engine_cdm_service.h", "browser/web_engine_content_browser_client.cc", "browser/web_engine_content_browser_client.h", "browser/web_engine_devtools_manager_delegate.cc", @@ -131,8 +145,6 @@ component("web_engine_core") { "browser/web_engine_remote_debugging.h", "browser/web_engine_screen.cc", "browser/web_engine_screen.h", - "browser/web_engine_url_request_context_getter.cc", - "browser/web_engine_url_request_context_getter.h", "common.cc", "common.h", "context_provider_impl.cc", @@ -153,9 +165,6 @@ component("web_engine_core") { ":*", "//fuchsia/runners:cast_runner_browsertests__exec", ] - if (web_engine_enable_vulkan) { - defines = [ "WEB_ENGINE_ENABLE_VULKAN" ] - } } executable("web_engine_exe") { @@ -210,6 +219,7 @@ source_set("browsertest_core") { test("web_engine_browsertests") { sources = [ + "browser/content_directory_browsertest.cc", "browser/context_impl_browsertest.cc", "browser/frame_impl_browsertest.cc", ] @@ -234,17 +244,24 @@ test("web_engine_browsertests") { test("web_engine_unittests") { sources = [ + "browser/accessibility_bridge_unittest.cc", + "browser/cookie_manager_impl_unittest.cc", "browser/frame_impl_unittest.cc", "context_provider_impl_unittest.cc", "fake_context.cc", "fake_context.h", + "test/run_all_unittests.cc", ] deps = [ ":web_engine_core", - "//base/test:run_all_unittests", "//base/test:test_support", + "//fuchsia/base:test_support", + "//mojo/core/embedder", + "//services/network:network_service", + "//services/network/public/mojom", "//testing/gmock", "//testing/gtest", + "//third_party/fuchsia-sdk/sdk:scenic_cpp", "//third_party/fuchsia-sdk/sdk:web", ] } @@ -254,8 +271,6 @@ test("web_engine_integration_tests") { sources = [ "test_debug_listener.cc", "test_debug_listener.h", - "test_devtools_list_fetcher.cc", - "test_devtools_list_fetcher.h", "web_engine_debug_integration_test.cc", "web_engine_integration_test.cc", ] @@ -267,8 +282,6 @@ test("web_engine_integration_tests") { "//base/test:run_all_unittests", "//fuchsia/base", "//fuchsia/base:test_support", - "//net", - "//net:test_support", "//third_party/fuchsia-sdk/sdk:web", ] package_deps = [ [ diff --git a/chromium/fuchsia/engine/DEPS b/chromium/fuchsia/engine/DEPS index a86f48cf2b7..eb2adde5782 100644 --- a/chromium/fuchsia/engine/DEPS +++ b/chromium/fuchsia/engine/DEPS @@ -1,5 +1,8 @@ include_rules = [ + "+components/viz/common", "+content/public/app", + "+gpu/command_buffer/service", "+services/service_manager", "+ui/base", + "+ui/gl/gl_switches.h", ] diff --git a/chromium/fuchsia/engine/browser/DEPS b/chromium/fuchsia/engine/browser/DEPS index 77a16fce5c5..70b09f16e2d 100644 --- a/chromium/fuchsia/engine/browser/DEPS +++ b/chromium/fuchsia/engine/browser/DEPS @@ -3,13 +3,17 @@ include_rules = [ "+components/version_info", "+content/public/common", "+content/public/browser", - "+content/public/test", + "+media/base", + "+media/fuchsia/cdm/service", + "+media/fuchsia/mojom", "+mojo/public/cpp/bindings", "+mojo/public/cpp/system", + "+services/network/public/mojom", "+third_party/blink/public/common/associated_interfaces", "+third_party/blink/public/common/logging", "+third_party/blink/public/common/messaging", "+third_party/blink/public/mojom/messaging", + "+third_party/widevine/cdm", "+ui/aura", "+ui/base/ime", "+ui/display", @@ -18,3 +22,10 @@ include_rules = [ "+ui/platform_window", "+ui/wm/core", ] + +specific_include_rules = { + ".*_(unit|browser|api)test\.cc": [ + "+content/public/test", + "+services/network", + ], +} diff --git a/chromium/fuchsia/engine/browser/accessibility_bridge.cc b/chromium/fuchsia/engine/browser/accessibility_bridge.cc new file mode 100644 index 00000000000..b2f521c0dde --- /dev/null +++ b/chromium/fuchsia/engine/browser/accessibility_bridge.cc @@ -0,0 +1,35 @@ +// Copyright 2019 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 "fuchsia/engine/browser/accessibility_bridge.h" + +#include "base/logging.h" + +AccessibilityBridge::AccessibilityBridge( + fuchsia::accessibility::semantics::SemanticsManagerPtr semantics_manager, + fuchsia::ui::views::ViewRef view_ref) + : binding_(this) { + semantics_manager->RegisterViewForSemantics( + std::move(view_ref), binding_.NewBinding(), tree_ptr_.NewRequest()); +} + +AccessibilityBridge::~AccessibilityBridge() = default; + +void AccessibilityBridge::OnAccessibilityActionRequested( + uint32_t node_id, + fuchsia::accessibility::semantics::Action action, + OnAccessibilityActionRequestedCallback callback) { + NOTIMPLEMENTED(); +} + +void AccessibilityBridge::HitTest(fuchsia::math::PointF local_point, + HitTestCallback callback) { + NOTIMPLEMENTED(); +} + +void AccessibilityBridge::OnSemanticsModeChanged( + bool updates_enabled, + OnSemanticsModeChangedCallback callback) { + NOTIMPLEMENTED(); +} diff --git a/chromium/fuchsia/engine/browser/accessibility_bridge.h b/chromium/fuchsia/engine/browser/accessibility_bridge.h new file mode 100644 index 00000000000..9a95027c332 --- /dev/null +++ b/chromium/fuchsia/engine/browser/accessibility_bridge.h @@ -0,0 +1,47 @@ +// Copyright 2019 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 FUCHSIA_ENGINE_BROWSER_ACCESSIBILITY_BRIDGE_H_ +#define FUCHSIA_ENGINE_BROWSER_ACCESSIBILITY_BRIDGE_H_ + +#include <fuchsia/accessibility/semantics/cpp/fidl.h> +#include <fuchsia/math/cpp/fidl.h> +#include <fuchsia/ui/views/cpp/fidl.h> +#include <lib/fidl/cpp/binding.h> + +#include "base/macros.h" + +// This class is the intermediate for accessibility between Chrome and Fuchsia. +// It handles registration to the Fuchsia Semantics Manager, translating events +// and data structures between the two services, and forwarding actions and +// events. +// The lifetime of an instance of AccessibilityBridge is the same as that of a +// View created by FrameImpl. This class refers to the View via the +// caller-supplied ViewRef. +class AccessibilityBridge + : public fuchsia::accessibility::semantics::SemanticListener { + public: + AccessibilityBridge( + fuchsia::accessibility::semantics::SemanticsManagerPtr semantics_manager, + fuchsia::ui::views::ViewRef view_ref); + ~AccessibilityBridge() final; + + private: + // fuchsia::accessibility::semantics::SemanticListener implementation. + void OnAccessibilityActionRequested( + uint32_t node_id, + fuchsia::accessibility::semantics::Action action, + OnAccessibilityActionRequestedCallback callback) final; + void HitTest(fuchsia::math::PointF local_point, + HitTestCallback callback) final; + void OnSemanticsModeChanged(bool updates_enabled, + OnSemanticsModeChangedCallback callback) final; + + fuchsia::accessibility::semantics::SemanticTreePtr tree_ptr_; + fidl::Binding<fuchsia::accessibility::semantics::SemanticListener> binding_; + + DISALLOW_COPY_AND_ASSIGN(AccessibilityBridge); +}; + +#endif // FUCHSIA_ENGINE_BROWSER_ACCESSIBILITY_BRIDGE_H_ diff --git a/chromium/fuchsia/engine/browser/accessibility_bridge_unittest.cc b/chromium/fuchsia/engine/browser/accessibility_bridge_unittest.cc new file mode 100644 index 00000000000..49a4f1ad813 --- /dev/null +++ b/chromium/fuchsia/engine/browser/accessibility_bridge_unittest.cc @@ -0,0 +1,102 @@ +// Copyright 2019 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 "fuchsia/engine/browser/accessibility_bridge.h" + +#include <fuchsia/accessibility/semantics/cpp/fidl.h> +#include <fuchsia/accessibility/semantics/cpp/fidl_test_base.h> +#include <lib/sys/cpp/component_context.h> +#include <lib/ui/scenic/cpp/view_ref_pair.h> +#include <zircon/types.h> + +#include "base/fuchsia/default_context.h" +#include "base/fuchsia/scoped_service_binding.h" +#include "base/fuchsia/service_directory_client.h" +#include "base/logging.h" +#include "base/test/task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +using fuchsia::accessibility::semantics::SemanticListener; +using fuchsia::accessibility::semantics::SemanticsManager; + +namespace { + +class FakeSemanticsManager : public fuchsia::accessibility::semantics::testing:: + SemanticsManager_TestBase { + public: + explicit FakeSemanticsManager() = default; + ~FakeSemanticsManager() override = default; + + // fuchsia::accessibility::semantics::SemanticsManager implementation. + void RegisterViewForSemantics( + fuchsia::ui::views::ViewRef view_ref, + fidl::InterfaceHandle<SemanticListener> listener, + fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree> + semantic_tree) final { + view_ref_ = std::move(view_ref); + listener_ = std::move(listener); + semantic_tree_ = std::move(semantic_tree); + } + + bool is_view_registered() const { return view_ref_.reference.is_valid(); } + + bool is_listener_handle_valid() const { return listener_.is_valid(); } + + bool is_semantic_tree_handle_valid() const { + return semantic_tree_.is_valid(); + } + + void NotImplemented_(const std::string& name) final { + NOTIMPLEMENTED() << name; + } + + private: + fuchsia::ui::views::ViewRef view_ref_; + fidl::InterfaceHandle<SemanticListener> listener_; + fidl::InterfaceRequest<fuchsia::accessibility::semantics::SemanticTree> + semantic_tree_; + + DISALLOW_COPY_AND_ASSIGN(FakeSemanticsManager); +}; + +} // namespace + +class AccessibilityBridgeTest : public testing::Test { + public: + AccessibilityBridgeTest() + : task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::IO), + semantics_manager_binding_(&semantics_manager_) {} + ~AccessibilityBridgeTest() override = default; + + void CreateAccessibilityBridge() { + auto view_ref_pair = scenic::ViewRefPair::New(); + control_ref_ = std::move(view_ref_pair.control_ref); + fuchsia::accessibility::semantics::SemanticsManagerPtr + semantics_manager_ptr; + semantics_manager_binding_.Bind(semantics_manager_ptr.NewRequest()); + accessibility_bridge_ = std::make_unique<AccessibilityBridge>( + std::move(semantics_manager_ptr), std::move(view_ref_pair.view_ref)); + } + + protected: + base::test::SingleThreadTaskEnvironment task_environment_; + std::unique_ptr<AccessibilityBridge> accessibility_bridge_; + FakeSemanticsManager semantics_manager_; + fidl::Binding<fuchsia::accessibility::semantics::SemanticsManager> + semantics_manager_binding_; + fuchsia::ui::views::ViewRefControl control_ref_; + + DISALLOW_COPY_AND_ASSIGN(AccessibilityBridgeTest); +}; + +// Test registration to the SemanticsManager. +TEST_F(AccessibilityBridgeTest, RegisterViewRef) { + CreateAccessibilityBridge(); + // Run loop so FIDL registration requests are processed. + task_environment_.RunUntilIdle(); + EXPECT_TRUE(semantics_manager_.is_view_registered()); + EXPECT_TRUE(semantics_manager_.is_listener_handle_valid()); + EXPECT_TRUE(semantics_manager_.is_semantic_tree_handle_valid()); +} diff --git a/chromium/fuchsia/engine/browser/content_directory_browsertest.cc b/chromium/fuchsia/engine/browser/content_directory_browsertest.cc new file mode 100644 index 00000000000..54f7f787cfa --- /dev/null +++ b/chromium/fuchsia/engine/browser/content_directory_browsertest.cc @@ -0,0 +1,208 @@ +// Copyright 2019 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 <lib/fdio/directory.h> + +#include "fuchsia/engine/test/web_engine_browser_test.h" + +#include "base/files/file_path.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "base/path_service.h" +#include "base/test/bind_test_util.h" +#include "base/test/test_timeouts.h" +#include "fuchsia/base/frame_test_util.h" +#include "fuchsia/base/test_navigation_listener.h" +#include "fuchsia/engine/browser/content_directory_loader_factory.h" +#include "fuchsia/engine/common.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ContentDirectoryTest : public cr_fuchsia::WebEngineBrowserTest { + public: + ContentDirectoryTest() + : run_timeout_(TestTimeouts::action_timeout(), + base::MakeExpectedNotRunClosure(FROM_HERE)) {} + ~ContentDirectoryTest() override = default; + + void SetUp() override { + // Set this flag early so that the fuchsia-dir:// scheme will be + // registered at browser startup. + base::CommandLine::ForCurrentProcess()->AppendSwitch(kContentDirectories); + + cr_fuchsia::WebEngineBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + std::vector<fuchsia::web::ContentDirectoryProvider> providers; + + fuchsia::web::ContentDirectoryProvider provider; + provider.set_name("testdata"); + base::FilePath pkg_path; + base::PathService::Get(base::DIR_ASSETS, &pkg_path); + provider.set_directory( + OpenDirectoryHandle(pkg_path.AppendASCII("fuchsia/engine/test/data"))); + providers.emplace_back(std::move(provider)); + + provider = {}; + provider.set_name("alternate"); + provider.set_directory( + OpenDirectoryHandle(pkg_path.AppendASCII("fuchsia/engine/test/data"))); + providers.emplace_back(std::move(provider)); + + ContentDirectoryLoaderFactory::SetContentDirectoriesForTest( + std::move(providers)); + + cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread(); + } + + void TearDown() override { + ContentDirectoryLoaderFactory::SetContentDirectoriesForTest({}); + } + + fidl::InterfaceHandle<fuchsia::io::Directory> OpenDirectoryHandle( + const base::FilePath& path) { + zx::channel directory_channel; + zx::channel remote_directory_channel; + zx_status_t status = + zx::channel::create(0, &directory_channel, &remote_directory_channel); + ZX_DCHECK(status == ZX_OK, status) << "zx_channel_create"; + + status = fdio_open( + path.value().c_str(), + fuchsia::io::OPEN_FLAG_DIRECTORY | fuchsia::io::OPEN_RIGHT_READABLE, + remote_directory_channel.release()); + ZX_DCHECK(status == ZX_OK, status) << "fdio_open"; + + return fidl::InterfaceHandle<fuchsia::io::Directory>( + std::move(directory_channel)); + } + + protected: + // Creates a Frame with |navigation_listener_| attached. + fuchsia::web::FramePtr CreateFrame() { + return WebEngineBrowserTest::CreateFrame(&navigation_listener_); + } + + cr_fuchsia::TestNavigationListener navigation_listener_; + + private: + const base::RunLoop::ScopedRunTimeoutForTest run_timeout_; + + DISALLOW_COPY_AND_ASSIGN(ContentDirectoryTest); +}; + +IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, Navigate) { + const GURL kUrl("fuchsia-dir://testdata/title1.html"); + + fuchsia::web::FramePtr frame = CreateFrame(); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + navigation_listener_.RunUntilUrlEquals(kUrl); +} + +// Navigate to a resource stored under a secondary provider. +IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, NavigateAlternate) { + const GURL kUrl("fuchsia-dir://alternate/title1.html"); + + fuchsia::web::FramePtr frame = CreateFrame(); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + navigation_listener_.RunUntilUrlEquals(kUrl); +} + +IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, ScriptSubresource) { + const GURL kUrl("fuchsia-dir://testdata/include_script.html"); + + fuchsia::web::FramePtr frame = CreateFrame(); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "title set by script"); +} + +IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, ImgSubresource) { + const GURL kUrl("fuchsia-dir://testdata/include_image.html"); + + fuchsia::web::FramePtr frame = CreateFrame(); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "image fetched"); +} + +// Verify that resource providers are origin-isolated. +IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, ScriptSrcCrossOriginBlocked) { + const GURL kUrl("fuchsia-dir://testdata/cross_origin_include_script.html"); + + fuchsia::web::FramePtr frame = CreateFrame(); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + // If the cross-origin script succeeded, then we should see "title set by + // script". If "not clobbered" remains set, then we know that CROS enforcement + // is working. + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "same origin ftw"); +} + +IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, CrossOriginImgBlocked) { + const GURL kUrl("fuchsia-dir://testdata/cross_origin_include_image.html"); + + fuchsia::web::FramePtr frame = CreateFrame(); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + + navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "image rejected"); +} + +IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, MetadataFileParsed) { + const GURL kUrl("fuchsia-dir://testdata/mime_override.html"); + + fuchsia::web::FramePtr frame = CreateFrame(); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + navigation_listener_.RunUntilUrlAndTitleEquals( + kUrl, "content-type: text/bleep; charset=US-ASCII"); +} + +IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, BadMetadataFile) { + const GURL kUrl("fuchsia-dir://testdata/mime_override_invalid.html"); + + fuchsia::web::FramePtr frame = CreateFrame(); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, + "content-type: text/html"); +} + +} // namespace diff --git a/chromium/fuchsia/engine/browser/content_directory_loader_factory.cc b/chromium/fuchsia/engine/browser/content_directory_loader_factory.cc new file mode 100644 index 00000000000..5ca9821d7d0 --- /dev/null +++ b/chromium/fuchsia/engine/browser/content_directory_loader_factory.cc @@ -0,0 +1,382 @@ +// Copyright 2019 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 "fuchsia/engine/browser/content_directory_loader_factory.h" + +#include <lib/fdio/directory.h> +#include <lib/fdio/fdio.h> + +#include <map> +#include <memory> +#include <utility> + +#include "base/files/memory_mapped_file.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "base/json/json_reader.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "base/strings/strcat.h" +#include "base/strings/string_split.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "fuchsia/engine/common.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/system/data_pipe_producer.h" +#include "mojo/public/cpp/system/string_data_source.h" +#include "net/base/filename_util.h" +#include "net/base/mime_sniffer.h" +#include "net/base/parse_number.h" +#include "services/network/public/cpp/resource_response.h" +#include "services/network/public/mojom/url_loader.mojom.h" + +namespace { + +using ContentDirectoriesMap = + std::map<std::string, fidl::InterfaceHandle<fuchsia::io::Directory>>; + +ContentDirectoriesMap ParseContentDirectoriesFromCommandLine() { + ContentDirectoriesMap directories; + + // Parse the list of content directories from the command line. + std::string content_directories_unsplit = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + kContentDirectories); + if (content_directories_unsplit.empty()) + return {}; + + base::StringPairs named_handle_ids; + if (!base::SplitStringIntoKeyValuePairs(content_directories_unsplit, '=', ',', + &named_handle_ids)) { + LOG(WARNING) << "Couldn't parse --" << kContentDirectories + << " into KV pairs: " << content_directories_unsplit; + return {}; + } + + for (const auto& named_handle_id : named_handle_ids) { + uint32_t handle_id = 0; + if (!net::ParseUint32(named_handle_id.second, &handle_id)) { + DLOG(FATAL) << "Couldn't parse handle ID as uint32: " + << named_handle_id.second; + continue; + } + + auto directory_channel = zx::channel(zx_take_startup_handle(handle_id)); + if (directory_channel == ZX_HANDLE_INVALID) { + DLOG(FATAL) << "Couldn't take startup handle: " << handle_id; + continue; + } + + directories.emplace(named_handle_id.first, + fidl::InterfaceHandle<fuchsia::io::Directory>( + std::move(directory_channel))); + } + return directories; +} + +// Gets the process-global list of content directory channels. +ContentDirectoriesMap* GetContentDirectories() { + static base::NoDestructor<ContentDirectoriesMap> directories( + ParseContentDirectoriesFromCommandLine()); + + return directories.get(); +} + +// Returns a list of populated response HTTP headers. +// |mime_type|: The MIME type of the resource. +// |charset|: The resource's character set. Optional. If omitted, the browser +// will assume the charset to be "text/plain" by default. +scoped_refptr<net::HttpResponseHeaders> CreateHeaders( + base::StringPiece mime_type, + const base::Optional<std::string>& charset) { + constexpr char kXFrameOptionsHeader[] = "X-Frame-Options: DENY"; + constexpr char kCacheHeader[] = "Cache-Control: no-cache"; + constexpr char kContentTypePrefix[] = "Content-Type: "; + constexpr char kCharsetSeparator[] = "; charset="; + + auto headers = + base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK\r\n"); + headers->AddHeader(kXFrameOptionsHeader); + headers->AddHeader(kCacheHeader); + + if (charset) { + headers->AddHeader(base::StrCat( + {kContentTypePrefix, mime_type, kCharsetSeparator, *charset})); + } else { + headers->AddHeader(base::StrCat({kContentTypePrefix, mime_type})); + } + + return headers; +} + +// Copies data from a fuchsia.io.Node file into a URL response stream. +class ContentDirectoryURLLoader : public network::mojom::URLLoader { + public: + ContentDirectoryURLLoader() = default; + ~ContentDirectoryURLLoader() final = default; + + // Creates a read-only MemoryMappedFile view to |file|. + bool MapFile(fidl::InterfaceHandle<fuchsia::io::Node> file, + base::MemoryMappedFile* mmap) { + // Bind the file channel to a FDIO entry and then a file descriptor so that + // we can use it for reading. + fdio_t* fdio = nullptr; + zx_status_t status = fdio_create(file.TakeChannel().release(), &fdio); + if (status == ZX_ERR_PEER_CLOSED) { + // File-not-found errors are expected in some cases, so handle this result + // w/o logging error text. + return false; + } else if (status != ZX_OK) { + ZX_DLOG_IF(WARNING, status != ZX_OK, status) << "fdio_create"; + return false; + } + + base::ScopedFD fd(fdio_bind_to_fd(fdio, -1, 0)); + if (!fd.is_valid()) { + LOG(ERROR) << "fdio_bind_to_fd returned an invalid FD."; + return false; + } + + // Map the file into memory. + if (!mmap->Initialize(base::File(fd.release()), + base::MemoryMappedFile::READ_ONLY)) { + return false; + } + + return true; + } + + // Initiates data transfer from |file_channel| to |client_info|. + // |metadata_channel|, if it is connected to a file, is accessed to get the + // MIME type and charset of the file. + static void CreateAndStart( + network::mojom::URLLoaderRequest url_loader_request, + const network::ResourceRequest& request, + network::mojom::URLLoaderClientPtrInfo client_info, + fidl::InterfaceHandle<fuchsia::io::Node> file_channel, + fidl::InterfaceHandle<fuchsia::io::Node> metadata_channel) { + std::unique_ptr<ContentDirectoryURLLoader> loader = + std::make_unique<ContentDirectoryURLLoader>(); + loader->Start(request, std::move(client_info), std::move(file_channel), + std::move(metadata_channel)); + + // |loader|'s lifetime is bound to the lifetime of the URLLoader Mojo + // client endpoint. + mojo::MakeStrongBinding(std::move(loader), std::move(url_loader_request)); + } + + void Start(const network::ResourceRequest& request, + network::mojom::URLLoaderClientPtrInfo client_info, + fidl::InterfaceHandle<fuchsia::io::Node> file_channel, + fidl::InterfaceHandle<fuchsia::io::Node> metadata_channel) { + client_.Bind(std::move(client_info)); + + if (!MapFile(std::move(file_channel), &mmap_)) { + client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + return; + } + + // Construct and deliver the HTTP response header. + network::ResourceResponseHead response; + + // Read the charset and MIME type from the optional _metadata file. + base::Optional<std::string> charset; + base::Optional<std::string> mime_type; + base::MemoryMappedFile metadata_mmap; + if (MapFile(std::move(metadata_channel), &metadata_mmap)) { + base::Optional<base::Value> metadata_parsed = base::JSONReader::Read( + base::StringPiece(reinterpret_cast<char*>(metadata_mmap.data()), + metadata_mmap.length())); + + if (metadata_parsed && metadata_parsed->is_dict()) { + if (metadata_parsed->FindStringKey("charset")) + charset = *metadata_parsed->FindStringKey("charset"); + + if (metadata_parsed->FindStringKey("mime")) + mime_type = *metadata_parsed->FindStringKey("mime"); + } + } + + // If a MIME type wasn't specified, then fall back on inferring the type + // from the file's contents. + if (!mime_type) { + mime_type.emplace(); + net::SniffMimeType( + reinterpret_cast<char*>(mmap_.data()), mmap_.length(), request.url, + "", net::ForceSniffFileUrlsForHtml::kDisabled, &*mime_type); + } + response.mime_type = *mime_type; + response.headers = CreateHeaders(response.mime_type, charset); + response.content_length = mmap_.length(); + client_->OnReceiveResponse(response); + + // Set up the Mojo DataPipe used for streaming the response payload to the + // client. + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + MojoResult rv = mojo::CreateDataPipe(0, &producer_handle, &consumer_handle); + if (rv != MOJO_RESULT_OK) { + client_->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES)); + return; + } + + client_->OnStartLoadingResponseBody(std::move(consumer_handle)); + + // Start reading the contents of |mmap_| into the response DataPipe. + body_writer_ = + std::make_unique<mojo::DataPipeProducer>(std::move(producer_handle)); + body_writer_->Write( + std::make_unique<mojo::StringDataSource>( + base::StringPiece(reinterpret_cast<char*>(mmap_.data()), + mmap_.length()), + mojo::StringDataSource::AsyncWritingMode:: + STRING_STAYS_VALID_UNTIL_COMPLETION), + base::BindOnce(&ContentDirectoryURLLoader::OnWriteComplete, + base::Unretained(this))); + } + + // network::mojom::URLLoader implementation: + void FollowRedirect(const std::vector<std::string>& removed_headers, + const net::HttpRequestHeaders& modified_request_headers, + const base::Optional<GURL>& new_url) override {} + void SetPriority(net::RequestPriority priority, + int32_t intra_priority_value) override {} + void PauseReadingBodyFromNet() override {} + void ResumeReadingBodyFromNet() override {} + + private: + // Called when body_writer_->Write() has completed asynchronously. + void OnWriteComplete(MojoResult result) { + // Signal stream EOF to the client. + body_writer_.reset(); + + if (result != MOJO_RESULT_OK) { + client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + return; + } + + network::URLLoaderCompletionStatus status(net::OK); + status.encoded_data_length = mmap_.length(); + status.encoded_body_length = mmap_.length(); + status.decoded_body_length = mmap_.length(); + client_->OnComplete(std::move(status)); + } + + // Used for sending status codes and response payloads to the client. + network::mojom::URLLoaderClientPtr client_; + + // A read-only, memory mapped view of the file being loaded. + base::MemoryMappedFile mmap_; + + // Manages chunked data transfer over the response DataPipe. + std::unique_ptr<mojo::DataPipeProducer> body_writer_; + + DISALLOW_COPY_AND_ASSIGN(ContentDirectoryURLLoader); +}; + +} // namespace + +ContentDirectoryLoaderFactory::ContentDirectoryLoaderFactory() + : task_runner_(base::CreateSequencedTaskRunner( + {base::ThreadPool(), base::MayBlock(), + base::TaskPriority::USER_VISIBLE, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) {} + +ContentDirectoryLoaderFactory::~ContentDirectoryLoaderFactory() {} + +net::Error ContentDirectoryLoaderFactory::OpenFileFromDirectory( + const std::string& directory_name, + base::FilePath path, + fidl::InterfaceRequest<fuchsia::io::Node> file_request) { + DCHECK(file_request); + + ContentDirectoriesMap* content_directories = GetContentDirectories(); + if (content_directories->find(directory_name) == content_directories->end()) + return net::ERR_FILE_NOT_FOUND; + + const fidl::InterfaceHandle<fuchsia::io::Directory>& directory = + content_directories->at(directory_name); + if (!directory) + return net::ERR_INVALID_HANDLE; + + zx_status_t status = fdio_open_at( + directory.channel().get(), path.value().c_str(), + fuchsia::io::OPEN_RIGHT_READABLE, file_request.TakeChannel().release()); + if (status != ZX_OK) { + ZX_DLOG(WARNING, status) << "fdio_open_at"; + return net::ERR_FILE_NOT_FOUND; + } + + return net::OK; +} + +void ContentDirectoryLoaderFactory::CreateLoaderAndStart( + network::mojom::URLLoaderRequest loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + network::mojom::URLLoaderClientPtr client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + if (!request.url.SchemeIs(kFuchsiaContentDirectoryScheme) || + !request.url.is_valid()) { + client->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_INVALID_URL)); + return; + } + + if (request.method != "GET") { + client->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_METHOD_NOT_SUPPORTED)); + return; + } + + fidl::InterfaceHandle<fuchsia::io::Node> file_handle; + net::Error open_result = OpenFileFromDirectory( + request.url.GetOrigin().host(), base::FilePath(request.url.path()), + file_handle.NewRequest()); + if (open_result != net::OK) { + client->OnComplete(network::URLLoaderCompletionStatus(open_result)); + return; + } + DCHECK(file_handle); + + // Metadata files are optional. In the event that the file is absent, + // |metadata_channel| will produce a ZX_CHANNEL_PEER_CLOSED status inside + // ContentDirectoryURLLoader::Start(). + fidl::InterfaceHandle<fuchsia::io::Node> metadata_handle; + open_result = + OpenFileFromDirectory(request.url.GetOrigin().host(), + base::FilePath(request.url.path() + "._metadata"), + metadata_handle.NewRequest()); + if (open_result != net::OK) { + client->OnComplete(network::URLLoaderCompletionStatus(open_result)); + return; + } + + // Load the resource on a blocking-capable TaskRunner. + task_runner_->PostTask(FROM_HERE, + base::Bind(&ContentDirectoryURLLoader::CreateAndStart, + base::Passed(std::move(loader)), request, + base::Passed(client.PassInterface()), + base::Passed(std::move(file_handle)), + base::Passed(std::move(metadata_handle)))); +} + +void ContentDirectoryLoaderFactory::Clone( + network::mojom::URLLoaderFactoryRequest loader) { + bindings_.AddBinding(this, std::move(loader)); +} + +void ContentDirectoryLoaderFactory::SetContentDirectoriesForTest( + std::vector<fuchsia::web::ContentDirectoryProvider> directories) { + ContentDirectoriesMap* current_process_directories = GetContentDirectories(); + + current_process_directories->clear(); + for (fuchsia::web::ContentDirectoryProvider& directory : directories) { + current_process_directories->emplace(directory.name(), + directory.mutable_directory()->Bind()); + } +} diff --git a/chromium/fuchsia/engine/browser/content_directory_loader_factory.h b/chromium/fuchsia/engine/browser/content_directory_loader_factory.h new file mode 100644 index 00000000000..c5257c29df5 --- /dev/null +++ b/chromium/fuchsia/engine/browser/content_directory_loader_factory.h @@ -0,0 +1,57 @@ +// Copyright 2019 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 FUCHSIA_ENGINE_BROWSER_CONTENT_DIRECTORY_LOADER_FACTORY_H_ +#define FUCHSIA_ENGINE_BROWSER_CONTENT_DIRECTORY_LOADER_FACTORY_H_ + +#include <fuchsia/io/cpp/fidl.h> +#include <fuchsia/web/cpp/fidl.h> +#include <lib/fidl/cpp/interface_handle.h> +#include <memory> +#include <string> +#include <vector> + +#include "fuchsia/engine/web_engine_export.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" + +// Creates a URLLoaderFactory which services requests for resources stored +// under named directories. The directories are accessed using the +// fuchsia-dir:// scheme. +class ContentDirectoryLoaderFactory : public network::mojom::URLLoaderFactory { + public: + ContentDirectoryLoaderFactory(); + ~ContentDirectoryLoaderFactory() override; + + // Sets the list of content directories for the duration of the process. + // Can be called multiple times for clearing or replacing the list. + static WEB_ENGINE_EXPORT void SetContentDirectoriesForTest( + std::vector<fuchsia::web::ContentDirectoryProvider> directories); + + // network::mojom::URLLoaderFactory: + void CreateLoaderAndStart( + network::mojom::URLLoaderRequest loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + network::mojom::URLLoaderClientPtr client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) final; + void Clone(network::mojom::URLLoaderFactoryRequest loader) final; + + private: + net::Error OpenFileFromDirectory( + const std::string& directory_name, + base::FilePath path, + fidl::InterfaceRequest<fuchsia::io::Node> file_request); + + // Used for executing blocking URLLoader routines. + const scoped_refptr<base::SequencedTaskRunner> task_runner_; + + mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_; + + DISALLOW_COPY_AND_ASSIGN(ContentDirectoryLoaderFactory); +}; + +#endif // FUCHSIA_ENGINE_BROWSER_CONTENT_DIRECTORY_LOADER_FACTORY_H_ diff --git a/chromium/fuchsia/engine/browser/context_impl.cc b/chromium/fuchsia/engine/browser/context_impl.cc index b87a396433a..b154c9739bd 100644 --- a/chromium/fuchsia/engine/browser/context_impl.cc +++ b/chromium/fuchsia/engine/browser/context_impl.cc @@ -4,21 +4,36 @@ #include "fuchsia/engine/browser/context_impl.h" -#include <lib/zx/object.h> +#include <lib/zx/channel.h> #include <memory> #include <utility> +#include "base/bind.h" #include "base/fuchsia/fuchsia_logging.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "fuchsia/engine/browser/frame_impl.h" -#include "fuchsia/engine/browser/web_engine_browser_context.h" #include "fuchsia/engine/common.h" ContextImpl::ContextImpl(content::BrowserContext* browser_context) - : browser_context_(browser_context) {} + : browser_context_(browser_context), + cookie_manager_(base::BindRepeating( + &content::StoragePartition::GetNetworkContext, + base::Unretained(content::BrowserContext::GetDefaultStoragePartition( + browser_context_)))) {} ContextImpl::~ContextImpl() = default; +fidl::InterfaceHandle<fuchsia::web::Frame> +ContextImpl::CreateFrameForPopupWebContents( + std::unique_ptr<content::WebContents> web_contents) { + fidl::InterfaceHandle<fuchsia::web::Frame> frame_handle; + frames_.insert(std::make_unique<FrameImpl>(std::move(web_contents), this, + frame_handle.NewRequest())); + return frame_handle; +} + void ContextImpl::DestroyFrame(FrameImpl* frame) { DCHECK(frames_.find(frame) != frames_.end()); frames_.erase(frames_.find(frame)); @@ -42,6 +57,11 @@ void ContextImpl::CreateFrame( std::move(frame))); } +void ContextImpl::GetCookieManager( + fidl::InterfaceRequest<fuchsia::web::CookieManager> request) { + cookie_manager_bindings_.AddBinding(&cookie_manager_, std::move(request)); +} + void ContextImpl::GetRemoteDebuggingPort( GetRemoteDebuggingPortCallback callback) { web_engine_remote_debugging_.GetRemoteDebuggingPort(std::move(callback)); diff --git a/chromium/fuchsia/engine/browser/context_impl.h b/chromium/fuchsia/engine/browser/context_impl.h index cd406106feb..6a53ae185ca 100644 --- a/chromium/fuchsia/engine/browser/context_impl.h +++ b/chromium/fuchsia/engine/browser/context_impl.h @@ -6,16 +6,19 @@ #define FUCHSIA_ENGINE_BROWSER_CONTEXT_IMPL_H_ #include <fuchsia/web/cpp/fidl.h> +#include <lib/fidl/cpp/binding_set.h> #include <memory> #include <set> #include "base/containers/unique_ptr_adapters.h" #include "base/macros.h" +#include "fuchsia/engine/browser/cookie_manager_impl.h" #include "fuchsia/engine/browser/web_engine_remote_debugging.h" #include "fuchsia/engine/web_engine_export.h" namespace content { class BrowserContext; +class WebContents; } // namespace content class FrameImpl; @@ -29,7 +32,7 @@ class WEB_ENGINE_EXPORT ContextImpl : public fuchsia::web::Context { explicit ContextImpl(content::BrowserContext* browser_context); // Tears down the Context, destroying any active Frames in the process. - ~ContextImpl() override; + ~ContextImpl() final; content::BrowserContext* browser_context_for_test() { return browser_context_; @@ -41,13 +44,19 @@ class WEB_ENGINE_EXPORT ContextImpl : public fuchsia::web::Context { // Returns |true| if JS injection was enabled for this Context. bool IsJavaScriptInjectionAllowed(); + // Registers a Frame originating from web content (i.e. a popup). + fidl::InterfaceHandle<fuchsia::web::Frame> CreateFrameForPopupWebContents( + std::unique_ptr<content::WebContents> web_contents); + // Called by Frames to signal a document has been loaded and signal to the // debug listeners in |web_engine_remote_debugging_| that they can now // successfully connect ChromeDriver. void OnDebugDevToolsPortReady(); // fuchsia::web::Context implementation. - void CreateFrame(fidl::InterfaceRequest<fuchsia::web::Frame> frame) override; + void CreateFrame(fidl::InterfaceRequest<fuchsia::web::Frame> frame) final; + void GetCookieManager( + fidl::InterfaceRequest<fuchsia::web::CookieManager> manager) final; void GetRemoteDebuggingPort(GetRemoteDebuggingPortCallback callback) override; // Gets the underlying FrameImpl service object associated with a connected @@ -57,6 +66,9 @@ class WEB_ENGINE_EXPORT ContextImpl : public fuchsia::web::Context { private: content::BrowserContext* const browser_context_; + CookieManagerImpl cookie_manager_; + fidl::BindingSet<fuchsia::web::CookieManager> cookie_manager_bindings_; + // TODO(crbug.com/893236): Make this false by default, and allow it to be // initialized at Context creation time. bool allow_javascript_injection_ = true; diff --git a/chromium/fuchsia/engine/browser/context_impl_browsertest.cc b/chromium/fuchsia/engine/browser/context_impl_browsertest.cc index d536330d572..ff6403870af 100644 --- a/chromium/fuchsia/engine/browser/context_impl_browsertest.cc +++ b/chromium/fuchsia/engine/browser/context_impl_browsertest.cc @@ -20,23 +20,11 @@ #include "fuchsia/engine/test/web_engine_browser_test.h" #include "services/network/public/mojom/cookie_manager.mojom.h" #include "services/network/public/mojom/network_context.mojom.h" -#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/url_constants.h" -using testing::_; -using testing::Field; -using testing::InvokeWithoutArgs; - namespace { -void OnCookiesReceived(net::CookieList* output, - base::OnceClosure on_received_cb, - const net::CookieList& cookies) { - *output = cookies; - std::move(on_received_cb).Run(); -} - // Defines a suite of tests that exercise browser-level configuration and // functionality. class ContextImplTest : public cr_fuchsia::WebEngineBrowserTest { @@ -50,19 +38,39 @@ class ContextImplTest : public cr_fuchsia::WebEngineBrowserTest { return WebEngineBrowserTest::CreateFrame(&navigation_listener_); } - // Synchronously gets a list of cookies for this BrowserContext. - net::CookieList GetCookies() { - base::RunLoop run_loop; - net::CookieList cookies; - network::mojom::CookieManagerPtr cookie_manager; - content::BrowserContext::GetDefaultStoragePartition( - context_impl()->browser_context_for_test()) - ->GetNetworkContext() - ->GetCookieManager(mojo::MakeRequest(&cookie_manager)); - cookie_manager->GetAllCookies(base::BindOnce(&OnCookiesReceived, - base::Unretained(&cookies), - run_loop.QuitClosure())); - run_loop.Run(); + // Synchronously gets the list of all cookies from the fuchsia.web.Context. + std::vector<fuchsia::web::Cookie> GetCookies() { + base::RunLoop get_cookies_loop; + + // Connect to the Context's CookieManager and request all the cookies. + fuchsia::web::CookieManagerPtr cookie_manager; + context()->GetCookieManager(cookie_manager.NewRequest()); + fuchsia::web::CookiesIteratorPtr cookies_iterator; + cookie_manager->GetCookieList(nullptr, nullptr, + cookies_iterator.NewRequest()); + + // |cookies_iterator| will disconnect once after the last cookies have been + // returned by GetNext(). + cookies_iterator.set_error_handler([&](zx_status_t status) { + EXPECT_EQ(ZX_ERR_PEER_CLOSED, status); + get_cookies_loop.Quit(); + }); + std::vector<fuchsia::web::Cookie> cookies; + + // std::function<> must be used here because fit::function<> is move-only + // and this callback will be used both for the initial GetNext() call, and + // for the follow-up calls made each time GetNext() results are received. + std::function<void(std::vector<fuchsia::web::Cookie>)> get_next_callback = + [&](std::vector<fuchsia::web::Cookie> new_cookies) { + cookies.insert(cookies.end(), + std::make_move_iterator(new_cookies.begin()), + std::make_move_iterator(new_cookies.end())); + cookies_iterator->GetNext(get_next_callback); + }; + cookies_iterator->GetNext(get_next_callback); + + get_cookies_loop.Run(); + return cookies; } @@ -74,44 +82,41 @@ class ContextImplTest : public cr_fuchsia::WebEngineBrowserTest { } // namespace -// Verifies that the BrowserContext has a working cookie store by setting -// cookies in the content layer and then querying the CookieStore afterward. -IN_PROC_BROWSER_TEST_F(ContextImplTest, VerifyPersistentCookieStore) { +// BrowserContext with persistent storage stores cookies such that they can +// be retrieved via the CookieManager API. +IN_PROC_BROWSER_TEST_F(ContextImplTest, PersistentCookieStore) { ASSERT_TRUE(embedded_test_server()->Start()); - GURL cookie_url(embedded_test_server()->GetURL("/set-cookie?foo=bar")); fuchsia::web::FramePtr frame = CreateFrame(); - fuchsia::web::NavigationControllerPtr navigation_controller; - frame->GetNavigationController(navigation_controller.NewRequest()); - - cr_fuchsia::LoadUrlAndExpectResponse(navigation_controller.get(), - fuchsia::web::LoadUrlParams(), - cookie_url.spec()); - navigation_listener_.RunUntilUrlEquals(cookie_url); - - auto cookies = GetCookies(); - bool found = false; - for (auto c : cookies) { - if (c.Name() == "foo" && c.Value() == "bar") { - found = true; - break; - } - } - EXPECT_TRUE(found); + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + const GURL kSetCookieUrl( + embedded_test_server()->GetURL("/set-cookie?foo=bar")); + cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kSetCookieUrl.spec()); + navigation_listener_.RunUntilUrlEquals(kSetCookieUrl); + + std::vector<fuchsia::web::Cookie> cookies = GetCookies(); + ASSERT_EQ(cookies.size(), 1u); + ASSERT_TRUE(cookies[0].has_id()); + ASSERT_TRUE(cookies[0].id().has_name()); + ASSERT_TRUE(cookies[0].has_value()); + EXPECT_EQ(cookies[0].id().name(), "foo"); + EXPECT_EQ(cookies[0].value(), "bar"); // Check that the cookie persists beyond the lifetime of the Frame by // releasing the Frame and re-querying the CookieStore. frame.Unbind(); base::RunLoop().RunUntilIdle(); - found = false; - for (auto c : cookies) { - if (c.Name() == "foo" && c.Value() == "bar") { - found = true; - break; - } - } - EXPECT_TRUE(found); + cookies = GetCookies(); + ASSERT_EQ(cookies.size(), 1u); + ASSERT_TRUE(cookies[0].has_id()); + ASSERT_TRUE(cookies[0].id().has_name()); + ASSERT_TRUE(cookies[0].has_value()); + EXPECT_EQ(cookies[0].id().name(), "foo"); + EXPECT_EQ(cookies[0].value(), "bar"); } // Suite for tests which run the BrowserContext in incognito mode (no data @@ -145,25 +150,25 @@ IN_PROC_BROWSER_TEST_F(IncognitoContextImplTest, NavigateFrame) { frame.Unbind(); } -IN_PROC_BROWSER_TEST_F(IncognitoContextImplTest, VerifyInMemoryCookieStore) { +// In-memory cookie store stores cookies, and is accessible via CookieManager. +IN_PROC_BROWSER_TEST_F(IncognitoContextImplTest, InMemoryCookieStore) { ASSERT_TRUE(embedded_test_server()->Start()); - GURL cookie_url(embedded_test_server()->GetURL("/set-cookie?foo=bar")); fuchsia::web::FramePtr frame = CreateFrame(); fuchsia::web::NavigationControllerPtr controller; frame->GetNavigationController(controller.NewRequest()); - EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( - controller.get(), fuchsia::web::LoadUrlParams(), cookie_url.spec())); - navigation_listener_.RunUntilUrlEquals(cookie_url); - - auto cookies = GetCookies(); - bool found = false; - for (auto c : cookies) { - if (c.Name() == "foo" && c.Value() == "bar") { - found = true; - break; - } - } - EXPECT_TRUE(found); + const GURL kSetCookieUrl( + embedded_test_server()->GetURL("/set-cookie?foo=bar")); + cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kSetCookieUrl.spec()); + navigation_listener_.RunUntilUrlEquals(kSetCookieUrl); + + std::vector<fuchsia::web::Cookie> cookies = GetCookies(); + ASSERT_EQ(cookies.size(), 1u); + ASSERT_TRUE(cookies[0].has_id()); + ASSERT_TRUE(cookies[0].id().has_name()); + ASSERT_TRUE(cookies[0].has_value()); + EXPECT_EQ(cookies[0].id().name(), "foo"); + EXPECT_EQ(cookies[0].value(), "bar"); } diff --git a/chromium/fuchsia/engine/browser/cookie_manager_impl.cc b/chromium/fuchsia/engine/browser/cookie_manager_impl.cc new file mode 100644 index 00000000000..ad875621fc5 --- /dev/null +++ b/chromium/fuchsia/engine/browser/cookie_manager_impl.cc @@ -0,0 +1,237 @@ +// Copyright 2019 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 "fuchsia/engine/browser/cookie_manager_impl.h" + +#include <lib/fidl/cpp/binding.h> + +#include "base/fuchsia/fuchsia_logging.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "net/cookies/canonical_cookie.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "url/gurl.h" + +namespace { + +fuchsia::web::Cookie ConvertCanonicalCookie( + const net::CanonicalCookie& canonical_cookie, + network::mojom::CookieChangeCause cause) { + fuchsia::web::CookieId id; + id.set_name(canonical_cookie.Name()); + id.set_domain(canonical_cookie.Domain()); + id.set_path(canonical_cookie.Path()); + + fuchsia::web::Cookie cookie; + cookie.set_id(std::move(id)); + switch (cause) { + case network::mojom::CookieChangeCause::INSERTED: + cookie.set_value(canonical_cookie.Value()); + break; + case network::mojom::CookieChangeCause::EXPLICIT: + case network::mojom::CookieChangeCause::UNKNOWN_DELETION: + case network::mojom::CookieChangeCause::OVERWRITE: + case network::mojom::CookieChangeCause::EXPIRED: + case network::mojom::CookieChangeCause::EVICTED: + case network::mojom::CookieChangeCause::EXPIRED_OVERWRITE: + break; + }; + + return cookie; +} + +class CookiesIteratorImpl : public fuchsia::web::CookiesIterator, + public network::mojom::CookieChangeListener { + public: + // |this| will delete itself when |mojo_request| or |changes| disconnect. + CookiesIteratorImpl( + mojo::PendingReceiver<network::mojom::CookieChangeListener> mojo_receiver, + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> changes) + : CookiesIteratorImpl(std::move(changes)) { + mojo_receiver_.Bind(std::move(mojo_receiver)); + mojo_receiver_.set_disconnect_handler(base::BindOnce( + &CookiesIteratorImpl::OnMojoError, base::Unretained(this))); + } + // |this| will delete itself when |iterator| disconnects, or if a GetNext() + // leaves |queued_cookies_| empty. + CookiesIteratorImpl( + const std::vector<net::CanonicalCookie>& cookies, + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator) + : CookiesIteratorImpl(std::move(iterator)) { + for (const auto& cookie : cookies) { + queued_cookies_[cookie.UniqueKey()] = ConvertCanonicalCookie( + cookie, network::mojom::CookieChangeCause::INSERTED); + } + } + // Same as above except it takes CookieStatusList instead of just CookieList. + CookiesIteratorImpl( + const std::vector<net::CookieWithStatus>& cookies_with_statuses, + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator) + : CookiesIteratorImpl(std::move(iterator)) { + for (const auto& cookie_with_status : cookies_with_statuses) { + queued_cookies_[cookie_with_status.cookie.UniqueKey()] = + ConvertCanonicalCookie(cookie_with_status.cookie, + network::mojom::CookieChangeCause::INSERTED); + } + } + ~CookiesIteratorImpl() final = default; + + // fuchsia::web::CookiesIterator implementation: + void GetNext(GetNextCallback callback) final { + DCHECK(!get_next_callback_); + get_next_callback_ = std::move(callback); + MaybeSendQueuedCookies(); + } + + private: + explicit CookiesIteratorImpl( + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator) + : mojo_receiver_(this), fidl_binding_(this) { + fidl_binding_.Bind(std::move(iterator)); + fidl_binding_.set_error_handler([this](zx_status_t status) { + ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status) + << "CookieChangeListener disconnected."; + delete this; + }); + } + + void OnMojoError() { + LOG(ERROR) << "NetworkService disconnected CookiesIterator."; + fidl_binding_.Close(ZX_ERR_UNAVAILABLE); + delete this; + } + + void MaybeSendQueuedCookies() { + // Assuming cookies values never exceed 4KB in size, plus some overhead for + // the name, domain and path, and that Zircon messages can be up to 64KB. + constexpr int kMaxCookiesPerMessage = 8; + + if (!get_next_callback_) + return; + if (mojo_receiver_.is_bound() && queued_cookies_.empty()) + return; + + // Build a vector of Cookies to return to the caller. + fuchsia::web::CookiesIterator::GetNextCallback callback( + std::move(get_next_callback_)); + std::vector<fuchsia::web::Cookie> cookies; + while (!queued_cookies_.empty() && cookies.size() < kMaxCookiesPerMessage) { + auto cookie = queued_cookies_.begin(); + cookies.emplace_back(std::move(cookie->second)); + queued_cookies_.erase(cookie); + } + callback(std::move(cookies)); + + // If this is a one-off CookieIterator then tear down once |queued_cookies_| + // is empty. + if (queued_cookies_.empty() && !mojo_receiver_.is_bound()) + delete this; + } + + // network::mojom::CookieChangeListener implementation: + void OnCookieChange(const net::CanonicalCookie& cookie, + network::mojom::CookieChangeCause cause) final { + queued_cookies_[cookie.UniqueKey()] = ConvertCanonicalCookie(cookie, cause); + MaybeSendQueuedCookies(); + } + + mojo::Receiver<network::mojom::CookieChangeListener> mojo_receiver_; + fidl::Binding<fuchsia::web::CookiesIterator> fidl_binding_; + + GetNextCallback get_next_callback_; + + // Map from "unique key"s (see net::CanonicalCookie::UniqueKey()) to the + // corresponding fuchsia::web::Cookie. + std::map<std::tuple<std::string, std::string, std::string>, + fuchsia::web::Cookie> + queued_cookies_; + + DISALLOW_COPY_AND_ASSIGN(CookiesIteratorImpl); +}; + +void OnAllCookiesReceived( + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator, + const std::vector<net::CanonicalCookie>& cookies) { + new CookiesIteratorImpl(cookies, std::move(iterator)); +} + +void OnCookiesAndExcludedReceived( + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator, + const std::vector<net::CookieWithStatus>& cookies_with_statuses, + const std::vector<net::CookieWithStatus>& excluded_cookies) { + // Since CookieOptions::set_return_excluded_cookies() is not used when calling + // the Mojo GetCookieList() API, |excluded_cookies| should be empty. + DCHECK(excluded_cookies.empty()); + new CookiesIteratorImpl(cookies_with_statuses, std::move(iterator)); +} + +} // namespace + +CookieManagerImpl::CookieManagerImpl( + GetNetworkContextCallback get_network_context) + : get_network_context_(std::move(get_network_context)) {} + +CookieManagerImpl::~CookieManagerImpl() = default; + +void CookieManagerImpl::ObserveCookieChanges( + fidl::StringPtr url, + fidl::StringPtr name, + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> changes) { + EnsureCookieManager(); + + mojo::PendingRemote<network::mojom::CookieChangeListener> mojo_listener; + new CookiesIteratorImpl(mojo_listener.InitWithNewPipeAndPassReceiver(), + std::move(changes)); + + if (url) { + base::Optional<std::string> maybe_name; + if (name) + maybe_name = *name; + cookie_manager_->AddCookieChangeListener(GURL(*url), maybe_name, + std::move(mojo_listener)); + } else { + cookie_manager_->AddGlobalChangeListener(std::move(mojo_listener)); + } +} + +void CookieManagerImpl::GetCookieList( + fidl::StringPtr url, + fidl::StringPtr name, + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> iterator) { + EnsureCookieManager(); + + if (!url && !name) { + cookie_manager_->GetAllCookies( + base::BindOnce(&OnAllCookiesReceived, std::move(iterator))); + } else { + if (!name) { + // Include HTTP and 1st-party-only cookies in those returned. + net::CookieOptions options; + options.set_include_httponly(); + options.set_same_site_cookie_context( + net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT); + + cookie_manager_->GetCookieList( + GURL(*url), options, + base::BindOnce(&OnCookiesAndExcludedReceived, std::move(iterator))); + } else { + // TODO(858853): Support filtering by name. + iterator.Close(ZX_ERR_NOT_SUPPORTED); + } + } +} + +void CookieManagerImpl::EnsureCookieManager() { + if (cookie_manager_.is_bound()) + return; + get_network_context_.Run()->GetCookieManager( + cookie_manager_.BindNewPipeAndPassReceiver()); + cookie_manager_.set_disconnect_handler(base::BindOnce( + &CookieManagerImpl::OnMojoDisconnect, base::Unretained(this))); +} + +void CookieManagerImpl::OnMojoDisconnect() { + LOG(ERROR) << "NetworkService disconnected CookieManager."; + cookie_manager_.reset(); +} diff --git a/chromium/fuchsia/engine/browser/cookie_manager_impl.h b/chromium/fuchsia/engine/browser/cookie_manager_impl.h new file mode 100644 index 00000000000..dac1098fb9b --- /dev/null +++ b/chromium/fuchsia/engine/browser/cookie_manager_impl.h @@ -0,0 +1,58 @@ +// Copyright 2019 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 FUCHSIA_ENGINE_BROWSER_COOKIE_MANAGER_IMPL_H_ +#define FUCHSIA_ENGINE_BROWSER_COOKIE_MANAGER_IMPL_H_ + +#include <fuchsia/web/cpp/fidl.h> + +#include "base/callback.h" +#include "base/macros.h" +#include "fuchsia/engine/web_engine_export.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" + +namespace network { +namespace mojom { +class NetworkContext; +} // namespace mojom +} // namespace network + +class WEB_ENGINE_EXPORT CookieManagerImpl : public fuchsia::web::CookieManager { + public: + // Used to request the BrowserContext's CookieManager. Since the + // NetworkContext may change, e.g. if the NetworkService crashes, the returned + // pointer will be used immediately, and not cached by the CookieManagerImpl. + using GetNetworkContextCallback = + base::RepeatingCallback<network::mojom::NetworkContext*()>; + + // |get_network_context| will be called to (re)connect to CookieManager, + // on-demand, in response to query/observation requests. + explicit CookieManagerImpl(GetNetworkContextCallback get_network_context); + ~CookieManagerImpl() final; + + // fuchsia::web::CookieManager implementation: + void ObserveCookieChanges( + fidl::StringPtr url, + fidl::StringPtr name, + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> changes) final; + void GetCookieList( + fidl::StringPtr url, + fidl::StringPtr name, + fidl::InterfaceRequest<fuchsia::web::CookiesIterator> cookies) final; + + private: + // (Re)connects |cookie_manager_| if not currently connected. + void EnsureCookieManager(); + + // Handles errors on the |cookie_manager_| Mojo channel. + void OnMojoDisconnect(); + + const GetNetworkContextCallback get_network_context_; + mojo::Remote<network::mojom::CookieManager> cookie_manager_; + + DISALLOW_COPY_AND_ASSIGN(CookieManagerImpl); +}; + +#endif // FUCHSIA_ENGINE_BROWSER_COOKIE_MANAGER_IMPL_H_ diff --git a/chromium/fuchsia/engine/browser/cookie_manager_impl_unittest.cc b/chromium/fuchsia/engine/browser/cookie_manager_impl_unittest.cc new file mode 100644 index 00000000000..1794c0f3a12 --- /dev/null +++ b/chromium/fuchsia/engine/browser/cookie_manager_impl_unittest.cc @@ -0,0 +1,377 @@ +// Copyright 2019 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 <lib/fidl/cpp/binding.h> + +#include <map> +#include <vector> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/optional.h" +#include "base/run_loop.h" +#include "base/test/bind_test_util.h" +#include "base/test/task_environment.h" +#include "fuchsia/base/fit_adapter.h" +#include "fuchsia/base/result_receiver.h" +#include "fuchsia/engine/browser/cookie_manager_impl.h" +#include "services/network/network_service.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kTestCookieUrl[] = "https://www.testing.com/"; +const char kTestOtherUrl[] = "https://www.other.com/"; +const char kCookieName1[] = "Cookie"; +const char kCookieName2[] = "Monster"; +const char kCookieValue1[] = "Eats"; +const char kCookieValue2[] = "Cookies"; +const char kCookieValue3[] = "Nyom nyom nyom"; + +// Creates a CanonicalCookie with |name| and |value|, for kTestCookieUrlHost. +std::unique_ptr<net::CanonicalCookie> CreateCookie(base::StringPiece name, + base::StringPiece value) { + return net::CanonicalCookie::CreateSanitizedCookie( + GURL(kTestCookieUrl), name.as_string(), value.as_string(), /*domain=*/"", + /*path=*/"", /*creation_time=*/base::Time(), + /*expiration_time=*/base::Time(), /*last_access_time=*/base::Time(), + /*secure=*/false, + /*httponly*/ false, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_MEDIUM); +} + +class CookieManagerImplTest : public testing::Test { + public: + CookieManagerImplTest() + : task_environment_( + base::test::TaskEnvironment::MainThreadType::IO), + network_service_(network::NetworkService::CreateForTesting()), + cookie_manager_( + base::BindRepeating(&CookieManagerImplTest::GetNetworkContext, + base::Unretained(this))) {} + ~CookieManagerImplTest() override = default; + + protected: + network::mojom::NetworkContext* GetNetworkContext() { + if (!network_context_.is_bound()) { + network_service_->CreateNetworkContext( + mojo::MakeRequest(&network_context_), + network::mojom::NetworkContextParams::New()); + network_context_.set_connection_error_handler( + base::BindLambdaForTesting([&]() { network_context_.reset(); })); + } + return network_context_.get(); + } + + // Adds the specified cookie under kTestCookieUrlHost. + void CreateAndSetCookieAsync(base::StringPiece name, + base::StringPiece value) { + EnsureMojoCookieManager(); + + net::CookieOptions options; + mojo_cookie_manager_->SetCanonicalCookie( + *CreateCookie(name, value), "https", options, + base::Bind([](net::CanonicalCookie::CookieInclusionStatus status) { + EXPECT_TRUE(status.IsInclude()); + })); + } + + // Removes the specified cookie from under kTestCookieUrlHost. + void DeleteCookieAsync(base::StringPiece name, base::StringPiece value) { + EnsureMojoCookieManager(); + + mojo_cookie_manager_->DeleteCanonicalCookie( + *CreateCookie(name, value), + base::Bind([](bool success) { EXPECT_TRUE(success); })); + } + + // Synchronously fetches all cookies via the |cookie_manager_|. + // Returns a base::nullopt if the iterator closes before a GetNext() returns. + base::Optional<std::vector<fuchsia::web::Cookie>> GetAllCookies() { + base::RunLoop get_cookies_loop; + fuchsia::web::CookiesIteratorPtr cookies_iterator; + cookies_iterator.set_error_handler([&](zx_status_t status) { + EXPECT_EQ(ZX_ERR_PEER_CLOSED, status); + get_cookies_loop.Quit(); + }); + cookie_manager_.GetCookieList(nullptr, nullptr, + cookies_iterator.NewRequest()); + base::Optional<std::vector<fuchsia::web::Cookie>> cookies; + std::function<void(std::vector<fuchsia::web::Cookie>)> get_next_callback = + [&](std::vector<fuchsia::web::Cookie> new_cookies) { + if (!cookies.has_value()) { + cookies.emplace(std::move(new_cookies)); + } else { + cookies->insert(cookies->end(), + std::make_move_iterator(new_cookies.begin()), + std::make_move_iterator(new_cookies.end())); + } + cookies_iterator->GetNext(get_next_callback); + }; + cookies_iterator->GetNext(get_next_callback); + get_cookies_loop.Run(); + return cookies; + } + + void EnsureMojoCookieManager() { + if (mojo_cookie_manager_.is_bound()) + return; + network_context_->GetCookieManager( + mojo_cookie_manager_.BindNewPipeAndPassReceiver()); + } + + base::test::TaskEnvironment task_environment_; + + std::unique_ptr<network::NetworkService> network_service_; + network::mojom::NetworkContextPtr network_context_; + mojo::Remote<network::mojom::CookieManager> mojo_cookie_manager_; + + CookieManagerImpl cookie_manager_; + + private: + DISALLOW_COPY_AND_ASSIGN(CookieManagerImplTest); +}; + +// Calls GetNext() on the supplied |iterator| and lets the caller express +// expectations on the results. +class GetNextCookiesIteratorResult { + public: + explicit GetNextCookiesIteratorResult(fuchsia::web::CookiesIterator* iterator) + : result_(loop_.QuitClosure()) { + iterator->GetNext( + cr_fuchsia::CallbackToFitFunction(result_.GetReceiveCallback())); + } + + ~GetNextCookiesIteratorResult() = default; + + void ExpectSingleCookie(base::StringPiece name, + base::Optional<base::StringPiece> value) { + ExpectCookieUpdates({{name, value}}); + } + + void ExpectDeleteSingleCookie(base::StringPiece name) { + ExpectCookieUpdates({{name, base::nullopt}}); + } + + // Specifies the cookie name/value pairs expected in the GetNext() results. + // Deletions expectations are specified by using base::nullopt as the value. + void ExpectCookieUpdates( + std::map<base::StringPiece, base::Optional<base::StringPiece>> expected) { + loop_.Run(); + ASSERT_TRUE(result_.has_value()); + ASSERT_EQ(result_->size(), expected.size()); + std::map<base::StringPiece, base::StringPiece> result_updates; + for (auto& cookie_update : *result_) { + ASSERT_TRUE(cookie_update.has_id()); + ASSERT_TRUE(cookie_update.id().has_name()); + auto it = expected.find(cookie_update.id().name()); + ASSERT_TRUE(it != expected.end()); + ASSERT_EQ(cookie_update.has_value(), it->second.has_value()); + if (it->second.has_value()) + EXPECT_EQ(*it->second, cookie_update.value()); + expected.erase(it); + } + EXPECT_TRUE(expected.empty()); + } + + void ExpectReceivedNoUpdates() { + // If we ran |loop_| then this would hang, so just ensure any pending work + // has been processed. + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(result_.has_value()); + } + + protected: + base::RunLoop loop_; + cr_fuchsia::ResultReceiver<std::vector<fuchsia::web::Cookie>> result_; + + DISALLOW_COPY_AND_ASSIGN(GetNextCookiesIteratorResult); +}; + +} // namespace + +TEST_F(CookieManagerImplTest, GetAndObserveAddModifyDelete) { + // Add global, URL-filtered and URL+name-filtered observers. + fuchsia::web::CookiesIteratorPtr global_changes; + global_changes.set_error_handler([](zx_status_t) { ADD_FAILURE(); }); + cookie_manager_.ObserveCookieChanges(nullptr, nullptr, + global_changes.NewRequest()); + + fuchsia::web::CookiesIteratorPtr url_changes; + url_changes.set_error_handler([](zx_status_t) { ADD_FAILURE(); }); + cookie_manager_.ObserveCookieChanges(kTestCookieUrl, nullptr, + url_changes.NewRequest()); + + fuchsia::web::CookiesIteratorPtr name_changes; + name_changes.set_error_handler([](zx_status_t) { ADD_FAILURE(); }); + cookie_manager_.ObserveCookieChanges(kTestCookieUrl, kCookieName1, + name_changes.NewRequest()); + + // Register interest in updates for another URL, so we can verify none are + // received. + fuchsia::web::CookiesIteratorPtr other_changes; + name_changes.set_error_handler([](zx_status_t) { ADD_FAILURE(); }); + cookie_manager_.ObserveCookieChanges(kTestOtherUrl, nullptr, + other_changes.NewRequest()); + GetNextCookiesIteratorResult other_updates(other_changes.get()); + + // Ensure that all ObserveCookieChanges() were processed before modifying + // cookies. + EXPECT_EQ(GetAllCookies()->size(), 0u); + + // Set cookie kCookieName1, which should trigger notifications to all + // observers. + { + GetNextCookiesIteratorResult global_update(global_changes.get()); + GetNextCookiesIteratorResult url_update(url_changes.get()); + GetNextCookiesIteratorResult name_update(name_changes.get()); + + CreateAndSetCookieAsync(kCookieName1, kCookieValue1); + + global_update.ExpectSingleCookie(kCookieName1, kCookieValue1); + url_update.ExpectSingleCookie(kCookieName1, kCookieValue1); + name_update.ExpectSingleCookie(kCookieName1, kCookieValue1); + } + + // Expect an add notification for kCookieName2, except on the with-name + // observer. If the with-name observer does get notified then the remove & + // re-add check below will observe kCookieName2 rather than kCookieName1, and + // fail. + { + GetNextCookiesIteratorResult global_update(global_changes.get()); + GetNextCookiesIteratorResult url_update(url_changes.get()); + + CreateAndSetCookieAsync(kCookieName2, kCookieValue2); + + global_update.ExpectSingleCookie(kCookieName2, kCookieValue2); + url_update.ExpectSingleCookie(kCookieName2, kCookieValue2); + } + + // Set kCookieName1 to a new value, which will trigger deletion notifications, + // followed by an addition with the new value. + { + GetNextCookiesIteratorResult global_update(global_changes.get()); + GetNextCookiesIteratorResult url_update(url_changes.get()); + GetNextCookiesIteratorResult name_update(name_changes.get()); + + // Updating the cookie will generate a deletion, following by an insertion. + // CookiesIterator will batch updates into a single response, so we may get + // two separate updates, or a single update, depending on timing. Eliminate + // the non-determinism by ensuring that the GetNext() calls have been + // received before updating the cookie. + base::RunLoop().RunUntilIdle(); + + CreateAndSetCookieAsync(kCookieName1, kCookieValue3); + + global_update.ExpectDeleteSingleCookie(kCookieName1); + url_update.ExpectDeleteSingleCookie(kCookieName1); + name_update.ExpectDeleteSingleCookie(kCookieName1); + } + { + GetNextCookiesIteratorResult global_update(global_changes.get()); + GetNextCookiesIteratorResult url_update(url_changes.get()); + GetNextCookiesIteratorResult name_update(name_changes.get()); + + global_update.ExpectSingleCookie(kCookieName1, kCookieValue3); + url_update.ExpectSingleCookie(kCookieName1, kCookieValue3); + name_update.ExpectSingleCookie(kCookieName1, kCookieValue3); + } + + // Set kCookieName2 empty, which will notify only the global and URL + // observers. If the name observer is mis-notified then the next step, below, + // will fail. + { + GetNextCookiesIteratorResult global_update(global_changes.get()); + GetNextCookiesIteratorResult url_update(url_changes.get()); + + DeleteCookieAsync(kCookieName2, kCookieValue2); + + global_update.ExpectDeleteSingleCookie(kCookieName2); + url_update.ExpectDeleteSingleCookie(kCookieName2); + } + + // Set kCookieName1 empty, which will notify all the observers that it was + // removed. + { + GetNextCookiesIteratorResult global_update(global_changes.get()); + GetNextCookiesIteratorResult url_update(url_changes.get()); + GetNextCookiesIteratorResult name_update(name_changes.get()); + + DeleteCookieAsync(kCookieName1, kCookieValue3); + + global_update.ExpectDeleteSingleCookie(kCookieName1); + url_update.ExpectDeleteSingleCookie(kCookieName1); + name_update.ExpectDeleteSingleCookie(kCookieName1); + } + + // Verify that no updates were received for the "other" URL (since we did not + // set any cookies for it). It is possible that this could pass due to the + // CookiesIterator not having been scheduled, but that is very unlikely. + other_updates.ExpectReceivedNoUpdates(); +} + +TEST_F(CookieManagerImplTest, UpdateBatching) { + fuchsia::web::CookiesIteratorPtr global_changes; + global_changes.set_error_handler([](zx_status_t) { ADD_FAILURE(); }); + cookie_manager_.ObserveCookieChanges(nullptr, nullptr, + global_changes.NewRequest()); + + // Ensure that all ObserveCookieChanges() were processed before modifying + // cookies. + EXPECT_EQ(GetAllCookies()->size(), 0u); + + { + // Verify that some insertions are batched into a single GetNext() result. + CreateAndSetCookieAsync(kCookieName1, kCookieValue1); + CreateAndSetCookieAsync(kCookieName2, kCookieValue2); + CreateAndSetCookieAsync(kCookieName1, kCookieValue3); + mojo_cookie_manager_.FlushForTesting(); + + GetNextCookiesIteratorResult global_updates(global_changes.get()); + global_updates.ExpectCookieUpdates( + {{kCookieName1, kCookieValue3}, {kCookieName2, kCookieValue2}}); + } + + { + // Verify that some deletions are batched into a single GetNext() result. + DeleteCookieAsync(kCookieName2, kCookieValue2); + DeleteCookieAsync(kCookieName1, kCookieValue3); + mojo_cookie_manager_.FlushForTesting(); + + GetNextCookiesIteratorResult global_updates(global_changes.get()); + global_updates.ExpectCookieUpdates( + {{kCookieName1, base::nullopt}, {kCookieName2, base::nullopt}}); + } +} + +TEST_F(CookieManagerImplTest, ReconnectToNetworkContext) { + // Attach a cookie observer, which we expect should become disconnected with + // an appropriate error if the NetworkService goes away. + base::RunLoop global_changes_disconnect_loop; + fuchsia::web::CookiesIteratorPtr global_changes; + global_changes.set_error_handler([&](zx_status_t status) { + EXPECT_EQ(ZX_ERR_UNAVAILABLE, status); + global_changes_disconnect_loop.Quit(); + }); + cookie_manager_.ObserveCookieChanges(nullptr, nullptr, + global_changes.NewRequest()); + + // Verify that GetAllCookies() returns a valid list of cookies (as opposed to + // not returning a list at all) initially. + EXPECT_TRUE(GetAllCookies().has_value()); + + // Tear-down and re-create the NetworkService, causing the CookieManager's + // connection to it to be dropped. + network_service_.reset(); + network_service_ = network::NetworkService::CreateForTesting(); + + // Expect that |global_changes| is disconnected at this point. + global_changes_disconnect_loop.Run(); + + // If the CookieManager fails to re-connect then GetAllCookies() will receive + // no data (as opposed to receiving an empty list of cookies). + EXPECT_TRUE(GetAllCookies().has_value()); +} diff --git a/chromium/fuchsia/engine/browser/frame_impl.cc b/chromium/fuchsia/engine/browser/frame_impl.cc index 0793e4dcf77..7a871690cb9 100644 --- a/chromium/fuchsia/engine/browser/frame_impl.cc +++ b/chromium/fuchsia/engine/browser/frame_impl.cc @@ -4,6 +4,7 @@ #include "fuchsia/engine/browser/frame_impl.h" +#include <lib/ui/scenic/cpp/view_ref_pair.h> #include <limits> #include "base/bind_helpers.h" @@ -11,14 +12,16 @@ #include "base/json/json_writer.h" #include "base/strings/strcat.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/message_port_provider.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/renderer_preferences_util.h" #include "content/public/browser/web_contents.h" -#include "content/public/common/was_activated_option.h" +#include "content/public/common/was_activated_option.mojom.h" #include "fuchsia/base/mem_buffer_util.h" #include "fuchsia/base/message_port.h" #include "fuchsia/engine/browser/context_impl.h" @@ -40,6 +43,13 @@ namespace { const logging::LogSeverity kLogSeverityNone = std::numeric_limits<logging::LogSeverity>::min(); +// Used for attaching popup-related metadata to a WebContents. +constexpr char kPopupCreationInfo[] = "popup-creation-info"; +class PopupFrameCreationInfoUserData : public base::SupportsUserData::Data { + public: + fuchsia::web::PopupFrameCreationInfo info; +}; + // Layout manager that allows only one child window and stretches it to fill the // parent. class LayoutManagerImpl : public aura::LayoutManager { @@ -266,8 +276,8 @@ void FrameImpl::ExecuteJavaScriptInternal(std::vector<std::string> origins, } fuchsia::web::Frame_ExecuteJavaScript_Response response; - response.result = - cr_fuchsia::MemBufferFromString(std::move(result_json)); + response.result = cr_fuchsia::MemBufferFromString( + std::move(result_json), "cr-execute-js-response"); result.set_response(std::move(response)); callback(std::move(result)); }, @@ -285,12 +295,130 @@ void FrameImpl::ExecuteJavaScriptInternal(std::vector<std::string> origins, } } +bool FrameImpl::ShouldCreateWebContents( + content::WebContents* web_contents, + content::RenderFrameHost* opener, + content::SiteInstance* source_site_instance, + int32_t route_id, + int32_t main_frame_route_id, + int32_t main_frame_widget_route_id, + content::mojom::WindowContainerType window_container_type, + const GURL& opener_url, + const std::string& frame_name, + const GURL& target_url, + const std::string& partition_id, + content::SessionStorageNamespace* session_storage_namespace) { + // Specify a generous upper bound for unacknowledged popup windows, so that we + // can catch bad client behavior while not interfering with normal operation. + constexpr size_t kMaxPendingWebContentsCount = 10; + + DCHECK_EQ(web_contents, web_contents_.get()); + + if (!popup_listener_) + return false; + + if (pending_popups_.size() >= kMaxPendingWebContentsCount) { + // The content is producing popups faster than the embedder can process + // them. Drop the popups so as to prevent resource exhaustion. + LOG(WARNING) << "Too many pending popups, ignoring request."; + + // Don't produce a WebContents for this popup. + return false; + } + + return true; +} + +void FrameImpl::AddNewContents( + content::WebContents* source, + std::unique_ptr<content::WebContents> new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture, + bool* was_blocked) { + DCHECK_EQ(source, web_contents_.get()); + + // TODO(crbug.com/995395): Add window disposition to the FIDL interface. + switch (disposition) { + case WindowOpenDisposition::NEW_FOREGROUND_TAB: + case WindowOpenDisposition::NEW_BACKGROUND_TAB: + case WindowOpenDisposition::NEW_POPUP: + case WindowOpenDisposition::NEW_WINDOW: { + PopupFrameCreationInfoUserData* popup_creation_info = + reinterpret_cast<PopupFrameCreationInfoUserData*>( + new_contents->GetUserData(kPopupCreationInfo)); + popup_creation_info->info.set_initiated_by_user(user_gesture); + pending_popups_.emplace_back(std::move(new_contents)); + MaybeSendPopup(); + return; + } + + // These kinds of windows don't produce Frames. + case WindowOpenDisposition::CURRENT_TAB: + case WindowOpenDisposition::SINGLETON_TAB: + case WindowOpenDisposition::SAVE_TO_DISK: + case WindowOpenDisposition::OFF_THE_RECORD: + case WindowOpenDisposition::IGNORE_ACTION: + case WindowOpenDisposition::SWITCH_TO_TAB: + case WindowOpenDisposition::UNKNOWN: + NOTIMPLEMENTED() << "Dropped new web contents (disposition: " + << static_cast<int>(disposition) << ")"; + return; + } +} + +void FrameImpl::WebContentsCreated(content::WebContents* source_contents, + int opener_render_process_id, + int opener_render_frame_id, + const std::string& frame_name, + const GURL& target_url, + content::WebContents* new_contents) { + auto creation_info = std::make_unique<PopupFrameCreationInfoUserData>(); + creation_info->info.set_initial_url(target_url.spec()); + new_contents->SetUserData(kPopupCreationInfo, std::move(creation_info)); +} + +void FrameImpl::MaybeSendPopup() { + if (!popup_listener_) + return; + + if (popup_ack_outstanding_ || pending_popups_.empty()) + return; + + std::unique_ptr<content::WebContents> popup = + std::move(pending_popups_.front()); + pending_popups_.pop_front(); + + fuchsia::web::PopupFrameCreationInfo creation_info = + std::move(reinterpret_cast<PopupFrameCreationInfoUserData*>( + popup->GetUserData(kPopupCreationInfo)) + ->info); + + // The PopupFrameCreationInfo won't be needed anymore, so clear it out. + popup->SetUserData(kPopupCreationInfo, nullptr); + + popup_listener_->OnPopupFrameCreated( + context_->CreateFrameForPopupWebContents(std::move(popup)), + std::move(creation_info), [this] { + popup_ack_outstanding_ = false; + MaybeSendPopup(); + }); + popup_ack_outstanding_ = true; +} + +void FrameImpl::OnPopupListenerDisconnected(zx_status_t status) { + ZX_LOG_IF(WARNING, status != ZX_ERR_PEER_CLOSED, status) + << "Popup listener disconnected."; + pending_popups_.clear(); +} + void FrameImpl::CreateView(fuchsia::ui::views::ViewToken view_token) { // If a View to this Frame is already active then disconnect it. TearDownView(); ui::PlatformWindowInitProperties properties; properties.view_token = std::move(view_token); + properties.view_ref_pair = scenic::ViewRefPair::New(); window_tree_host_ = std::make_unique<ScenicWindowTreeHost>(std::move(properties)); @@ -372,7 +500,7 @@ void FrameImpl::AddBeforeLoadJavaScript( // Create a read-only VMO from |script|. fuchsia::mem::Buffer script_buffer = - cr_fuchsia::MemBufferFromString16(script_utf16); + cr_fuchsia::MemBufferFromString16(script_utf16, "cr-before-load-js"); // Wrap the VMO into a read-only shared-memory container that Mojo can work // with. @@ -485,6 +613,13 @@ void FrameImpl::SetEnableInput(bool enable_input) { discarding_event_filter_.set_discard_events(!enable_input); } +void FrameImpl::SetPopupFrameCreationListener( + fidl::InterfaceHandle<fuchsia::web::PopupFrameCreationListener> listener) { + popup_listener_ = listener.Bind(); + popup_listener_.set_error_handler( + fit::bind_member(this, &FrameImpl::OnPopupListenerDisconnected)); +} + void FrameImpl::CloseContents(content::WebContents* source) { DCHECK_EQ(source, web_contents_.get()); context_->DestroyFrame(this); @@ -529,29 +664,6 @@ bool FrameImpl::DidAddMessageToConsole( return true; } -bool FrameImpl::ShouldCreateWebContents( - content::WebContents* web_contents, - content::RenderFrameHost* opener, - content::SiteInstance* source_site_instance, - int32_t route_id, - int32_t main_frame_route_id, - int32_t main_frame_widget_route_id, - content::mojom::WindowContainerType window_container_type, - const GURL& opener_url, - const std::string& frame_name, - const GURL& target_url, - const std::string& partition_id, - content::SessionStorageNamespace* session_storage_namespace) { - DCHECK_EQ(web_contents, web_contents_.get()); - - // Prevent any child WebContents (popup windows, tabs, etc.) from spawning. - // TODO(crbug.com/888131): Implement support for popup windows. - NOTIMPLEMENTED() << "Ignored popup window request for URL: " - << target_url.spec(); - - return false; -} - void FrameImpl::ReadyToCommitNavigation( content::NavigationHandle* navigation_handle) { if (before_load_scripts_.empty()) diff --git a/chromium/fuchsia/engine/browser/frame_impl.h b/chromium/fuchsia/engine/browser/frame_impl.h index 74415513da5..dbd85f680d5 100644 --- a/chromium/fuchsia/engine/browser/frame_impl.h +++ b/chromium/fuchsia/engine/browser/frame_impl.h @@ -92,6 +92,11 @@ class FrameImpl : public fuchsia::web::Frame, ExecuteJavaScriptCallback callback, bool need_result); + // Sends the next entry in |pending_popups_| to |popup_listener_|. + void MaybeSendPopup(); + + void OnPopupListenerDisconnected(zx_status_t status); + // fuchsia::web::Frame implementation. void CreateView(fuchsia::ui::views::ViewToken view_token) override; void GetNavigationController( @@ -118,6 +123,9 @@ class FrameImpl : public fuchsia::web::Frame, override; void SetJavaScriptLogLevel(fuchsia::web::ConsoleLogLevel level) override; void SetEnableInput(bool enable_input) override; + void SetPopupFrameCreationListener( + fidl::InterfaceHandle<fuchsia::web::PopupFrameCreationListener> listener) + override; // content::WebContentsDelegate implementation. void CloseContents(content::WebContents* source) override; @@ -139,6 +147,18 @@ class FrameImpl : public fuchsia::web::Frame, const GURL& target_url, const std::string& partition_id, content::SessionStorageNamespace* session_storage_namespace) override; + void WebContentsCreated(content::WebContents* source_contents, + int opener_render_process_id, + int opener_render_frame_id, + const std::string& frame_name, + const GURL& target_url, + content::WebContents* new_contents) override; + void AddNewContents(content::WebContents* source, + std::unique_ptr<content::WebContents> new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture, + bool* was_blocked) override; // content::WebContentsObserver implementation. void ReadyToCommitNavigation( @@ -158,6 +178,11 @@ class FrameImpl : public fuchsia::web::Frame, std::vector<uint64_t> before_load_scripts_order_; base::RepeatingCallback<void(base::StringPiece)> console_log_message_hook_; + // Used for receiving and dispatching popup created by this Frame. + fuchsia::web::PopupFrameCreationListenerPtr popup_listener_; + std::list<std::unique_ptr<content::WebContents>> pending_popups_; + bool popup_ack_outstanding_ = false; + fidl::Binding<fuchsia::web::Frame> binding_; DISALLOW_COPY_AND_ASSIGN(FrameImpl); diff --git a/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc b/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc index 67a73935570..50bd2538ed8 100644 --- a/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc +++ b/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc @@ -5,8 +5,6 @@ #include <lib/fidl/cpp/binding.h> #include <lib/ui/scenic/cpp/view_token_pair.h> -#include "base/containers/span.h" - #include "base/bind.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/macros.h" @@ -21,6 +19,7 @@ #include "fuchsia/base/frame_test_util.h" #include "fuchsia/base/mem_buffer_util.h" #include "fuchsia/base/result_receiver.h" +#include "fuchsia/base/string_util.h" #include "fuchsia/base/test_navigation_listener.h" #include "fuchsia/engine/browser/frame_impl.h" #include "fuchsia/engine/common.h" @@ -52,6 +51,9 @@ const char kPage1Path[] = "/title1.html"; const char kPage2Path[] = "/title2.html"; const char kPage3Path[] = "/websql.html"; const char kDynamicTitlePath[] = "/dynamic_title.html"; +const char kPopupPath[] = "/popup_parent.html"; +const char kPopupRedirectPath[] = "/popup_child.html"; +const char kPopupMultiplePath[] = "/popup_multiple.html"; const char kPage1Title[] = "title 1"; const char kPage2Title[] = "title 2"; const char kPage3Title[] = "websql not available"; @@ -80,11 +82,6 @@ class MockWebContentsObserver : public content::WebContentsObserver { void(content::RenderViewHost* render_view_host)); }; -std::vector<uint8_t> StringToUnsignedVector(base::StringPiece str) { - const uint8_t* raw_data = reinterpret_cast<const uint8_t*>(str.data()); - return std::vector<uint8_t>(raw_data, raw_data + str.length()); -} - std::string StringFromMemBufferOrDie(const fuchsia::mem::Buffer& buffer) { std::string output; CHECK(cr_fuchsia::StringFromMemBuffer(buffer, &output)); @@ -612,7 +609,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScript) { frame->AddBeforeLoadJavaScript( kBindingsId, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'hello';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -634,7 +631,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptUpdated) { frame->AddBeforeLoadJavaScript( kBindingsId, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'hello';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -645,7 +642,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptUpdated) { frame->AddBeforeLoadJavaScript( kBindingsId, {url.GetOrigin().spec()}, cr_fuchsia::MemBufferFromString( - "stashed_title = document.title + 'clobber';"), + "stashed_title = document.title + 'clobber';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -670,13 +667,13 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptOrdered) { frame->AddBeforeLoadJavaScript( kBindingsId1, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'hello';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); frame->AddBeforeLoadJavaScript( kBindingsId2, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title += ' there';"), + cr_fuchsia::MemBufferFromString("stashed_title += ' there';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -699,7 +696,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptRemoved) { frame->AddBeforeLoadJavaScript( kBindingsId1, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title = 'foo';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'foo';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -707,7 +704,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptRemoved) { // Add a script which clobbers "foo". frame->AddBeforeLoadJavaScript( kBindingsId2, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title = 'bar';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'bar';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -759,7 +756,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScript) { frame->ExecuteJavaScriptNoResult( {kUrl.GetOrigin().spec()}, cr_fuchsia::MemBufferFromString( - base::StringPrintf("my_variable = %s;", kJsonStringLiteral)), + base::StringPrintf("my_variable = %s;", kJsonStringLiteral), "test"), [](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -768,7 +765,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScript) { base::RunLoop loop; frame->ExecuteJavaScript( {kUrl.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("my_variable;"), + cr_fuchsia::MemBufferFromString("my_variable;", "test"), [&](fuchsia::web::Frame_ExecuteJavaScript_Result result) { ASSERT_TRUE(result.is_response()); std::string result_json = @@ -786,7 +783,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptVmoDestroyed) { frame->AddBeforeLoadJavaScript( kOnLoadScriptId, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'hello';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -806,7 +803,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptWrongOrigin) { frame->AddBeforeLoadJavaScript( kOnLoadScriptId, {"http://example.com"}, - cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'hello';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -829,7 +826,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, BeforeLoadScriptWildcardOrigin) { frame->AddBeforeLoadJavaScript( kOnLoadScriptId, {"*"}, - cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'hello';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -865,7 +862,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, frame->AddBeforeLoadJavaScript( kOnLoadScriptId, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"), + cr_fuchsia::MemBufferFromString("stashed_title = 'hello';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -879,7 +876,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, frame->AddBeforeLoadJavaScript( kOnLoadScriptId2, {url.GetOrigin().spec()}, - cr_fuchsia::MemBufferFromString("stashed_title += ' there';"), + cr_fuchsia::MemBufferFromString("stashed_title += ' there';", "test"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { EXPECT_TRUE(result.is_response()); }); @@ -912,7 +909,8 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptBadEncoding) { // 0xFE is an illegal UTF-8 byte; it should cause UTF-8 conversion to fail. frame->ExecuteJavaScriptNoResult( - {url.GetOrigin().spec()}, cr_fuchsia::MemBufferFromString("true;\xfe"), + {url.GetOrigin().spec()}, + cr_fuchsia::MemBufferFromString("true;\xfe", "test"), [&run_loop](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) { EXPECT_TRUE(result.is_err()); EXPECT_EQ(result.err(), fuchsia::web::FrameError::BUFFER_NOT_UTF8); @@ -1084,7 +1082,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessage) { "postmessage"); fuchsia::web::WebMessage message; - message.set_data(cr_fuchsia::MemBufferFromString(kPage1Path)); + message.set_data(cr_fuchsia::MemBufferFromString(kPage1Path, "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( @@ -1122,7 +1120,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessagePassMessagePort) { std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; outgoing_vector.push_back(std::move(outgoing)); msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("hi")); + msg.set_data(cr_fuchsia::MemBufferFromString("hi", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( @@ -1140,7 +1138,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessagePassMessagePort) { } { - msg.set_data(cr_fuchsia::MemBufferFromString("ping")); + msg.set_data(cr_fuchsia::MemBufferFromString("ping", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result> post_result; message_port->PostMessage( @@ -1183,7 +1181,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageMessagePortDisconnected) { std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; outgoing_vector.push_back(std::move(outgoing)); msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("hi")); + msg.set_data(cr_fuchsia::MemBufferFromString("hi", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( @@ -1240,7 +1238,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageUseContentProvidedPort) { std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; outgoing_vector.push_back(std::move(outgoing)); msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("hi")); + msg.set_data(cr_fuchsia::MemBufferFromString("hi", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; frame->PostMessage( @@ -1268,7 +1266,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageUseContentProvidedPort) { base::RunLoop run_loop; cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result> post_result(run_loop.QuitClosure()); - msg.set_data(cr_fuchsia::MemBufferFromString("ping")); + msg.set_data(cr_fuchsia::MemBufferFromString("ping", "test")); incoming_message_port->PostMessage( std::move(msg), cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback())); @@ -1286,7 +1284,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageUseContentProvidedPort) { std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; outgoing_vector.push_back(std::move(outgoing)); msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("hi")); + msg.set_data(cr_fuchsia::MemBufferFromString("hi", "test")); // Quit the runloop only after we've received a WebMessage AND a PostMessage // result. @@ -1345,7 +1343,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageBadOriginDropped) { std::vector<fuchsia::web::OutgoingTransferable> unused_outgoing_vector; unused_outgoing_vector.push_back(std::move(unused_outgoing)); msg.set_outgoing_transfer(std::move(unused_outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("bad origin, bad!")); + msg.set_data(cr_fuchsia::MemBufferFromString("bad origin, bad!", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> unused_post_result; @@ -1369,7 +1367,7 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageBadOriginDropped) { std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector; outgoing_vector.push_back(std::move(outgoing)); msg.set_outgoing_transfer(std::move(outgoing_vector)); - msg.set_data(cr_fuchsia::MemBufferFromString("good origin")); + msg.set_data(cr_fuchsia::MemBufferFromString("good origin", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result> post_result; @@ -1444,6 +1442,114 @@ IN_PROC_BROWSER_TEST_F(FrameImplTest, RecreateView) { navigation_listener_.RunUntilUrlAndTitleEquals(page1_url, kPage1Title); } +// Tests SetNavigationEventListener() immediately returns a NavigationEvent, +// even in the absence of a new navigation. +IN_PROC_BROWSER_TEST_F(FrameImplTest, ImmediateNavigationEvent) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL page_url(embedded_test_server()->GetURL(kPage1Path)); + + // The first NavigationState received should be empty. + base::RunLoop run_loop; + navigation_listener_.SetBeforeAckHook(base::BindRepeating( + [](base::RunLoop* run_loop, const fuchsia::web::NavigationState& change, + OnNavigationStateChangedCallback callback) { + EXPECT_TRUE(change.IsEmpty()); + run_loop->Quit(); + callback(); + }, + base::Unretained(&run_loop))); + fuchsia::web::FramePtr frame = CreateFrame(); + run_loop.Run(); + navigation_listener_.SetBeforeAckHook({}); + + // Navigate to a page and wait for the navigation to complete. + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), page_url.spec())); + navigation_listener_.RunUntilUrlEquals(page_url); + + // Attach a new navigation listener, we should get the new page state, even if + // no new navigation occurred. + cr_fuchsia::TestNavigationListener navigation_listener2; + fidl::Binding<fuchsia::web::NavigationEventListener> + navigation_listener_binding(&navigation_listener2); + frame->SetNavigationEventListener(navigation_listener_binding.NewBinding()); + navigation_listener2.RunUntilUrlAndTitleEquals(page_url, kPage1Title); +} + +// Check loading an invalid URL in NavigationController.LoadUrl() sets the right +// error. +IN_PROC_BROWSER_TEST_F(FrameImplTest, InvalidUrl) { + fuchsia::web::FramePtr frame = CreateFrame(); + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + base::RunLoop run_loop; + cr_fuchsia::ResultReceiver<fuchsia::web::NavigationController_LoadUrl_Result> + result(run_loop.QuitClosure()); + controller->LoadUrl( + "http:google.com:foo", fuchsia::web::LoadUrlParams(), + cr_fuchsia::CallbackToFitFunction(result.GetReceiveCallback())); + run_loop.Run(); + + ASSERT_TRUE(result->is_err()); + EXPECT_EQ(result->err(), + fuchsia::web::NavigationControllerError::INVALID_URL); +} + +// Check setting invalid headers in NavigationController.LoadUrl() sets the +// right error. +IN_PROC_BROWSER_TEST_F(FrameImplTest, InvalidHeader) { + fuchsia::web::FramePtr frame = CreateFrame(); + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + + { + // Set an invalid header name. + fuchsia::web::LoadUrlParams load_url_params; + fuchsia::net::http::Header header; + header.name = cr_fuchsia::StringToBytes("Invalid:Header"); + header.value = cr_fuchsia::StringToBytes("1"); + load_url_params.set_headers({header}); + + base::RunLoop run_loop; + cr_fuchsia::ResultReceiver< + fuchsia::web::NavigationController_LoadUrl_Result> + result(run_loop.QuitClosure()); + controller->LoadUrl( + "http://site.ext/", std::move(load_url_params), + cr_fuchsia::CallbackToFitFunction(result.GetReceiveCallback())); + run_loop.Run(); + + ASSERT_TRUE(result->is_err()); + EXPECT_EQ(result->err(), + fuchsia::web::NavigationControllerError::INVALID_HEADER); + } + + { + // Set an invalid header value. + fuchsia::web::LoadUrlParams load_url_params; + fuchsia::net::http::Header header; + header.name = cr_fuchsia::StringToBytes("Header"); + header.value = cr_fuchsia::StringToBytes("Invalid\rValue"); + load_url_params.set_headers({header}); + + base::RunLoop run_loop; + cr_fuchsia::ResultReceiver< + fuchsia::web::NavigationController_LoadUrl_Result> + result(run_loop.QuitClosure()); + controller->LoadUrl( + "http://site.ext/", std::move(load_url_params), + cr_fuchsia::CallbackToFitFunction(result.GetReceiveCallback())); + run_loop.Run(); + + ASSERT_TRUE(result->is_err()); + EXPECT_EQ(result->err(), + fuchsia::web::NavigationControllerError::INVALID_HEADER); + } +} + class RequestMonitoringFrameImplBrowserTest : public FrameImplTest { public: RequestMonitoringFrameImplBrowserTest() = default; @@ -1493,11 +1599,11 @@ IN_PROC_BROWSER_TEST_F(RequestMonitoringFrameImplBrowserTest, ExtraHeaders) { const GURL page_url(embedded_test_server()->GetURL(kPage1Path)); fuchsia::web::LoadUrlParams load_url_params; fuchsia::net::http::Header header1; - header1.name = StringToUnsignedVector("X-ExtraHeaders"); - header1.value = StringToUnsignedVector("1"); + header1.name = cr_fuchsia::StringToBytes("X-ExtraHeaders"); + header1.value = cr_fuchsia::StringToBytes("1"); fuchsia::net::http::Header header2; - header2.name = StringToUnsignedVector("X-2ExtraHeaders"); - header2.value = StringToUnsignedVector("2"); + header2.name = cr_fuchsia::StringToBytes("X-2ExtraHeaders"); + header2.value = cr_fuchsia::StringToBytes("2"); load_url_params.set_headers({header1, header2}); EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( @@ -1513,3 +1619,100 @@ IN_PROC_BROWSER_TEST_F(RequestMonitoringFrameImplBrowserTest, ExtraHeaders) { EXPECT_THAT(iter->second.headers, testing::Contains(testing::Key("X-2ExtraHeaders"))); } + +class TestPopupListener : public fuchsia::web::PopupFrameCreationListener { + public: + TestPopupListener() = default; + ~TestPopupListener() override = default; + + void GetAndAckNextPopup(fuchsia::web::FramePtr* frame, + fuchsia::web::PopupFrameCreationInfo* creation_info) { + if (!frame_) { + base::RunLoop run_loop; + received_popup_callback_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + *frame = frame_.Bind(); + *creation_info = std::move(creation_info_); + + popup_ack_callback_(); + popup_ack_callback_ = {}; + } + + private: + void OnPopupFrameCreated(fidl::InterfaceHandle<fuchsia::web::Frame> frame, + fuchsia::web::PopupFrameCreationInfo creation_info, + OnPopupFrameCreatedCallback callback) override { + creation_info_ = std::move(creation_info); + frame_ = std::move(frame); + + popup_ack_callback_ = std::move(callback); + + if (received_popup_callback_) + std::move(received_popup_callback_).Run(); + } + + fidl::InterfaceHandle<fuchsia::web::Frame> frame_; + fuchsia::web::PopupFrameCreationInfo creation_info_; + base::OnceClosure received_popup_callback_; + OnPopupFrameCreatedCallback popup_ack_callback_; +}; + +IN_PROC_BROWSER_TEST_F(FrameImplTest, PopupWindow) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL popup_url(embedded_test_server()->GetURL(kPopupPath)); + GURL popup_child_url(embedded_test_server()->GetURL(kPopupRedirectPath)); + GURL title1_url(embedded_test_server()->GetURL(kPage1Path)); + fuchsia::web::FramePtr frame = CreateFrame(); + + TestPopupListener popup_listener; + fidl::Binding<fuchsia::web::PopupFrameCreationListener> + popup_listener_binding(&popup_listener); + frame->SetPopupFrameCreationListener(popup_listener_binding.NewBinding()); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(controller.get(), {}, + popup_url.spec())); + + // Verify the popup's initial URL, "popup_child.html". + fuchsia::web::FramePtr popup_frame; + fuchsia::web::PopupFrameCreationInfo popup_info; + popup_listener.GetAndAckNextPopup(&popup_frame, &popup_info); + EXPECT_EQ(popup_info.initial_url(), popup_child_url); + + // Verify that the popup eventually redirects to "title1.html". + cr_fuchsia::TestNavigationListener popup_nav_listener; + fidl::Binding<fuchsia::web::NavigationEventListener> + popup_nav_listener_binding(&popup_nav_listener); + popup_frame->SetNavigationEventListener( + popup_nav_listener_binding.NewBinding()); + popup_nav_listener.RunUntilUrlAndTitleEquals(title1_url, kPage1Title); +} + +IN_PROC_BROWSER_TEST_F(FrameImplTest, MultiplePopups) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL popup_url(embedded_test_server()->GetURL(kPopupMultiplePath)); + GURL title1_url(embedded_test_server()->GetURL(kPage1Path)); + GURL title2_url(embedded_test_server()->GetURL(kPage2Path)); + fuchsia::web::FramePtr frame = CreateFrame(); + + TestPopupListener popup_listener; + fidl::Binding<fuchsia::web::PopupFrameCreationListener> + popup_listener_binding(&popup_listener); + frame->SetPopupFrameCreationListener(popup_listener_binding.NewBinding()); + + fuchsia::web::NavigationControllerPtr controller; + frame->GetNavigationController(controller.NewRequest()); + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(controller.get(), {}, + popup_url.spec())); + + fuchsia::web::FramePtr popup_frame; + fuchsia::web::PopupFrameCreationInfo popup_info; + popup_listener.GetAndAckNextPopup(&popup_frame, &popup_info); + EXPECT_EQ(popup_info.initial_url(), title1_url); + + popup_listener.GetAndAckNextPopup(&popup_frame, &popup_info); + EXPECT_EQ(popup_info.initial_url(), title2_url); +} diff --git a/chromium/fuchsia/engine/browser/navigation_controller_impl.cc b/chromium/fuchsia/engine/browser/navigation_controller_impl.cc index cf0eb7e2435..5f5257b42a9 100644 --- a/chromium/fuchsia/engine/browser/navigation_controller_impl.cc +++ b/chromium/fuchsia/engine/browser/navigation_controller_impl.cc @@ -9,7 +9,9 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" -#include "content/public/common/was_activated_option.h" +#include "content/public/common/was_activated_option.mojom.h" +#include "fuchsia/base/string_util.h" +#include "net/http/http_util.h" #include "ui/base/page_transition_types.h" namespace { @@ -62,12 +64,26 @@ void NavigationControllerImpl::SetEventListener( previous_navigation_state_ = {}; pending_navigation_event_ = {}; - if (listener) { - navigation_listener_.Bind(std::move(listener)); - navigation_listener_.set_error_handler( - [this](zx_status_t status) { SetEventListener(nullptr); }); - } else { + // Simply unbind if no new listener was set. + if (!listener) { navigation_listener_.Unbind(); + return; + } + + navigation_listener_.Bind(std::move(listener)); + navigation_listener_.set_error_handler( + [this](zx_status_t status) { SetEventListener(nullptr); }); + + // Immediately send the current navigation state, even if it is empty. + if (web_contents_->GetController().GetVisibleEntry() == nullptr) { + waiting_for_navigation_event_ack_ = true; + navigation_listener_->OnNavigationStateChanged( + fuchsia::web::NavigationState(), [this]() { + waiting_for_navigation_event_ack_ = false; + MaybeSendNavigationEvent(); + }); + } else { + OnNavigationEntryChanged(); } } @@ -126,13 +142,15 @@ void NavigationControllerImpl::LoadUrl(std::string url, std::vector<std::string> extra_headers; extra_headers.reserve(params.headers().size()); for (const auto& header : params.headers()) { - // TODO(crbug.com/964732): Check there is no colon in |header_name|. - base::StringPiece header_name( - reinterpret_cast<const char*>(header.name.data()), - header.name.size()); - base::StringPiece header_value( - reinterpret_cast<const char*>(header.value.data()), - header.value.size()); + base::StringPiece header_name = cr_fuchsia::BytesAsString(header.name); + base::StringPiece header_value = cr_fuchsia::BytesAsString(header.value); + if (!net::HttpUtil::IsValidHeaderName(header_name) || + !net::HttpUtil::IsValidHeaderValue(header_value)) { + result.set_err(fuchsia::web::NavigationControllerError::INVALID_HEADER); + callback(std::move(result)); + return; + } + extra_headers.emplace_back( base::StrCat({header_name, ": ", header_value})); } @@ -145,9 +163,9 @@ void NavigationControllerImpl::LoadUrl(std::string url, params_converted.transition_type = ui::PageTransitionFromInt( ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); if (params.has_was_user_activated() && params.was_user_activated()) { - params_converted.was_activated = content::WasActivatedOption::kYes; + params_converted.was_activated = content::mojom::WasActivatedOption::kYes; } else { - params_converted.was_activated = content::WasActivatedOption::kNo; + params_converted.was_activated = content::mojom::WasActivatedOption::kNo; } web_contents_->GetController().LoadURLWithParams(params_converted); diff --git a/chromium/fuchsia/engine/browser/web_engine_browser_context.cc b/chromium/fuchsia/engine/browser/web_engine_browser_context.cc index df15f6cc017..c381d9c7b6d 100644 --- a/chromium/fuchsia/engine/browser/web_engine_browser_context.cc +++ b/chromium/fuchsia/engine/browser/web_engine_browser_context.cc @@ -19,9 +19,7 @@ #include "content/public/browser/resource_context.h" #include "fuchsia/engine/browser/web_engine_net_log.h" #include "fuchsia/engine/browser/web_engine_permission_manager.h" -#include "fuchsia/engine/browser/web_engine_url_request_context_getter.h" #include "fuchsia/engine/common.h" -#include "net/url_request/url_request_context.h" #include "services/network/public/cpp/network_switches.h" class WebEngineBrowserContext::ResourceContext @@ -144,21 +142,3 @@ content::BrowsingDataRemoverDelegate* WebEngineBrowserContext::GetBrowsingDataRemoverDelegate() { return nullptr; } - -net::URLRequestContextGetter* WebEngineBrowserContext::CreateRequestContext( - content::ProtocolHandlerMap* protocol_handlers, - content::URLRequestInterceptorScopedVector request_interceptors) { - DCHECK(!url_request_getter_); - url_request_getter_ = new WebEngineURLRequestContextGetter( - base::CreateSingleThreadTaskRunnerWithTraits( - {content::BrowserThread::IO}), - net_log_.get(), std::move(*protocol_handlers), - std::move(request_interceptors), data_dir_path_); - return url_request_getter_.get(); -} - -net::URLRequestContextGetter* -WebEngineBrowserContext::CreateMediaRequestContext() { - DCHECK(url_request_getter_.get()); - return url_request_getter_.get(); -} diff --git a/chromium/fuchsia/engine/browser/web_engine_browser_context.h b/chromium/fuchsia/engine/browser/web_engine_browser_context.h index 914f7de2306..c508858c24d 100644 --- a/chromium/fuchsia/engine/browser/web_engine_browser_context.h +++ b/chromium/fuchsia/engine/browser/web_engine_browser_context.h @@ -14,7 +14,6 @@ class WebEngineNetLog; class WebEnginePermissionManager; -class WebEngineURLRequestContextGetter; class WebEngineBrowserContext : public content::BrowserContext { public: @@ -42,10 +41,6 @@ class WebEngineBrowserContext : public content::BrowserContext { content::BackgroundSyncController* GetBackgroundSyncController() override; content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate() override; - net::URLRequestContextGetter* CreateRequestContext( - content::ProtocolHandlerMap* protocol_handlers, - content::URLRequestInterceptorScopedVector request_interceptors) override; - net::URLRequestContextGetter* CreateMediaRequestContext() override; private: // Contains URLRequestContextGetter required for resource loading. @@ -54,7 +49,6 @@ class WebEngineBrowserContext : public content::BrowserContext { base::FilePath data_dir_path_; std::unique_ptr<WebEngineNetLog> net_log_; - scoped_refptr<WebEngineURLRequestContextGetter> url_request_getter_; std::unique_ptr<SimpleFactoryKey> simple_factory_key_; std::unique_ptr<ResourceContext> resource_context_; std::unique_ptr<WebEnginePermissionManager> permission_manager_; diff --git a/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.cc b/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.cc index ae98822ff45..a8ac93a91eb 100644 --- a/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.cc +++ b/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.cc @@ -10,12 +10,14 @@ #include "base/command_line.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/logging.h" +#include "content/public/browser/gpu_data_manager.h" #include "content/public/browser/render_frame_host.h" #include "content/public/common/main_function_params.h" #include "fuchsia/engine/browser/context_impl.h" #include "fuchsia/engine/browser/web_engine_browser_context.h" #include "fuchsia/engine/browser/web_engine_screen.h" #include "fuchsia/engine/common.h" +#include "gpu/command_buffer/service/gpu_switches.h" #include "ui/aura/screen_ozone.h" #include "ui/ozone/public/ozone_platform.h" @@ -31,17 +33,20 @@ WebEngineBrowserMainParts::~WebEngineBrowserMainParts() { void WebEngineBrowserMainParts::PreMainMessageLoopRun() { DCHECK(!screen_); - auto platform_screen = ui::OzonePlatform::GetInstance()->CreateScreen(); - if (platform_screen) { - screen_ = std::make_unique<aura::ScreenOzone>(std::move(platform_screen)); - } else { - // Use dummy display::Screen for Ozone platforms that don't provide - // PlatformScreen. - screen_ = std::make_unique<WebEngineScreen>(); - } - + screen_ = std::make_unique<aura::ScreenOzone>(); display::Screen::SetScreenInstance(screen_.get()); + // If Vulkan is not enabled then disable hardware acceleration. Otherwise gpu + // process will be restarted several times trying to initialize GL before + // falling back to software compositing. + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kUseVulkan)) { + content::GpuDataManager* gpu_data_manager = + content::GpuDataManager::GetInstance(); + DCHECK(gpu_data_manager); + gpu_data_manager->DisableHardwareAcceleration(); + } + DCHECK(!browser_context_); browser_context_ = std::make_unique<WebEngineBrowserContext>( base::CommandLine::ForCurrentProcess()->HasSwitch(kIncognitoSwitch)); diff --git a/chromium/fuchsia/engine/browser/web_engine_cdm_service.cc b/chromium/fuchsia/engine/browser/web_engine_cdm_service.cc new file mode 100644 index 00000000000..968b02884e9 --- /dev/null +++ b/chromium/fuchsia/engine/browser/web_engine_cdm_service.cc @@ -0,0 +1,131 @@ +// Copyright 2019 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 "fuchsia/engine/browser/web_engine_cdm_service.h" + +#include <fuchsia/media/drm/cpp/fidl.h> +#include <lib/sys/cpp/component_context.h> +#include <string> + +#include "base/bind.h" +#include "base/fuchsia/default_context.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/frame_service_base.h" +#include "content/public/browser/provision_fetcher_factory.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/storage_partition.h" +#include "media/base/provision_fetcher.h" +#include "media/fuchsia/cdm/service/fuchsia_cdm_manager.h" +#include "media/fuchsia/mojom/fuchsia_cdm_provider.mojom.h" +#include "third_party/widevine/cdm/widevine_cdm_common.h" + +namespace { +class FuchsiaCdmProviderImpl + : public content::FrameServiceBase<media::mojom::FuchsiaCdmProvider> { + public: + FuchsiaCdmProviderImpl(media::FuchsiaCdmManager* cdm_manager, + media::CreateFetcherCB create_fetcher_cb, + content::RenderFrameHost* render_frame_host, + media::mojom::FuchsiaCdmProviderRequest request); + ~FuchsiaCdmProviderImpl() final; + + // media::mojom::FuchsiaCdmProvider implementation. + void CreateCdmInterface( + const std::string& key_system, + fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule> + request) final; + + private: + media::FuchsiaCdmManager* const cdm_manager_; + const media::CreateFetcherCB create_fetcher_cb_; + + DISALLOW_COPY_AND_ASSIGN(FuchsiaCdmProviderImpl); +}; + +FuchsiaCdmProviderImpl::FuchsiaCdmProviderImpl( + media::FuchsiaCdmManager* cdm_manager, + media::CreateFetcherCB create_fetcher_cb, + content::RenderFrameHost* render_frame_host, + media::mojom::FuchsiaCdmProviderRequest request) + : FrameServiceBase(render_frame_host, std::move(request)), + cdm_manager_(cdm_manager), + create_fetcher_cb_(std::move(create_fetcher_cb)) { + DCHECK(cdm_manager_); +} + +FuchsiaCdmProviderImpl::~FuchsiaCdmProviderImpl() = default; + +void FuchsiaCdmProviderImpl::CreateCdmInterface( + const std::string& key_system, + fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule> + request) { + cdm_manager_->CreateAndProvision(key_system, origin(), create_fetcher_cb_, + std::move(request)); +} + +void BindFuchsiaCdmProvider(media::FuchsiaCdmManager* cdm_manager, + media::mojom::FuchsiaCdmProviderRequest request, + content::RenderFrameHost* const frame_host) { + scoped_refptr<network::SharedURLLoaderFactory> loader_factory = + content::BrowserContext::GetDefaultStoragePartition( + frame_host->GetProcess()->GetBrowserContext()) + ->GetURLLoaderFactoryForBrowserProcess(); + + // The object will delete itself when connection to the frame is broken. + new FuchsiaCdmProviderImpl( + cdm_manager, + base::BindRepeating(&content::CreateProvisionFetcher, + std::move(loader_factory)), + frame_host, std::move(request)); +} + +class WidevineHandler : public media::FuchsiaCdmManager::KeySystemHandler { + public: + WidevineHandler() = default; + ~WidevineHandler() override = default; + + void CreateCdm( + fidl::InterfaceRequest<fuchsia::media::drm::ContentDecryptionModule> + request) override { + auto widevine = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::media::drm::Widevine>(); + widevine->CreateContentDecryptionModule(std::move(request)); + } + + fuchsia::media::drm::ProvisionerPtr CreateProvisioner() override { + fuchsia::media::drm::ProvisionerPtr provisioner; + + auto widevine = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::media::drm::Widevine>(); + widevine->CreateProvisioner(provisioner.NewRequest()); + + return provisioner; + } +}; + +// Supported key systems: +std::unique_ptr<media::FuchsiaCdmManager> CreateCdmManager() { + media::FuchsiaCdmManager::KeySystemHandlerMap handlers; + handlers.emplace(kWidevineKeySystem, std::make_unique<WidevineHandler>()); + + return std::make_unique<media::FuchsiaCdmManager>(std::move(handlers)); +} +} // namespace + +WebEngineCdmService::WebEngineCdmService( + service_manager::BinderRegistryWithArgs<content::RenderFrameHost*>* + registry) + : cdm_manager_(CreateCdmManager()), registry_(registry) { + DCHECK(cdm_manager_); + DCHECK(registry_); + registry_->AddInterface( + base::BindRepeating(&BindFuchsiaCdmProvider, cdm_manager_.get())); +} + +WebEngineCdmService::~WebEngineCdmService() { + registry_->RemoveInterface<media::mojom::FuchsiaCdmProvider>(); +} diff --git a/chromium/fuchsia/engine/browser/web_engine_cdm_service.h b/chromium/fuchsia/engine/browser/web_engine_cdm_service.h new file mode 100644 index 00000000000..d35882fe354 --- /dev/null +++ b/chromium/fuchsia/engine/browser/web_engine_cdm_service.h @@ -0,0 +1,37 @@ +// Copyright 2019 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 FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_CDM_SERVICE_H_ +#define FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_CDM_SERVICE_H_ + +#include <memory> + +#include "services/service_manager/public/cpp/binder_registry.h" + +namespace content { +class RenderFrameHost; +} + +namespace media { +class FuchsiaCdmManager; +} + +class WebEngineCdmService { + public: + explicit WebEngineCdmService( + service_manager::BinderRegistryWithArgs<content::RenderFrameHost*>* + registry); + ~WebEngineCdmService(); + + private: + std::unique_ptr<media::FuchsiaCdmManager> cdm_manager_; + + // Not owned pointer. |registry_| must outlive |this|. + service_manager::BinderRegistryWithArgs<content::RenderFrameHost*>* const + registry_; + + DISALLOW_COPY_AND_ASSIGN(WebEngineCdmService); +}; + +#endif // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_CDM_SERVICE_H_ diff --git a/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc b/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc index f6621b54b96..a16f17ddb88 100644 --- a/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc +++ b/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc @@ -4,6 +4,8 @@ #include "fuchsia/engine/browser/web_engine_content_browser_client.h" +#include <fuchsia/web/cpp/fidl.h> +#include <string> #include <utility> #include "components/version_info/version_info.h" @@ -16,7 +18,7 @@ WebEngineContentBrowserClient::WebEngineContentBrowserClient( fidl::InterfaceRequest<fuchsia::web::Context> request) - : request_(std::move(request)) {} + : request_(std::move(request)), cdm_service_(&mojo_service_registry_) {} WebEngineContentBrowserClient::~WebEngineContentBrowserClient() = default; @@ -60,3 +62,39 @@ void WebEngineContentBrowserClient::OverrideWebkitPrefs( // Disable WebSQL support since it's being removed from the web platform. web_prefs->databases_enabled = false; } + +void WebEngineContentBrowserClient::BindInterfaceRequestFromFrame( + content::RenderFrameHost* render_frame_host, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) { + mojo_service_registry_.BindInterface( + interface_name, std::move(interface_pipe), render_frame_host); +} + +void WebEngineContentBrowserClient:: + RegisterNonNetworkNavigationURLLoaderFactories( + int frame_tree_node_id, + NonNetworkURLLoaderFactoryMap* factories) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch(kContentDirectories)) { + (*factories)[kFuchsiaContentDirectoryScheme] = + std::make_unique<ContentDirectoryLoaderFactory>(); + } +} + +void WebEngineContentBrowserClient:: + RegisterNonNetworkSubresourceURLLoaderFactories( + int render_process_id, + int render_frame_id, + NonNetworkURLLoaderFactoryMap* factories) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch(kContentDirectories)) { + (*factories)[kFuchsiaContentDirectoryScheme] = + std::make_unique<ContentDirectoryLoaderFactory>(); + } +} + +void WebEngineContentBrowserClient::AppendExtraCommandLineSwitches( + base::CommandLine* command_line, + int child_process_id) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch(kContentDirectories)) + command_line->AppendSwitch(kContentDirectories); +} diff --git a/chromium/fuchsia/engine/browser/web_engine_content_browser_client.h b/chromium/fuchsia/engine/browser/web_engine_content_browser_client.h index 73092827e29..560ed6c7546 100644 --- a/chromium/fuchsia/engine/browser/web_engine_content_browser_client.h +++ b/chromium/fuchsia/engine/browser/web_engine_content_browser_client.h @@ -5,13 +5,17 @@ #ifndef FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_CONTENT_BROWSER_CLIENT_H_ #define FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_CONTENT_BROWSER_CLIENT_H_ -#include <memory> +#include <lib/zx/channel.h> #include <fuchsia/web/cpp/fidl.h> -#include <lib/zx/channel.h> +#include <memory> +#include <string> #include "base/macros.h" #include "content/public/browser/content_browser_client.h" +#include "fuchsia/engine/browser/content_directory_loader_factory.h" +#include "fuchsia/engine/browser/web_engine_cdm_service.h" +#include "services/service_manager/public/cpp/binder_registry.h" class WebEngineBrowserMainParts; @@ -19,18 +23,31 @@ class WebEngineContentBrowserClient : public content::ContentBrowserClient { public: explicit WebEngineContentBrowserClient( fidl::InterfaceRequest<fuchsia::web::Context> request); - ~WebEngineContentBrowserClient() override; + ~WebEngineContentBrowserClient() final; WebEngineBrowserMainParts* main_parts_for_test() const { return main_parts_; } // ContentBrowserClient overrides. std::unique_ptr<content::BrowserMainParts> CreateBrowserMainParts( - const content::MainFunctionParams& parameters) override; - content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override; - std::string GetProduct() override; - std::string GetUserAgent() override; + const content::MainFunctionParams& parameters) final; + content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() final; + std::string GetProduct() final; + std::string GetUserAgent() final; void OverrideWebkitPrefs(content::RenderViewHost* rvh, - content::WebPreferences* web_prefs) override; + content::WebPreferences* web_prefs) final; + void BindInterfaceRequestFromFrame( + content::RenderFrameHost* render_frame_host, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) final; + void RegisterNonNetworkNavigationURLLoaderFactories( + int frame_tree_node_id, + NonNetworkURLLoaderFactoryMap* factories) final; + void RegisterNonNetworkSubresourceURLLoaderFactories( + int render_process_id, + int render_frame_id, + NonNetworkURLLoaderFactoryMap* factories) final; + void AppendExtraCommandLineSwitches(base::CommandLine* command_line, + int child_process_id) final; private: fidl::InterfaceRequest<fuchsia::web::Context> request_; @@ -38,6 +55,10 @@ class WebEngineContentBrowserClient : public content::ContentBrowserClient { // Owned by content::BrowserMainLoop. WebEngineBrowserMainParts* main_parts_; + service_manager::BinderRegistryWithArgs<content::RenderFrameHost*> + mojo_service_registry_; + WebEngineCdmService cdm_service_; + DISALLOW_COPY_AND_ASSIGN(WebEngineContentBrowserClient); }; diff --git a/chromium/fuchsia/engine/browser/web_engine_url_request_context_getter.cc b/chromium/fuchsia/engine/browser/web_engine_url_request_context_getter.cc deleted file mode 100644 index 91a97a60498..00000000000 --- a/chromium/fuchsia/engine/browser/web_engine_url_request_context_getter.cc +++ /dev/null @@ -1,69 +0,0 @@ -// 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 "fuchsia/engine/browser/web_engine_url_request_context_getter.h" - -#include <utility> - -#include "base/single_thread_task_runner.h" -#include "content/public/browser/cookie_store_factory.h" -#include "net/cookies/cookie_store.h" -#include "net/proxy_resolution/proxy_config_service.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_builder.h" - -WebEngineURLRequestContextGetter::WebEngineURLRequestContextGetter( - scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, - net::NetLog* net_log, - content::ProtocolHandlerMap protocol_handlers, - content::URLRequestInterceptorScopedVector request_interceptors, - base::FilePath data_dir_path) - : network_task_runner_(std::move(network_task_runner)), - net_log_(net_log), - protocol_handlers_(std::move(protocol_handlers)), - request_interceptors_(std::move(request_interceptors)), - data_dir_path_(data_dir_path) {} - -WebEngineURLRequestContextGetter::~WebEngineURLRequestContextGetter() = default; - -net::URLRequestContext* -WebEngineURLRequestContextGetter::GetURLRequestContext() { - if (!url_request_context_) { - net::URLRequestContextBuilder builder; - builder.set_net_log(net_log_); - builder.set_data_enabled(true); - - for (auto& protocol_handler : protocol_handlers_) { - builder.SetProtocolHandler(protocol_handler.first, - std::move(protocol_handler.second)); - } - protocol_handlers_.clear(); - - builder.SetInterceptors(std::move(request_interceptors_)); - - if (data_dir_path_.empty()) { - // Set up an in-memory (ephemeral) CookieStore. - builder.SetCookieStore( - content::CreateCookieStore(content::CookieStoreConfig(), nullptr)); - } else { - // Set up a persistent CookieStore under |data_dir_path|. - content::CookieStoreConfig cookie_config( - data_dir_path_.Append(FILE_PATH_LITERAL("Cookies")), false, false, - NULL); - - // Fuchsia protects the local data at rest so there is no need to encrypt - // cookie store. - builder.SetCookieStore( - content::CreateCookieStore(cookie_config, nullptr)); - } - - url_request_context_ = builder.Build(); - } - return url_request_context_.get(); -} - -scoped_refptr<base::SingleThreadTaskRunner> -WebEngineURLRequestContextGetter::GetNetworkTaskRunner() const { - return network_task_runner_; -} diff --git a/chromium/fuchsia/engine/browser/web_engine_url_request_context_getter.h b/chromium/fuchsia/engine/browser/web_engine_url_request_context_getter.h deleted file mode 100644 index 9d501cdc692..00000000000 --- a/chromium/fuchsia/engine/browser/web_engine_url_request_context_getter.h +++ /dev/null @@ -1,53 +0,0 @@ -// 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 FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_URL_REQUEST_CONTEXT_GETTER_H_ -#define FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_URL_REQUEST_CONTEXT_GETTER_H_ - -#include <memory> - -#include "base/macros.h" -#include "content/public/browser/browser_context.h" -#include "net/url_request/url_request_context_getter.h" - -namespace base { -class SingleThreadTaskRunner; -} // namespace base - -namespace net { -class NetLog; -class ProxyConfigService; -} // namespace net - -class WebEngineURLRequestContextGetter : public net::URLRequestContextGetter { - public: - WebEngineURLRequestContextGetter( - scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, - net::NetLog* net_log, - content::ProtocolHandlerMap protocol_handlers, - content::URLRequestInterceptorScopedVector request_interceptors, - base::FilePath data_dir_path); - - // net::URLRequestContextGetter overrides. - net::URLRequestContext* GetURLRequestContext() override; - scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() - const override; - - protected: - ~WebEngineURLRequestContextGetter() override; - - private: - scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; - net::NetLog* net_log_; - std::unique_ptr<net::ProxyConfigService> proxy_config_service_; - std::unique_ptr<net::URLRequestContext> url_request_context_; - - content::ProtocolHandlerMap protocol_handlers_; - content::URLRequestInterceptorScopedVector request_interceptors_; - base::FilePath data_dir_path_; - - DISALLOW_COPY_AND_ASSIGN(WebEngineURLRequestContextGetter); -}; - -#endif // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_URL_REQUEST_CONTEXT_GETTER_H_ diff --git a/chromium/fuchsia/engine/common.cc b/chromium/fuchsia/engine/common.cc index 22db116c1fb..55207757550 100644 --- a/chromium/fuchsia/engine/common.cc +++ b/chromium/fuchsia/engine/common.cc @@ -7,3 +7,5 @@ constexpr char kIncognitoSwitch[] = "incognito"; constexpr char kRemoteDebuggerHandles[] = "remote-debugger-handles"; constexpr char kUserAgentProductAndVersion[] = "user-agent-product"; +constexpr char kContentDirectories[] = "content-directories"; +constexpr char kFuchsiaContentDirectoryScheme[] = "fuchsia-dir"; diff --git a/chromium/fuchsia/engine/common.h b/chromium/fuchsia/engine/common.h index f0664e0e6a3..7a478794281 100644 --- a/chromium/fuchsia/engine/common.h +++ b/chromium/fuchsia/engine/common.h @@ -27,4 +27,17 @@ WEB_ENGINE_EXPORT extern const char kUserAgentProductAndVersion[]; // Context process. constexpr uint32_t kContextRequestHandleId = PA_HND(PA_USER0, 0); +// The URL scheme used to access content directories. +WEB_ENGINE_EXPORT extern const char + kFuchsiaContentDirectoryScheme[]; // "fuchsia-dir" + +// The command line switch used to register custom schemes with a process. +// The contents are structured as a list of key-value pairs which map the scheme +// name to the handle of a Fuchsia::io::Directory channel. +// e.g. foo=1234,bar=5678 +// The switch can be used to send names by themselves, by pairing the names with +// zeroed handles. +WEB_ENGINE_EXPORT extern const char + kContentDirectories[]; // "content-directories"; + #endif // FUCHSIA_ENGINE_COMMON_H_ diff --git a/chromium/fuchsia/engine/context_provider_impl.cc b/chromium/fuchsia/engine/context_provider_impl.cc index b9a39ba03d5..5d8d9b279dd 100644 --- a/chromium/fuchsia/engine/context_provider_impl.cc +++ b/chromium/fuchsia/engine/context_provider_impl.cc @@ -16,10 +16,12 @@ #include <unistd.h> #include <zircon/processargs.h> +#include <string> #include <utility> #include <vector> #include "base/base_paths_fuchsia.h" +#include "base/base_switches.h" #include "base/bind.h" #include "base/command_line.h" #include "base/files/scoped_file.h" @@ -28,12 +30,16 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/process/launch.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "components/viz/common/features.h" #include "content/public/common/content_switches.h" #include "fuchsia/engine/common.h" +#include "gpu/command_buffer/service/gpu_switches.h" #include "net/http/http_util.h" #include "services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h" +#include "ui/gl/gl_switches.h" namespace { @@ -55,27 +61,41 @@ zx::channel ValidateDirectoryAndTakeChannel( return zx::channel(); } -// Verifies that Vulkan loader service is provided by the specified service -// directory. -bool CheckVulkanSupport( - const fidl::InterfaceHandle<::fuchsia::io::Directory>& directory_handle, - bool* vulkan_supported) { - zx::channel dir_channel(fdio_service_clone(directory_handle.channel().get())); - if (!dir_channel) - return false; - - base::ScopedFD dir_fd; - zx_status_t status = fdio_fd_create(dir_channel.release(), - base::ScopedFD::Receiver(dir_fd).get()); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "fdio_fd_create()"; - return false; +// Populates a CommandLine with content directory name/handle pairs. +bool SetContentDirectoriesInCommandLine( + std::vector<fuchsia::web::ContentDirectoryProvider> directories, + base::CommandLine* command_line, + base::LaunchOptions* launch_options) { + DCHECK(command_line); + DCHECK(launch_options); + + std::vector<std::string> directory_pairs; + for (size_t i = 0; i < directories.size(); ++i) { + fuchsia::web::ContentDirectoryProvider& directory = directories[i]; + + if (directory.name().find('=') != std::string::npos || + directory.name().find(',') != std::string::npos) { + DLOG(ERROR) << "Invalid character in directory name: " + << directory.name(); + return false; + } + + if (!directory.directory().is_valid()) { + DLOG(ERROR) << "Service directory handle not valid for directory: " + << directory.name(); + return false; + } + + uint32_t directory_handle_id = base::LaunchOptions::AddHandleToTransfer( + &launch_options->handles_to_transfer, + directory.mutable_directory()->TakeChannel().release()); + directory_pairs.emplace_back( + base::StrCat({directory.name().c_str(), "=", + base::NumberToString(directory_handle_id)})); } - struct stat statbuf; - int result = - fstatat(dir_fd.get(), "fuchsia.vulkan.loader.Loader", &statbuf, 0); - *vulkan_supported = result == 0; + command_line->AppendSwitchASCII(kContentDirectories, + base::JoinString(directory_pairs, ",")); return true; } @@ -102,17 +122,15 @@ void ContextProviderImpl::Create( fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory = std::move(*params.mutable_service_directory()); - - // Enable Vulkan if the Vulkan loader service is present in the service - // directory. - bool vulkan_supported = false; - if (!CheckVulkanSupport(service_directory, &vulkan_supported)) { - // TODO(crbug.com/934539): Add type epitaph. + if (!service_directory) { DLOG(WARNING) << "Invalid |service_directory| in CreateContextParams."; + context_request.Close(ZX_ERR_INVALID_ARGS); return; } base::LaunchOptions launch_options; + launch_options.process_name_suffix = ":context"; + service_manager::SandboxPolicyFuchsia sandbox_policy; sandbox_policy.Initialize(service_manager::SANDBOX_TYPE_WEB_CONTEXT); sandbox_policy.SetServiceDirectory(std::move(service_directory)); @@ -178,18 +196,21 @@ void ContextProviderImpl::Create( base::JoinString(handles_ids, ",")); } -#if defined(WEB_ENGINE_ENABLE_VULKAN) - // Enable Vulkan when the Vulkan loader service is included in the service - // directory. - // TODO(https://crbug.com/962617): Enable Vulkan by default and remove this - // hack. - if (vulkan_supported) { - launch_command.AppendSwitchASCII( - "--enable-features", "DefaultEnableOopRasterization,UseSkiaRenderer"); - launch_command.AppendSwitch("--use-vulkan"); - launch_command.AppendSwitchASCII("--use-gl", "stub"); + fuchsia::web::ContextFeatureFlags features = {}; + if (params.has_features()) + features = params.features(); + + bool enable_vulkan = (features & fuchsia::web::ContextFeatureFlags::VULKAN) == + fuchsia::web::ContextFeatureFlags::VULKAN; + + if (enable_vulkan) { + launch_command.AppendSwitch(switches::kUseVulkan); + launch_command.AppendSwitchASCII(switches::kEnableFeatures, + features::kUseSkiaRenderer.name); + launch_command.AppendSwitch(switches::kEnableOopRasterization); + launch_command.AppendSwitchASCII(switches::kUseGL, + gl::kGLImplementationStubName); } -#endif // WEB_ENGINE_ENABLE_VULKAN // Validate embedder-supplied product, and optional version, and pass it to // the Context to include in the UserAgent. @@ -216,6 +237,14 @@ void ContextProviderImpl::Create( return; } + if (params.has_content_directories() && + !SetContentDirectoriesInCommandLine( + std::move(*params.mutable_content_directories()), &launch_command, + &launch_options)) { + context_request.Close(ZX_ERR_INVALID_ARGS); + return; + } + if (launch_for_test_) launch_for_test_.Run(launch_command, launch_options); else diff --git a/chromium/fuchsia/engine/context_provider_impl_unittest.cc b/chromium/fuchsia/engine/context_provider_impl_unittest.cc index 078e765b4e2..6f30b4174d6 100644 --- a/chromium/fuchsia/engine/context_provider_impl_unittest.cc +++ b/chromium/fuchsia/engine/context_provider_impl_unittest.cc @@ -29,9 +29,9 @@ #include "base/fuchsia/file_utils.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/service_directory.h" -#include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/test/multiprocess_test.h" +#include "base/test/task_environment.h" #include "base/test/test_timeouts.h" #include "fuchsia/engine/common.h" #include "fuchsia/engine/fake_context.h" @@ -46,7 +46,9 @@ constexpr char kUrl[] = "chrome://:emorhc"; constexpr char kTitle[] = "Palindrome"; MULTIPROCESS_TEST_MAIN(SpawnContextServer) { - base::MessageLoopForIO message_loop; + base::test::TaskEnvironment task_environment( + base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, + base::test::TaskEnvironment::MainThreadType::IO); base::FilePath data_dir; CHECK(base::PathService::Get(base::DIR_APP_DATA, &data_dir)); @@ -177,7 +179,9 @@ class ContextProviderImplTest : public base::MultiProcessTest { } protected: - base::MessageLoopForIO message_loop_; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, + base::test::TaskEnvironment::MainThreadType::IO}; std::unique_ptr<ContextProviderImpl> provider_; fuchsia::web::ContextProviderPtr provider_ptr_; fidl::BindingSet<fuchsia::web::ContextProvider> bindings_; @@ -326,8 +330,7 @@ static bool WaitUntilJobIsEmpty(zx::unowned_job job, zx::duration timeout) { TEST_F(ContextProviderImplTest, CleansUpContextJobs) { // Replace the default job with one that is guaranteed to be empty. zx::job job; - ASSERT_EQ(base::GetDefaultJob()->duplicate(ZX_RIGHT_SAME_RIGHTS, &job), - ZX_OK); + ASSERT_EQ(zx::job::create(*base::GetDefaultJob(), 0, &job), ZX_OK); base::ScopedDefaultJobForTest empty_default_job(std::move(job)); // Bind to the ContextProvider. diff --git a/chromium/fuchsia/engine/context_provider_main.cc b/chromium/fuchsia/engine/context_provider_main.cc index 5355a817da5..b080857221b 100644 --- a/chromium/fuchsia/engine/context_provider_main.cc +++ b/chromium/fuchsia/engine/context_provider_main.cc @@ -4,26 +4,30 @@ #include "fuchsia/engine/context_provider_main.h" +#include <lib/sys/cpp/component_context.h> +#include <lib/sys/cpp/outgoing_directory.h> + +#include "base/fuchsia/default_context.h" #include "base/fuchsia/scoped_service_binding.h" -#include "base/fuchsia/service_directory.h" #include "base/logging.h" +#include "base/message_loop/message_pump_type.h" #include "base/run_loop.h" #include "base/task/single_thread_task_executor.h" #include "fuchsia/base/lifecycle_impl.h" #include "fuchsia/engine/context_provider_impl.h" int ContextProviderMain() { - base::SingleThreadTaskExecutor main_task_executor( - base::MessagePump::Type::UI); - base::fuchsia::ServiceDirectory* const directory = - base::fuchsia::ServiceDirectory::GetDefault(); + base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI); + sys::OutgoingDirectory* directory = + base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(); ContextProviderImpl context_provider; base::fuchsia::ScopedServiceBinding<fuchsia::web::ContextProvider> binding( directory, &context_provider); + directory->ServeFromStartupInfo(); base::fuchsia::ScopedServiceBinding<fuchsia::web::Debug> debug_binding( - directory->outgoing_directory()->debug_dir(), &context_provider); + directory->debug_dir(), &context_provider); base::RunLoop run_loop; cr_fuchsia::LifecycleImpl lifecycle(directory, run_loop.QuitClosure()); diff --git a/chromium/fuchsia/engine/web_engine_content_client.cc b/chromium/fuchsia/engine/web_engine_content_client.cc index f8caada0a34..41993815873 100644 --- a/chromium/fuchsia/engine/web_engine_content_client.cc +++ b/chromium/fuchsia/engine/web_engine_content_client.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "fuchsia/engine/web_engine_content_client.h" +#include "base/command_line.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -35,3 +36,8 @@ blink::OriginTrialPolicy* WebEngineContentClient::GetOriginTrialPolicy() { NOTIMPLEMENTED_LOG_ONCE(); return nullptr; } + +void WebEngineContentClient::AddAdditionalSchemes(Schemes* schemes) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch(kContentDirectories)) + schemes->standard_schemes.push_back(kFuchsiaContentDirectoryScheme); +} diff --git a/chromium/fuchsia/engine/web_engine_content_client.h b/chromium/fuchsia/engine/web_engine_content_client.h index 820d2ce606b..c2e9b4085a5 100644 --- a/chromium/fuchsia/engine/web_engine_content_client.h +++ b/chromium/fuchsia/engine/web_engine_content_client.h @@ -7,6 +7,7 @@ #include "base/macros.h" #include "content/public/common/content_client.h" +#include "fuchsia/engine/common.h" class WebEngineContentClient : public content::ContentClient { public: @@ -20,6 +21,7 @@ class WebEngineContentClient : public content::ContentClient { base::RefCountedMemory* GetDataResourceBytes(int resource_id) override; gfx::Image& GetNativeImageNamed(int resource_id) override; blink::OriginTrialPolicy* GetOriginTrialPolicy() override; + void AddAdditionalSchemes(Schemes* schemes) override; private: DISALLOW_COPY_AND_ASSIGN(WebEngineContentClient); diff --git a/chromium/fuchsia/engine/web_engine_debug_integration_test.cc b/chromium/fuchsia/engine/web_engine_debug_integration_test.cc index e70c05bb4c5..2168aebefd7 100644 --- a/chromium/fuchsia/engine/web_engine_debug_integration_test.cc +++ b/chromium/fuchsia/engine/web_engine_debug_integration_test.cc @@ -5,17 +5,20 @@ #include <fuchsia/web/cpp/fidl.h> #include <lib/fidl/cpp/binding.h> #include <lib/fidl/cpp/binding_set.h> +#include <lib/sys/cpp/component_context.h> #include "base/files/file_enumerator.h" +#include "base/fuchsia/default_context.h" #include "base/fuchsia/file_utils.h" #include "base/fuchsia/service_directory_client.h" #include "base/macros.h" +#include "base/test/task_environment.h" #include "fuchsia/base/fit_adapter.h" #include "fuchsia/base/frame_test_util.h" #include "fuchsia/base/result_receiver.h" +#include "fuchsia/base/test_devtools_list_fetcher.h" #include "fuchsia/base/test_navigation_listener.h" #include "fuchsia/engine/test_debug_listener.h" -#include "fuchsia/engine/test_devtools_list_fetcher.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "testing/gtest/include/gtest/gtest.h" @@ -33,9 +36,9 @@ class WebEngineDebugIntegrationTest : public testing::Test { ~WebEngineDebugIntegrationTest() override = default; void SetUp() override { - web_context_provider_ = - base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToService<fuchsia::web::ContextProvider>(); + web_context_provider_ = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::web::ContextProvider>(); web_context_provider_.set_error_handler( [](zx_status_t status) { ADD_FAILURE(); }); @@ -93,7 +96,9 @@ class WebEngineDebugIntegrationTest : public testing::Test { fuchsia::web::ContextError::REMOTE_DEBUGGING_PORT_NOT_OPENED); } - base::MessageLoopForIO message_loop_; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, + base::test::TaskEnvironment::MainThreadType::IO}; TestDebugListener dev_tools_listener_; fidl::Binding<fuchsia::web::DevToolsListener> dev_tools_listener_binding_; @@ -151,8 +156,8 @@ TEST_F(WebEngineDebugIntegrationTest, DebugService) { // Test the debug information is correct. dev_tools_listener_.RunUntilNumberOfPortsIs(1u); - base::Value devtools_list = - GetDevToolsListFromPort(*dev_tools_listener_.debug_ports().begin()); + base::Value devtools_list = cr_fuchsia::GetDevToolsListFromPort( + *dev_tools_listener_.debug_ports().begin()); ASSERT_TRUE(devtools_list.is_list()); EXPECT_EQ(devtools_list.GetList().size(), 1u); @@ -179,7 +184,7 @@ TEST_F(WebEngineDebugIntegrationTest, MultipleDebugClients) { dev_tools_listener_.RunUntilNumberOfPortsIs(1u); uint16_t port1 = *dev_tools_listener_.debug_ports().begin(); - base::Value devtools_list1 = GetDevToolsListFromPort(port1); + base::Value devtools_list1 = cr_fuchsia::GetDevToolsListFromPort(port1); ASSERT_TRUE(devtools_list1.is_list()); EXPECT_EQ(devtools_list1.GetList().size(), 1u); @@ -213,7 +218,7 @@ TEST_F(WebEngineDebugIntegrationTest, MultipleDebugClients) { ASSERT_NE(dev_tools_listener_.debug_ports().find(port2), dev_tools_listener_.debug_ports().end()); - base::Value devtools_list2 = GetDevToolsListFromPort(port2); + base::Value devtools_list2 = cr_fuchsia::GetDevToolsListFromPort(port2); ASSERT_TRUE(devtools_list2.is_list()); EXPECT_EQ(devtools_list2.GetList().size(), 1u); diff --git a/chromium/fuchsia/engine/web_engine_integration_test.cc b/chromium/fuchsia/engine/web_engine_integration_test.cc index d2aa6b10f89..a1ad9b41374 100644 --- a/chromium/fuchsia/engine/web_engine_integration_test.cc +++ b/chromium/fuchsia/engine/web_engine_integration_test.cc @@ -3,18 +3,22 @@ // found in the LICENSE file. #include <fuchsia/web/cpp/fidl.h> +#include <lib/fdio/directory.h> #include <lib/fidl/cpp/binding.h> +#include <lib/sys/cpp/component_context.h> +#include "base/fuchsia/default_context.h" #include "base/fuchsia/file_utils.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/service_directory_client.h" #include "base/macros.h" -#include "base/test/scoped_task_environment.h" +#include "base/path_service.h" +#include "base/test/task_environment.h" #include "fuchsia/base/fit_adapter.h" #include "fuchsia/base/frame_test_util.h" #include "fuchsia/base/result_receiver.h" +#include "fuchsia/base/test_devtools_list_fetcher.h" #include "fuchsia/base/test_navigation_listener.h" -#include "fuchsia/engine/test_devtools_list_fetcher.h" #include "net/http/http_request_headers.h" #include "net/test/embedded_test_server/default_handlers.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -33,14 +37,13 @@ constexpr char kInvalidUserAgentVersion[] = "dev/12345"; class WebEngineIntegrationTest : public testing::Test { public: WebEngineIntegrationTest() - : task_environment_( - base::test::ScopedTaskEnvironment::MainThreadType::IO) {} + : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {} ~WebEngineIntegrationTest() override = default; void SetUp() override { - web_context_provider_ = - base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToService<fuchsia::web::ContextProvider>(); + web_context_provider_ = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::web::ContextProvider>(); web_context_provider_.set_error_handler( [](zx_status_t status) { ADD_FAILURE(); }); @@ -105,8 +108,19 @@ class WebEngineIntegrationTest : public testing::Test { return value ? value->GetString() : std::string(); } + fidl::InterfaceHandle<fuchsia::io::Directory> OpenDirectoryHandle( + const base::FilePath& path) { + fidl::InterfaceHandle<fuchsia::io::Directory> directory_channel; + zx_status_t status = fdio_open( + path.value().c_str(), + fuchsia::io::OPEN_FLAG_DIRECTORY | fuchsia::io::OPEN_RIGHT_READABLE, + directory_channel.NewRequest().TakeChannel().release()); + ZX_DCHECK(status == ZX_OK, status) << "fdio_open"; + return directory_channel; + } + protected: - const base::test::ScopedTaskEnvironment task_environment_; + const base::test::TaskEnvironment task_environment_; fuchsia::web::ContextProviderPtr web_context_provider_; @@ -240,7 +254,8 @@ TEST_F(WebEngineIntegrationTest, RemoteDebuggingPort) { nav_controller.get(), fuchsia::web::LoadUrlParams(), url.spec())); navigation_listener.RunUntilUrlEquals(url); - base::Value devtools_list = GetDevToolsListFromPort(remote_debugging_port); + base::Value devtools_list = + cr_fuchsia::GetDevToolsListFromPort(remote_debugging_port); ASSERT_TRUE(devtools_list.is_list()); EXPECT_EQ(devtools_list.GetList().size(), 1u); @@ -248,3 +263,37 @@ TEST_F(WebEngineIntegrationTest, RemoteDebuggingPort) { ASSERT_TRUE(devtools_url->is_string()); EXPECT_EQ(devtools_url->GetString(), url); } + +// Navigates to a resource served under the "testdata" ContentDirectory. +TEST_F(WebEngineIntegrationTest, ContentDirectoryProvider) { + const GURL kUrl("fuchsia-dir://testdata/title1.html"); + constexpr char kTitle[] = "title 1"; + + fuchsia::web::CreateContextParams create_params = DefaultContextParams(); + + fuchsia::web::ContentDirectoryProvider provider; + provider.set_name("testdata"); + base::FilePath pkg_path; + CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path)); + provider.set_directory( + OpenDirectoryHandle(pkg_path.AppendASCII("fuchsia/engine/test/data"))); + + create_params.mutable_content_directories()->emplace_back( + std::move(provider)); + + CreateContextAndFrame(std::move(create_params)); + + fuchsia::web::NavigationControllerPtr controller; + frame_->GetNavigationController(controller.NewRequest()); + controller.set_error_handler([](zx_status_t status) { ADD_FAILURE(); }); + + // Navigate to test1.html and verify that the resource was correctly + // downloaded and interpreted by inspecting the document title. + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec())); + cr_fuchsia::TestNavigationListener navigation_listener; + fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding( + &navigation_listener); + frame_->SetNavigationEventListener(listener_binding.NewBinding()); + navigation_listener.RunUntilUrlAndTitleEquals(kUrl, kTitle); +} diff --git a/chromium/fuchsia/engine/web_engine_integration_tests.cmx b/chromium/fuchsia/engine/web_engine_integration_tests.cmx index 3c0c9ca6645..21096856cf8 100644 --- a/chromium/fuchsia/engine/web_engine_integration_tests.cmx +++ b/chromium/fuchsia/engine/web_engine_integration_tests.cmx @@ -1,10 +1,10 @@ { "sandbox": { "features": [ - "isolated-persistent-storage", "deprecated-shell", - "system-temp", - "deprecated-ambient-replace-as-executable" + "deprecated-ambient-replace-as-executable", + "isolated-persistent-storage", + "isolated-temp" ], "services": [ "fuchsia.deprecatedtimezone.Timezone", @@ -12,7 +12,6 @@ "fuchsia.fonts.Provider", "fuchsia.logger.LogSink", "fuchsia.net.NameLookup", - "fuchsia.net.SocketProvider", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", "fuchsia.process.Launcher", diff --git a/chromium/fuchsia/engine/web_engine_main_delegate.cc b/chromium/fuchsia/engine/web_engine_main_delegate.cc index e88300dc302..c796b92beff 100644 --- a/chromium/fuchsia/engine/web_engine_main_delegate.cc +++ b/chromium/fuchsia/engine/web_engine_main_delegate.cc @@ -24,10 +24,14 @@ WebEngineMainDelegate* g_current_web_engine_main_delegate = nullptr; void InitLoggingFromCommandLine(const base::CommandLine& command_line) { logging::LoggingSettings settings; - settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + if (command_line.GetSwitchValueASCII(switches::kEnableLogging) == "stderr") { + settings.logging_dest = logging::LOG_TO_STDERR; + } else { + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + } if (command_line.HasSwitch(switches::kLogFile)) { settings.logging_dest |= logging::LOG_TO_FILE; - settings.log_file = + settings.log_file_path = command_line.GetSwitchValueASCII(switches::kLogFile).c_str(); settings.delete_old = logging::DELETE_OLD_LOG_FILE; } diff --git a/chromium/fuchsia/fidl/cast/application_config.fidl b/chromium/fuchsia/fidl/cast/application_config.fidl index 070bb9577a3..6e7505b8ca2 100644 --- a/chromium/fuchsia/fidl/cast/application_config.fidl +++ b/chromium/fuchsia/fidl/cast/application_config.fidl @@ -21,6 +21,10 @@ table ApplicationConfig { /// If false, then touch input is disabled. /// If unset, then the caller is allowed to enable or disable input. 4: bool touch_enabled_policy; + + // If true, enable remote debugging for this application. + // if false or unset, remote debugging is disabled for this application. + 5: bool enable_remote_debugging; }; /// Service interface for working with application configurations. diff --git a/chromium/fuchsia/fidl/cast/application_controller.fidl b/chromium/fuchsia/fidl/cast/application_controller.fidl new file mode 100644 index 00000000000..17bf0313c00 --- /dev/null +++ b/chromium/fuchsia/fidl/cast/application_controller.fidl @@ -0,0 +1,19 @@ +// Copyright 2019 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. + +library chromium.cast; + +[Discoverable] +protocol ApplicationControllerReceiver { + /// Used by the Agent to receive a controller from the Cast Runner. + /// Can only be called at most one time for the lifetime of the Component. + SetApplicationController(ApplicationController controller); +}; + +/// Allows clients to access and modify certain aspects of the Cast receiver +/// application runtime. +protocol ApplicationController { + /// Enables or disables touch event processing. + SetTouchInputEnabled(bool enable); +}; diff --git a/chromium/fuchsia/fidl/cast/queryable_data.fidl b/chromium/fuchsia/fidl/cast/queryable_data.fidl deleted file mode 100644 index c7148a13965..00000000000 --- a/chromium/fuchsia/fidl/cast/queryable_data.fidl +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 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. - -library chromium.cast; - -struct QueryableDataEntry { - string key; - string json_value; -}; - -/// Interface for serving configuration- and environmental-related values -/// to CastRunner instances. -[Discoverable] -protocol QueryableData { - /// Gets a list of changed entries since the last call to GetChangedEntries, - /// delaying execution of the the pending callback until a change becomes - /// available. The initial call will return all entries managed by the - /// QueryableData service. - GetChangedEntries() -> (vector<QueryableDataEntry> values); -}; - diff --git a/chromium/fuchsia/fidl/cast/url_request_rewriter.fidl b/chromium/fuchsia/fidl/cast/url_request_rewriter.fidl new file mode 100644 index 00000000000..45fd69e15ef --- /dev/null +++ b/chromium/fuchsia/fidl/cast/url_request_rewriter.fidl @@ -0,0 +1,17 @@ +// Copyright 2019 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. + +library chromium.cast; + +using fuchsia.web; + +/// Provides URL request rewriting rules from the Agent. +[Discoverable] +protocol UrlRequestRewriteRulesProvider { + /// Returns a set of URL request rewriting rules. The first call must always + /// return immediately with a potentially empty set of rules. On subsequent + /// calls, the callback will only be invoked when the rules have changed. + GetUrlRequestRewriteRules() -> + (vector<fuchsia.web.UrlRequestRewriteRule> rules); +}; diff --git a/chromium/fuchsia/http/BUILD.gn b/chromium/fuchsia/http/BUILD.gn index 624ff0ad7a3..27ec91039bf 100644 --- a/chromium/fuchsia/http/BUILD.gn +++ b/chromium/fuchsia/http/BUILD.gn @@ -20,9 +20,11 @@ source_set("core") { ] public_deps = [ "//base:base", + "//fuchsia/base", "//net:net", "//third_party/fuchsia-sdk/sdk:net_oldhttp", "//third_party/fuchsia-sdk/sdk:sys", + "//third_party/fuchsia-sdk/sdk:sys_cpp", ] visibility = [ ":*" ] } diff --git a/chromium/fuchsia/http/http.cmx b/chromium/fuchsia/http/http.cmx index b4cde0cf4b4..389f2b56a08 100644 --- a/chromium/fuchsia/http/http.cmx +++ b/chromium/fuchsia/http/http.cmx @@ -9,7 +9,6 @@ "fuchsia.device.NameProvider", "fuchsia.logger.LogSink", "fuchsia.net.NameLookup", - "fuchsia.net.SocketProvider", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider" ] diff --git a/chromium/fuchsia/http/http_service_main.cc b/chromium/fuchsia/http/http_service_main.cc index 81fbfc742d3..359a15bb84e 100644 --- a/chromium/fuchsia/http/http_service_main.cc +++ b/chromium/fuchsia/http/http_service_main.cc @@ -2,30 +2,33 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <lib/sys/cpp/component_context.h> + #include "base/at_exit.h" #include "base/bind.h" #include "base/command_line.h" +#include "base/fuchsia/default_context.h" #include "base/fuchsia/scoped_service_binding.h" -#include "base/fuchsia/service_directory.h" +#include "base/message_loop/message_pump_type.h" #include "base/run_loop.h" #include "base/task/single_thread_task_executor.h" -#include "base/task/thread_pool/thread_pool.h" +#include "base/task/thread_pool/thread_pool_instance.h" #include "fuchsia/http/http_service_impl.h" int main(int argc, char** argv) { // Instantiate various global structures. base::ThreadPoolInstance::CreateAndStartWithDefaultParams("HTTP Service"); base::CommandLine::Init(argc, argv); - base::SingleThreadTaskExecutor io_task_executor(base::MessagePump::Type::IO); + base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO); base::AtExitManager exit_manager; - // Bind the parent-supplied ServiceDirectory-request to a directory and + // Bind the parent-supplied OutgoingDirectory-request to a directory and // publish the HTTP service into it. - base::fuchsia::ServiceDirectory* directory = - base::fuchsia::ServiceDirectory::GetDefault(); + sys::OutgoingDirectory* outgoing_directory = + base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(); HttpServiceImpl http_service; base::fuchsia::ScopedServiceBinding<::fuchsia::net::oldhttp::HttpService> - binding(directory, &http_service); + binding(outgoing_directory, &http_service); base::RunLoop run_loop; diff --git a/chromium/fuchsia/http/http_service_unittest.cc b/chromium/fuchsia/http/http_service_unittest.cc index c1b52fc66a9..628e2ef5f2d 100644 --- a/chromium/fuchsia/http/http_service_unittest.cc +++ b/chromium/fuchsia/http/http_service_unittest.cc @@ -8,7 +8,7 @@ #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/service_directory.h" #include "base/run_loop.h" -#include "base/test/scoped_task_environment.h" +#include "base/test/task_environment.h" #include "fuchsia/http/http_service_impl.h" #include "fuchsia/http/url_loader_impl.h" #include "net/base/net_errors.h" @@ -31,8 +31,7 @@ using ResponseHeaders = std::multimap<std::string, std::string>; class HttpServiceTest : public ::testing::Test { public: HttpServiceTest() - : task_environment_( - base::test::ScopedTaskEnvironment::MainThreadType::IO), + : task_environment_(base::test::TaskEnvironment::MainThreadType::IO), binding_(&http_service_server_) { // Initialize the test server. test_server_.AddDefaultHandlers( @@ -41,7 +40,7 @@ class HttpServiceTest : public ::testing::Test { } protected: - base::test::ScopedTaskEnvironment task_environment_; + base::test::TaskEnvironment task_environment_; void SetUp() override { ASSERT_TRUE(test_server_.Start()); @@ -203,7 +202,8 @@ void CheckResponseBuffer(const oldhttp::URLResponse& response, void CheckResponseHeaders(const oldhttp::URLResponse& response, ResponseHeaders* expected_headers) { - for (auto& header : response.headers.get()) { + ASSERT_TRUE(response.headers.has_value()); + for (auto& header : response.headers.value()) { const std::string header_name = header.name.data(); const std::string header_value = header.value.data(); auto iter = std::find_if(expected_headers->begin(), expected_headers->end(), diff --git a/chromium/fuchsia/http/url_loader_impl.cc b/chromium/fuchsia/http/url_loader_impl.cc index f745b9c240e..fdcdf92aa93 100644 --- a/chromium/fuchsia/http/url_loader_impl.cc +++ b/chromium/fuchsia/http/url_loader_impl.cc @@ -7,6 +7,7 @@ #include "base/fuchsia/fuchsia_logging.h" #include "base/message_loop/message_loop_current.h" #include "base/task/post_task.h" +#include "fuchsia/base/mem_buffer_util.h" #include "net/base/chunked_upload_data_stream.h" #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" @@ -30,20 +31,9 @@ oldhttp::URLBodyPtr CreateURLBodyFromBuffer(net::GrowableIOBuffer* buffer) { // The response buffer size is exactly the offset. size_t total_size = buffer->offset(); - ::fuchsia::mem::Buffer mem_buffer; - mem_buffer.size = total_size; - zx_status_t result = zx::vmo::create(total_size, 0, &mem_buffer.vmo); - if (result != ZX_OK) { - ZX_DLOG(WARNING, result) << "zx_vmo_create"; - return nullptr; - } - - result = mem_buffer.vmo.write(buffer->StartOfBuffer(), 0, total_size); - if (result != ZX_OK) { - ZX_DLOG(WARNING, result) << "zx_vmo_write"; - return nullptr; - } - body->set_buffer(std::move(mem_buffer)); + body->set_buffer(cr_fuchsia::MemBufferFromString( + base::StringPiece(buffer->StartOfBuffer(), total_size), + "cr-http-url-body")); return body; } @@ -420,12 +410,13 @@ oldhttp::URLResponse URLLoaderImpl::BuildResponse(int net_error) { size_t iter = 0; std::string header_name; std::string header_value; + response.headers.emplace(); while (response_headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) { oldhttp::HttpHeader header; header.name = header_name; header.value = header_value; - response.headers.push_back(header); + response.headers->push_back(header); } } diff --git a/chromium/fuchsia/mojo/example.typemap b/chromium/fuchsia/mojo/example.typemap deleted file mode 100644 index b1d15125e4c..00000000000 --- a/chromium/fuchsia/mojo/example.typemap +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2019 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. - -mojom = "//fuchsia/mojo/example.mojom" -os_whitelist = [ "fuchsia" ] -public_headers = [ "base/fuchsia/testfidl/cpp/fidl.h" ] -traits_headers = [ "//fuchsia/mojo/test_interface_request_mojom_traits.h" ] -sources = [ - "//fuchsia/mojo/test_interface_request_mojom_traits.h", -] -public_deps = [ - "//base:testfidl", - "//fuchsia/mojo:traits", -] -type_mappings = [ "fuchsia.test.mojom.TestInterfaceRequest=fidl::InterfaceRequest<base::fuchsia::testfidl::TestInterface>[move_only]" ] diff --git a/chromium/fuchsia/mojo/BUILD.gn b/chromium/fuchsia/mojom/BUILD.gn index c79710377c7..e8c4919e507 100644 --- a/chromium/fuchsia/mojo/BUILD.gn +++ b/chromium/fuchsia/mojom/BUILD.gn @@ -34,6 +34,7 @@ test("fuchsia_mojo_unittests") { deps = [ ":example_interfaces", "//base:testfidl", + "//base/test:test_support", "//mojo/core/test:run_all_unittests", "//mojo/public/cpp/test_support:test_utils", "//testing/gtest", diff --git a/chromium/fuchsia/mojo/DEPS b/chromium/fuchsia/mojom/DEPS index ef8ad28d9d4..ef8ad28d9d4 100644 --- a/chromium/fuchsia/mojo/DEPS +++ b/chromium/fuchsia/mojom/DEPS diff --git a/chromium/fuchsia/mojo/OWNERS b/chromium/fuchsia/mojom/OWNERS index ae29a36aac8..ae29a36aac8 100644 --- a/chromium/fuchsia/mojo/OWNERS +++ b/chromium/fuchsia/mojom/OWNERS diff --git a/chromium/fuchsia/mojo/example.mojom b/chromium/fuchsia/mojom/example.mojom index a27c956182e..a27c956182e 100644 --- a/chromium/fuchsia/mojo/example.mojom +++ b/chromium/fuchsia/mojom/example.mojom diff --git a/chromium/fuchsia/mojom/example.typemap b/chromium/fuchsia/mojom/example.typemap new file mode 100644 index 00000000000..7e2aaac3df0 --- /dev/null +++ b/chromium/fuchsia/mojom/example.typemap @@ -0,0 +1,16 @@ +# Copyright 2019 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. + +mojom = "//fuchsia/mojom/example.mojom" +os_whitelist = [ "fuchsia" ] +public_headers = [ "base/fuchsia/testfidl/cpp/fidl.h" ] +traits_headers = [ "//fuchsia/mojom/test_interface_request_mojom_traits.h" ] +sources = [ + "//fuchsia/mojom/test_interface_request_mojom_traits.h", +] +public_deps = [ + "//base:testfidl", + "//fuchsia/mojom:traits", +] +type_mappings = [ "fuchsia.test.mojom.TestInterfaceRequest=::fidl::InterfaceRequest<::base::fuchsia::testfidl::TestInterface>[move_only]" ] diff --git a/chromium/fuchsia/mojo/fidl_interface_request_mojom_traits.h b/chromium/fuchsia/mojom/fidl_interface_request_mojom_traits.h index 2c38438aaeb..d40f369930a 100644 --- a/chromium/fuchsia/mojo/fidl_interface_request_mojom_traits.h +++ b/chromium/fuchsia/mojom/fidl_interface_request_mojom_traits.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FUCHSIA_MOJO_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_ -#define FUCHSIA_MOJO_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_ +#ifndef FUCHSIA_MOJOM_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_ +#define FUCHSIA_MOJOM_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_ #include <lib/fidl/cpp/interface_request.h> @@ -40,4 +40,4 @@ struct FidlInterfaceRequestStructTraits { } // namespace mojo -#endif // FUCHSIA_MOJO_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_ +#endif // FUCHSIA_MOJOM_FIDL_INTERFACE_REQUEST_MOJOM_TRAITS_H_ diff --git a/chromium/fuchsia/mojo/fidl_interface_request_mojom_traits_unittest.cc b/chromium/fuchsia/mojom/fidl_interface_request_mojom_traits_unittest.cc index 8e34582c25e..a9d5772de15 100644 --- a/chromium/fuchsia/mojo/fidl_interface_request_mojom_traits_unittest.cc +++ b/chromium/fuchsia/mojom/fidl_interface_request_mojom_traits_unittest.cc @@ -3,9 +3,9 @@ // found in the LICENSE file. #include "base/fuchsia/testfidl/cpp/fidl.h" -#include "base/message_loop/message_loop.h" -#include "fuchsia/mojo/example.mojom.h" -#include "fuchsia/mojo/test_interface_request_mojom_traits.h" +#include "base/test/task_environment.h" +#include "fuchsia/mojom/example.mojom.h" +#include "fuchsia/mojom/test_interface_request_mojom_traits.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -15,7 +15,9 @@ using base::fuchsia::testfidl::TestInterface; using base::fuchsia::testfidl::TestInterfacePtr; TEST(InterfaceRequestStructTraitsTest, Serialization) { - base::MessageLoopForIO message_loop; + base::test::TaskEnvironment task_environment( + base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, + base::test::TaskEnvironment::MainThreadType::IO); TestInterfacePtr test_ptr; fidl::InterfaceRequest<TestInterface> input_request = test_ptr.NewRequest(); fidl::InterfaceRequest<TestInterface> output_request; diff --git a/chromium/fuchsia/mojo/test_interface_request_mojom_traits.h b/chromium/fuchsia/mojom/test_interface_request_mojom_traits.h index 0f0aeb66858..f9be179d816 100644 --- a/chromium/fuchsia/mojo/test_interface_request_mojom_traits.h +++ b/chromium/fuchsia/mojom/test_interface_request_mojom_traits.h @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FUCHSIA_MOJO_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_ -#define FUCHSIA_MOJO_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_ +#ifndef FUCHSIA_MOJOM_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_ +#define FUCHSIA_MOJOM_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_ -#include "fuchsia/mojo/fidl_interface_request_mojom_traits.h" +#include "fuchsia/mojom/fidl_interface_request_mojom_traits.h" namespace mojo { @@ -19,4 +19,4 @@ struct StructTraits< } // namespace mojo -#endif // FUCHSIA_MOJO_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_ +#endif // FUCHSIA_MOJOM_TEST_INTERFACE_REQUEST_MOJOM_TRAITS_H_ diff --git a/chromium/fuchsia/mojo/test_typemaps.gni b/chromium/fuchsia/mojom/test_typemaps.gni index 0fb662f47d8..521c8d4f8fb 100644 --- a/chromium/fuchsia/mojo/test_typemaps.gni +++ b/chromium/fuchsia/mojom/test_typemaps.gni @@ -2,4 +2,4 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -typemaps = [ "//fuchsia/mojo/example.typemap" ] +typemaps = [ "//fuchsia/mojom/example.typemap" ] diff --git a/chromium/fuchsia/runners/BUILD.gn b/chromium/fuchsia/runners/BUILD.gn index e8916eeafde..ed8fb9e209f 100644 --- a/chromium/fuchsia/runners/BUILD.gn +++ b/chromium/fuchsia/runners/BUILD.gn @@ -39,6 +39,7 @@ source_set("common") { ] public_deps = [ "//third_party/fuchsia-sdk/sdk:sys", + "//third_party/fuchsia-sdk/sdk:sys_cpp", "//third_party/fuchsia-sdk/sdk:web", ] visibility = [ ":*" ] @@ -48,6 +49,8 @@ source_set("cast_runner_core") { sources = [ "cast/api_bindings_client.cc", "cast/api_bindings_client.h", + "cast/application_controller_impl.cc", + "cast/application_controller_impl.h", "cast/cast_component.cc", "cast/cast_component.h", "cast/cast_platform_bindings_ids.h", @@ -70,6 +73,7 @@ source_set("cast_runner_core") { "//fuchsia/base", "//fuchsia/base:modular", "//third_party/fuchsia-sdk/sdk:modular", + "//third_party/fuchsia-sdk/sdk:sys_cpp", "//url", ] public_deps = [ @@ -128,6 +132,24 @@ source_set("cast_runner_test_core") { visibility = [ ":*" ] } +test("cast_runner_unittests") { + sources = [ + "cast/application_controller_impl_unittest.cc", + ] + deps = [ + ":cast_runner_core", + ":cast_runner_test_core", + "//base", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//fuchsia/base:test_support", + "//net:test_support", + "//testing/gmock", + "//testing/gtest", + "//third_party/fuchsia-sdk/sdk:web", + ] +} + test("cast_runner_integration_tests") { sources = [ "cast/cast_runner_integration_test.cc", @@ -143,6 +165,7 @@ test("cast_runner_integration_tests") { "//fuchsia/base:test_support", "//net:test_support", "//testing/gtest", + "//third_party/fuchsia-sdk/sdk:modular", ] package_deps = [ [ "//fuchsia/engine:web_engine", diff --git a/chromium/fuchsia/runners/cast/api_bindings_client_browsertest.cc b/chromium/fuchsia/runners/cast/api_bindings_client_browsertest.cc index 947886f1ca6..3cbb79573ac 100644 --- a/chromium/fuchsia/runners/cast/api_bindings_client_browsertest.cc +++ b/chromium/fuchsia/runners/cast/api_bindings_client_browsertest.cc @@ -77,7 +77,8 @@ IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) { std::vector<chromium::cast::ApiBinding> binding_list; chromium::cast::ApiBinding echo_binding; echo_binding.set_before_load_script(cr_fuchsia::MemBufferFromString( - "window.echo = cast.__platform__.PortConnector.bind('echoService');")); + "window.echo = cast.__platform__.PortConnector.bind('echoService');", + "test")); binding_list.emplace_back(std::move(echo_binding)); api_service_.set_bindings(std::move(binding_list)); StartClient(); @@ -92,7 +93,7 @@ IN_PROC_BROWSER_TEST_F(ApiBindingsClientTest, EndToEnd) { api_service_.RunUntilMessagePortReceived("echoService").Bind(); fuchsia::web::WebMessage message; - message.set_data(cr_fuchsia::MemBufferFromString("ping")); + message.set_data(cr_fuchsia::MemBufferFromString("ping", "ping-msg")); port->PostMessage(std::move(message), [](fuchsia::web::MessagePort_PostMessage_Result result) { EXPECT_TRUE(result.is_response()); diff --git a/chromium/fuchsia/runners/cast/application_controller_impl.cc b/chromium/fuchsia/runners/cast/application_controller_impl.cc new file mode 100644 index 00000000000..d613247be1e --- /dev/null +++ b/chromium/fuchsia/runners/cast/application_controller_impl.cc @@ -0,0 +1,31 @@ +// Copyright 2019 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 "fuchsia/runners/cast/application_controller_impl.h" + +#include "base/fuchsia/fuchsia_logging.h" +#include "base/logging.h" + +ApplicationControllerImpl::ApplicationControllerImpl( + fuchsia::web::Frame* frame, + fidl::InterfaceHandle<chromium::cast::ApplicationControllerReceiver> + receiver) + : binding_(this), frame_(frame) { + DCHECK(receiver); + DCHECK(frame_); + + receiver.Bind()->SetApplicationController(binding_.NewBinding()); + + binding_.set_error_handler([](zx_status_t status) { + if (status != ZX_ERR_PEER_CLOSED && status != ZX_ERR_CANCELED) { + ZX_LOG(WARNING, status) << "Application bindings connection dropped."; + } + }); +} + +ApplicationControllerImpl::~ApplicationControllerImpl() = default; + +void ApplicationControllerImpl::SetTouchInputEnabled(bool enable) { + frame_->SetEnableInput(enable); +} diff --git a/chromium/fuchsia/runners/cast/application_controller_impl.h b/chromium/fuchsia/runners/cast/application_controller_impl.h new file mode 100644 index 00000000000..766e274a075 --- /dev/null +++ b/chromium/fuchsia/runners/cast/application_controller_impl.h @@ -0,0 +1,32 @@ +// Copyright 2019 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 FUCHSIA_RUNNERS_CAST_APPLICATION_CONTROLLER_IMPL_H_ +#define FUCHSIA_RUNNERS_CAST_APPLICATION_CONTROLLER_IMPL_H_ + +#include <fuchsia/web/cpp/fidl.h> +#include <lib/fidl/cpp/binding.h> + +#include "base/macros.h" +#include "fuchsia/fidl/chromium/cast/cpp/fidl.h" + +class ApplicationControllerImpl : public chromium::cast::ApplicationController { + public: + ApplicationControllerImpl( + fuchsia::web::Frame* frame, + fidl::InterfaceHandle<chromium::cast::ApplicationControllerReceiver> + receiver); + ~ApplicationControllerImpl() final; + + protected: + void SetTouchInputEnabled(bool enable) override; + + private: + fidl::Binding<chromium::cast::ApplicationController> binding_; + fuchsia::web::Frame* const frame_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationControllerImpl); +}; + +#endif // FUCHSIA_RUNNERS_CAST_APPLICATION_CONTROLLER_IMPL_H_ diff --git a/chromium/fuchsia/runners/cast/application_controller_impl_unittest.cc b/chromium/fuchsia/runners/cast/application_controller_impl_unittest.cc new file mode 100644 index 00000000000..8569fc616a8 --- /dev/null +++ b/chromium/fuchsia/runners/cast/application_controller_impl_unittest.cc @@ -0,0 +1,91 @@ +// Copyright 2019 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 <fuchsia/web/cpp/fidl.h> +#include <fuchsia/web/cpp/fidl_test_base.h> +#include <lib/fidl/cpp/binding.h> +#include <string> +#include <utility> + +#include "base/logging.h" +#include "base/test/bind_test_util.h" +#include "base/test/task_environment.h" +#include "base/test/test_timeouts.h" +#include "fuchsia/base/fit_adapter.h" +#include "fuchsia/fidl/chromium/cast/cpp/fidl.h" +#include "fuchsia/runners/cast/application_controller_impl.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::InvokeWithoutArgs; + +namespace { + +class MockFrame : public fuchsia::web::testing::Frame_TestBase { + public: + void NotImplemented_(const std::string& name) final { + LOG(FATAL) << "No mock defined for " << name; + } + + MOCK_METHOD1(SetEnableInput, void(bool)); +}; + +class ApplicationControllerImplTest + : public chromium::cast::ApplicationControllerReceiver, + public testing::Test { + public: + ApplicationControllerImplTest() + : run_timeout_(TestTimeouts::action_timeout(), + base::MakeExpectedNotRunClosure(FROM_HERE)), + application_receiver_binding_(this), + application_(&frame_, application_receiver_binding_.NewBinding()) { + base::RunLoop run_loop; + wait_for_controller_callback_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + ~ApplicationControllerImplTest() override = default; + + protected: + // chromium::cast::ApplicationReceiver implementation. + void SetApplicationController( + fidl::InterfaceHandle<chromium::cast::ApplicationController> application) + final { + DCHECK(wait_for_controller_callback_); + + application_ptr_ = application.Bind(); + std::move(wait_for_controller_callback_).Run(); + } + + const base::RunLoop::ScopedRunTimeoutForTest run_timeout_; + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; + + MockFrame frame_; + fidl::Binding<chromium::cast::ApplicationControllerReceiver> + application_receiver_binding_; + + chromium::cast::ApplicationControllerPtr application_ptr_; + ApplicationControllerImpl application_; + base::OnceClosure wait_for_controller_callback_; + + private: + DISALLOW_COPY_AND_ASSIGN(ApplicationControllerImplTest); +}; + +// Verifies that SetTouchInputEnabled() calls the Frame API correctly. +TEST_F(ApplicationControllerImplTest, SetEnableInput) { + base::RunLoop run_loop; + + EXPECT_CALL(frame_, SetEnableInput(true)).Times(2); + EXPECT_CALL(frame_, SetEnableInput(false)) + .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); })); + + application_ptr_->SetTouchInputEnabled(true); + application_ptr_->SetTouchInputEnabled(true); + application_ptr_->SetTouchInputEnabled(false); + run_loop.Run(); +} + +} // namespace diff --git a/chromium/fuchsia/runners/cast/cast_component.cc b/chromium/fuchsia/runners/cast/cast_component.cc index 03a0067dbd7..9aed46f6045 100644 --- a/chromium/fuchsia/runners/cast/cast_component.cc +++ b/chromium/fuchsia/runners/cast/cast_component.cc @@ -38,21 +38,22 @@ TouchInputPolicy TouchInputPolicyFromApplicationConfig( } // namespace -CastComponent::CastComponent( - CastRunner* runner, - chromium::cast::ApplicationConfig application_config, - std::unique_ptr<ApiBindingsClient> api_bindings_client, - std::unique_ptr<base::fuchsia::StartupContext> context, - fidl::InterfaceRequest<fuchsia::sys::ComponentController> - controller_request, - std::unique_ptr<cr_fuchsia::AgentManager> agent_manager) - : WebComponent(runner, std::move(context), std::move(controller_request)), - agent_manager_(std::move(agent_manager)), - application_config_(std::move(application_config)), +CastComponent::CastComponentParams::CastComponentParams() = default; +CastComponent::CastComponentParams::CastComponentParams(CastComponentParams&&) = + default; +CastComponent::CastComponentParams::~CastComponentParams() = default; + +CastComponent::CastComponent(CastRunner* runner, + CastComponent::CastComponentParams params) + : WebComponent(runner, + std::move(params.startup_context), + std::move(params.controller_request)), + agent_manager_(std::move(params.agent_manager)), + application_config_(std::move(params.app_config)), touch_input_policy_( TouchInputPolicyFromApplicationConfig(application_config_)), connector_(frame()), - api_bindings_client_(std::move(api_bindings_client)), + api_bindings_client_(std::move(params.api_bindings_client)), navigation_listener_binding_(this) { base::AutoReset<bool> constructor_active_reset(&constructor_active_, true); @@ -65,6 +66,11 @@ CastComponent::CastComponent( kBindingsFailureExitCode, fuchsia::sys::TerminationReason::INTERNAL_ERROR)); + application_controller_ = std::make_unique<ApplicationControllerImpl>( + frame(), agent_manager_->ConnectToAgentService< + chromium::cast::ApplicationControllerReceiver>( + CastRunner::kAgentComponentUrl)); + InitializeCastPlatformBindings(); } diff --git a/chromium/fuchsia/runners/cast/cast_component.h b/chromium/fuchsia/runners/cast/cast_component.h index a65f3b8d2d8..9b1fe7d597a 100644 --- a/chromium/fuchsia/runners/cast/cast_component.h +++ b/chromium/fuchsia/runners/cast/cast_component.h @@ -9,8 +9,11 @@ #include <memory> #include "base/fuchsia/service_directory.h" +#include "base/fuchsia/startup_context.h" +#include "base/optional.h" #include "fuchsia/base/agent_manager.h" #include "fuchsia/runners/cast/api_bindings_client.h" +#include "fuchsia/runners/cast/application_controller_impl.h" #include "fuchsia/runners/cast/named_message_port_connector.h" #include "fuchsia/runners/cast/touch_input_bindings.h" #include "fuchsia/runners/common/web_component.h" @@ -21,13 +24,23 @@ class CastRunner; class CastComponent : public WebComponent, public fuchsia::web::NavigationEventListener { public: - CastComponent(CastRunner* runner, - chromium::cast::ApplicationConfig application_config, - std::unique_ptr<ApiBindingsClient> bindings_manager, - std::unique_ptr<base::fuchsia::StartupContext> startup_context, - fidl::InterfaceRequest<fuchsia::sys::ComponentController> - controller_request, - std::unique_ptr<cr_fuchsia::AgentManager> agent_manager); + struct CastComponentParams { + CastComponentParams(); + CastComponentParams(CastComponentParams&&); + ~CastComponentParams(); + + chromium::cast::ApplicationConfigManagerPtr app_config_manager; + std::unique_ptr<base::fuchsia::StartupContext> startup_context; + std::unique_ptr<cr_fuchsia::AgentManager> agent_manager; + std::unique_ptr<ApiBindingsClient> api_bindings_client; + fidl::InterfaceRequest<fuchsia::sys::ComponentController> + controller_request; + chromium::cast::ApplicationConfig app_config; + fuchsia::web::AdditionalHeadersProviderPtr headers_provider; + base::Optional<std::vector<fuchsia::net::http::Header>> headers; + }; + + CastComponent(CastRunner* runner, CastComponentParams params); ~CastComponent() override; private: @@ -52,6 +65,7 @@ class CastComponent : public WebComponent, NamedMessagePortConnector connector_; std::unique_ptr<TouchInputBindings> touch_input_; std::unique_ptr<ApiBindingsClient> api_bindings_client_; + std::unique_ptr<ApplicationControllerImpl> application_controller_; fidl::Binding<fuchsia::web::NavigationEventListener> navigation_listener_binding_; diff --git a/chromium/fuchsia/runners/cast/cast_runner.cc b/chromium/fuchsia/runners/cast/cast_runner.cc index 6f53482ebcd..a6d9368b023 100644 --- a/chromium/fuchsia/runners/cast/cast_runner.cc +++ b/chromium/fuchsia/runners/cast/cast_runner.cc @@ -10,29 +10,15 @@ #include <utility> #include "base/fuchsia/fuchsia_logging.h" -#include "base/fuchsia/startup_context.h" #include "base/logging.h" -#include "fuchsia/base/agent_manager.h" -#include "fuchsia/runners/cast/cast_component.h" #include "url/gurl.h" -CastRunner::CastRunner(base::fuchsia::ServiceDirectory* service_directory, +CastRunner::CastRunner(sys::OutgoingDirectory* outgoing_directory, fuchsia::web::ContextPtr context) - : WebContentRunner(service_directory, std::move(context)) {} + : WebContentRunner(outgoing_directory, std::move(context)) {} CastRunner::~CastRunner() = default; -struct CastRunner::PendingComponent { - chromium::cast::ApplicationConfigManagerPtr app_config_manager; - std::unique_ptr<base::fuchsia::StartupContext> startup_context; - std::unique_ptr<cr_fuchsia::AgentManager> agent_manager; - std::unique_ptr<ApiBindingsClient> bindings_manager; - fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request; - chromium::cast::ApplicationConfig app_config; - fuchsia::web::AdditionalHeadersProviderPtr headers_provider; - base::Optional<std::vector<fuchsia::net::http::Header>> headers; -}; - void CastRunner::StartComponent( fuchsia::sys::Package package, fuchsia::sys::StartupInfo startup_info, @@ -54,7 +40,8 @@ void CastRunner::StartComponent( // The application configuration asynchronously via the per-component // ApplicationConfigManager, the pointer to that service must be kept live // until the request completes, or CastRunner is deleted. - auto pending_component = std::make_unique<PendingComponent>(); + auto pending_component = + std::make_unique<CastComponent::CastComponentParams>(); pending_component->startup_context = std::make_unique<base::fuchsia::StartupContext>(std::move(startup_info)); pending_component->agent_manager = std::make_unique<cr_fuchsia::AgentManager>( @@ -75,7 +62,7 @@ void CastRunner::StartComponent( fidl::InterfaceHandle<chromium::cast::ApiBindings> api_bindings_client; pending_component->agent_manager->ConnectToAgentService( kAgentComponentUrl, api_bindings_client.NewRequest()); - pending_component->bindings_manager = std::make_unique<ApiBindingsClient>( + pending_component->api_bindings_client = std::make_unique<ApiBindingsClient>( std::move(api_bindings_client), base::BindOnce(&CastRunner::MaybeStartComponent, base::Unretained(this), base::Unretained(pending_component.get()))); @@ -116,12 +103,8 @@ const char CastRunner::kAgentComponentUrl[] = "fuchsia-pkg://fuchsia.com/cast_agent#meta/cast_agent.cmx"; void CastRunner::GetConfigCallback( - PendingComponent* pending_component, + CastComponent::CastComponentParams* pending_component, chromium::cast::ApplicationConfig app_config) { - // Ideally the PendingComponent would be move()d out of |pending_components_| - // here, but that requires extract(), which isn't available until C++17. - // Instead find |pending_component| and move() the individual fields out - // before erase()ing it. auto it = pending_components_.find(pending_component); DCHECK(it != pending_components_.end()); @@ -137,25 +120,23 @@ void CastRunner::GetConfigCallback( MaybeStartComponent(pending_component); } -void CastRunner::MaybeStartComponent(PendingComponent* pending_component) { +void CastRunner::MaybeStartComponent( + CastComponent::CastComponentParams* pending_component) { if (pending_component->app_config.IsEmpty()) return; - if (!pending_component->bindings_manager->HasBindings()) + if (!pending_component->api_bindings_client->HasBindings()) return; if (!pending_component->headers.has_value()) return; // Create a component based on the returned configuration, and pass it the - // fields stashed in PendingComponent. - GURL cast_app_url(pending_component->app_config.web_url()); - auto component = std::make_unique<CastComponent>( - this, std::move(pending_component->app_config), - std::move(pending_component->bindings_manager), - std::move(pending_component->startup_context), - std::move(pending_component->controller_request), - std::move(pending_component->agent_manager)); + // |pending_component|. std::vector<fuchsia::net::http::Header> additional_headers = pending_component->headers.value(); + + GURL cast_app_url(pending_component->app_config.web_url()); + auto component = + std::make_unique<CastComponent>(this, std::move(*pending_component)); pending_components_.erase(pending_component); component->LoadUrl(std::move(cast_app_url), std::move(additional_headers)); diff --git a/chromium/fuchsia/runners/cast/cast_runner.cmx b/chromium/fuchsia/runners/cast/cast_runner.cmx index cdb498dd370..ddd13673854 100644 --- a/chromium/fuchsia/runners/cast/cast_runner.cmx +++ b/chromium/fuchsia/runners/cast/cast_runner.cmx @@ -10,10 +10,9 @@ "fuchsia.fonts.Provider", "fuchsia.logger.LogSink", "fuchsia.media.Audio", - "fuchsia.media.drm.WidevineContentDecryptionModule", + "fuchsia.media.drm.Widevine", "fuchsia.mediacodec.CodecFactory", "fuchsia.net.NameLookup", - "fuchsia.net.SocketProvider", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", "fuchsia.process.Launcher", @@ -21,6 +20,7 @@ "fuchsia.ui.input.ImeService", "fuchsia.ui.input.ImeVisibilityService", "fuchsia.ui.scenic.Scenic", + "fuchsia.vulkan.loader.Loader", "fuchsia.web.ContextProvider" ] } diff --git a/chromium/fuchsia/runners/cast/cast_runner.h b/chromium/fuchsia/runners/cast/cast_runner.h index fe44c2de7e3..afbcaae63a7 100644 --- a/chromium/fuchsia/runners/cast/cast_runner.h +++ b/chromium/fuchsia/runners/cast/cast_runner.h @@ -14,12 +14,13 @@ #include "base/containers/unique_ptr_adapters.h" #include "base/macros.h" #include "fuchsia/fidl/chromium/cast/cpp/fidl.h" +#include "fuchsia/runners/cast/cast_component.h" #include "fuchsia/runners/common/web_content_runner.h" // sys::Runner which instantiates Cast activities specified via cast/casts URIs. class CastRunner : public WebContentRunner { public: - CastRunner(base::fuchsia::ServiceDirectory* service_directory, + CastRunner(sys::OutgoingDirectory* outgoing_directory, fuchsia::web::ContextPtr context); ~CastRunner() override; @@ -34,19 +35,20 @@ class CastRunner : public WebContentRunner { static const char kAgentComponentUrl[]; private: - struct PendingComponent; - - void GetConfigCallback(PendingComponent* pending_component, + void GetConfigCallback(CastComponent::CastComponentParams* pending_component, chromium::cast::ApplicationConfig app_config); - void GetBindingsCallback(PendingComponent* pending_component, - std::vector<chromium::cast::ApiBinding> bindings); + void GetBindingsCallback( + CastComponent::CastComponentParams* pending_component, + std::vector<chromium::cast::ApiBinding> bindings); // Starts a component once all configuration data is available. - void MaybeStartComponent(PendingComponent* pending_component); + void MaybeStartComponent( + CastComponent::CastComponentParams* pending_component); // Holds StartComponent() requests while the ApplicationConfig is being // fetched from the ApplicationConfigManager. - base::flat_set<std::unique_ptr<PendingComponent>, base::UniquePtrComparator> + base::flat_set<std::unique_ptr<CastComponent::CastComponentParams>, + base::UniquePtrComparator> pending_components_; DISALLOW_COPY_AND_ASSIGN(CastRunner); diff --git a/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc b/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc index ed8e8f2cb65..88a63ead670 100644 --- a/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc +++ b/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc @@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <fuchsia/modular/cpp/fidl.h> +#include <lib/fdio/directory.h> #include <lib/fidl/cpp/binding.h> #include <lib/zx/channel.h> #include "base/fuchsia/file_utils.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/scoped_service_binding.h" -#include "base/fuchsia/service_directory.h" -#include "base/fuchsia/service_directory_client.h" -#include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" #include "base/test/bind_test_util.h" +#include "base/test/task_environment.h" #include "base/test/test_timeouts.h" #include "base/threading/sequenced_task_runner_handle.h" #include "fuchsia/base/agent_impl.h" @@ -21,6 +21,7 @@ #include "fuchsia/base/frame_test_util.h" #include "fuchsia/base/mem_buffer_util.h" #include "fuchsia/base/result_receiver.h" +#include "fuchsia/base/string_util.h" #include "fuchsia/base/test_navigation_listener.h" #include "fuchsia/runners/cast/cast_runner.h" #include "fuchsia/runners/cast/fake_application_config_manager.h" @@ -44,15 +45,10 @@ void ComponentErrorHandler(zx_status_t status) { ADD_FAILURE(); } -std::vector<uint8_t> StringToUnsignedVector(base::StringPiece str) { - const uint8_t* raw_data = reinterpret_cast<const uint8_t*>(str.data()); - return std::vector<uint8_t>(raw_data, raw_data + str.length()); -} - class FakeAdditionalHeadersProvider : public fuchsia::web::AdditionalHeadersProvider { public: - FakeAdditionalHeadersProvider(base::fuchsia::ServiceDirectory* directory) + explicit FakeAdditionalHeadersProvider(sys::OutgoingDirectory* directory) : binding_(directory, this) {} ~FakeAdditionalHeadersProvider() override = default; @@ -60,8 +56,8 @@ class FakeAdditionalHeadersProvider void GetHeaders(GetHeadersCallback callback) override { std::vector<fuchsia::net::http::Header> headers; fuchsia::net::http::Header header; - header.name = StringToUnsignedVector("Test"); - header.value = StringToUnsignedVector("Value"); + header.name = cr_fuchsia::StringToBytes("Test"); + header.value = cr_fuchsia::StringToBytes("Value"); headers.push_back(std::move(header)); callback(std::move(headers), 0); } @@ -73,21 +69,53 @@ class FakeAdditionalHeadersProvider DISALLOW_COPY_AND_ASSIGN(FakeAdditionalHeadersProvider); }; +class FakeApplicationControllerReceiver + : public chromium::cast::ApplicationControllerReceiver { + public: + FakeApplicationControllerReceiver() = default; + ~FakeApplicationControllerReceiver() final = default; + + chromium::cast::ApplicationController* controller() { + if (!controller_) + return nullptr; + + return controller_.get(); + } + + private: + // chromium::cast::ApplicationControllerReceiver implementation. + void SetApplicationController( + fidl::InterfaceHandle<chromium::cast::ApplicationController> controller) + final { + controller_ = controller.Bind(); + } + + chromium::cast::ApplicationControllerPtr controller_; + + DISALLOW_COPY_AND_ASSIGN(FakeApplicationControllerReceiver); +}; + class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase { public: FakeComponentState( base::StringPiece component_url, chromium::cast::ApplicationConfigManager* app_config_manager, - chromium::cast::ApiBindings* bindings_manager) + chromium::cast::ApiBindings* bindings_manager, + bool provide_controller_receiver) : ComponentStateBase(component_url), - app_config_binding_(service_directory(), app_config_manager), + app_config_binding_(outgoing_directory(), app_config_manager), additional_headers_provider_( std::make_unique<FakeAdditionalHeadersProvider>( - service_directory())) { + outgoing_directory())) { if (bindings_manager) { bindings_manager_binding_ = std::make_unique< base::fuchsia::ScopedServiceBinding<chromium::cast::ApiBindings>>( - service_directory(), bindings_manager); + outgoing_directory(), bindings_manager); + } + + if (provide_controller_receiver) { + controller_receiver_binding_.emplace(outgoing_directory(), + &controller_receiver_); } } ~FakeComponentState() override { @@ -95,6 +123,10 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase { std::move(on_delete_).Run(); } + FakeApplicationControllerReceiver* controller_receiver() { + return &controller_receiver_; + } + void set_on_delete(base::OnceClosure on_delete) { on_delete_ = std::move(on_delete); } @@ -107,6 +139,10 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase { base::fuchsia::ScopedServiceBinding<chromium::cast::ApiBindings>> bindings_manager_binding_; std::unique_ptr<FakeAdditionalHeadersProvider> additional_headers_provider_; + FakeApplicationControllerReceiver controller_receiver_; + base::Optional<base::fuchsia::ScopedServiceBinding< + chromium::cast::ApplicationControllerReceiver>> + controller_receiver_binding_; base::OnceClosure on_delete_; DISALLOW_COPY_AND_ASSIGN(FakeComponentState); @@ -120,21 +156,19 @@ class CastRunnerIntegrationTest : public testing::Test { : run_timeout_( TestTimeouts::action_timeout(), base::MakeExpectedNotRunClosure(FROM_HERE, "Run() timed out.")) { - // Create a new test ServiceDirectory, and ServiceDirectoryClient connected - // to it, for tests to use to drive the CastRunner. - fidl::InterfaceHandle<fuchsia::io::Directory> directory; - public_services_ = std::make_unique<base::fuchsia::ServiceDirectory>( - directory.NewRequest()); - // Create the CastRunner, published into |test_services_|. + constexpr fuchsia::web::ContextFeatureFlags kFeatures = {}; cast_runner_ = std::make_unique<CastRunner>( - public_services_.get(), WebContentRunner::CreateIncognitoWebContext()); + &outgoing_directory_, + WebContentRunner::CreateIncognitoWebContext(kFeatures)); // Connect to the CastRunner's fuchsia.sys.Runner interface. - base::fuchsia::ServiceDirectoryClient public_directory_client( - std::move(directory)); - cast_runner_ptr_ = - public_directory_client.ConnectToService<fuchsia::sys::Runner>(); + fidl::InterfaceHandle<fuchsia::io::Directory> directory; + outgoing_directory_.GetOrCreateDirectory("svc")->Serve( + fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE, + directory.NewRequest().TakeChannel()); + sys::ServiceDirectory public_directory_client(std::move(directory)); + cast_runner_ptr_ = public_directory_client.Connect<fuchsia::sys::Runner>(); cast_runner_ptr_.set_error_handler([](zx_status_t status) { ZX_LOG(ERROR, status) << "CastRunner closed channel."; ADD_FAILURE(); @@ -157,20 +191,35 @@ class CastRunnerIntegrationTest : public testing::Test { base::StringPiece component_url) { DCHECK(!component_state_); - // Create a ServiceDirectory and publish the ComponentContext into it. - fidl::InterfaceHandle<fuchsia::io::Directory> directory; - component_services_ = std::make_unique<base::fuchsia::ServiceDirectory>( - directory.NewRequest()); + // Create an OutgoingDirectory and publish the ComponentContext into it. component_context_ = std::make_unique<cr_fuchsia::FakeComponentContext>( base::BindRepeating(&CastRunnerIntegrationTest::OnComponentConnect, base::Unretained(this)), - component_services_.get(), component_url); + &component_services_, component_url); // Configure the Runner, including a service directory channel to publish // services to. + fidl::InterfaceHandle<fuchsia::io::Directory> directory; + component_services_.GetOrCreateDirectory("svc")->Serve( + fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE, + directory.NewRequest().TakeChannel()); fuchsia::sys::StartupInfo startup_info; startup_info.launch_info.url = component_url.as_string(); + fidl::InterfaceHandle<fuchsia::io::Directory> outgoing_directory; + startup_info.launch_info.directory_request = + outgoing_directory.NewRequest().TakeChannel(); + + fidl::InterfaceHandle<::fuchsia::io::Directory> svc_directory; + CHECK_EQ(fdio_service_connect_at( + outgoing_directory.channel().get(), "svc", + svc_directory.NewRequest().TakeChannel().release()), + ZX_OK); + + component_services_client_ = + std::make_unique<base::fuchsia::ServiceDirectoryClient>( + std::move(svc_directory)); + // Place the ServiceDirectory in the |flat_namespace|. startup_info.flat_namespace.paths.emplace_back( base::fuchsia::kServiceDirectoryPath); @@ -194,13 +243,16 @@ class CastRunnerIntegrationTest : public testing::Test { base::StringPiece component_url) { auto component_state = std::make_unique<FakeComponentState>( component_url, &app_config_manager_, - (provide_api_bindings_ ? &api_bindings_ : nullptr)); + (provide_api_bindings_ ? &api_bindings_ : nullptr), + provide_controller_receiver_); component_state_ = component_state.get(); return component_state; } const base::RunLoop::ScopedRunTimeoutForTest run_timeout_; - base::MessageLoopForIO message_loop_; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, + base::test::TaskEnvironment::MainThreadType::IO}; net::EmbeddedTestServer test_server_; // Returns fake Cast application information to the CastRunner. @@ -208,16 +260,23 @@ class CastRunnerIntegrationTest : public testing::Test { TestApiBindings api_bindings_; + // If set, publishes a ApplicationControllerReceiver service from the agent. + // TODO(crbug.com/953958): Remove this flag and make provisioning of the + // receiver service mandatory once CastAgent supports it. + bool provide_controller_receiver_ = true; + // If set, publishes an ApiBindings service from the Agent. bool provide_api_bindings_ = true; // Incoming service directory, ComponentContext and per-component state. - std::unique_ptr<base::fuchsia::ServiceDirectory> component_services_; + sys::OutgoingDirectory component_services_; std::unique_ptr<cr_fuchsia::FakeComponentContext> component_context_; + std::unique_ptr<base::fuchsia::ServiceDirectoryClient> + component_services_client_; FakeComponentState* component_state_ = nullptr; // ServiceDirectory into which the CastRunner will publish itself. - std::unique_ptr<base::fuchsia::ServiceDirectory> public_services_; + sys::OutgoingDirectory outgoing_directory_; std::unique_ptr<CastRunner> cast_runner_; fuchsia::sys::RunnerPtr cast_runner_ptr_; @@ -283,7 +342,8 @@ TEST_F(CastRunnerIntegrationTest, ApiBindings) { std::vector<chromium::cast::ApiBinding> binding_list; chromium::cast::ApiBinding echo_binding; echo_binding.set_before_load_script(cr_fuchsia::MemBufferFromString( - "window.echo = cast.__platform__.PortConnector.bind('echoService');")); + "window.echo = cast.__platform__.PortConnector.bind('echoService');", + "test")); binding_list.emplace_back(std::move(echo_binding)); api_bindings_.set_bindings(std::move(binding_list)); @@ -296,7 +356,7 @@ TEST_F(CastRunnerIntegrationTest, ApiBindings) { api_bindings_.RunUntilMessagePortReceived("echoService").Bind(); fuchsia::web::WebMessage message; - message.set_data(cr_fuchsia::MemBufferFromString("ping")); + message.set_data(cr_fuchsia::MemBufferFromString("ping", "ping-msg")); port->PostMessage(std::move(message), [](fuchsia::web::MessagePort_PostMessage_Result result) { EXPECT_TRUE(result.is_response()); @@ -374,4 +434,69 @@ TEST_F(CastRunnerIntegrationTest, AdditionalHeadersProvider) { EXPECT_EQ(result->GetString(), "Value"); } +TEST_F(CastRunnerIntegrationTest, ApplicationControllerBound) { + const char kCastChannelAppId[] = "00000001"; + const char kCastChannelAppPath[] = "/defaultresponse"; + app_config_manager_.AddAppMapping(kCastChannelAppId, + test_server_.GetURL(kCastChannelAppPath)); + + fuchsia::sys::ComponentControllerPtr component_controller = + StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId)); + + // Spin the message loop to handle creation of the component state. + base::RunLoop().RunUntilIdle(); + ASSERT_TRUE(component_state_); + EXPECT_TRUE(component_state_->controller_receiver()->controller()); +} + +// Verify that we gracefully handle the interim case where the Agent doesn't +// publish the ApplicationController service. +// TODO(crbug.com/953958): Remove this test case once CastAgent provides the +// service. +TEST_F(CastRunnerIntegrationTest, ApplicationControllerNotSupported) { + const char kCastChannelAppId[] = "00000001"; + const char kCastChannelAppPath[] = "/defaultresponse"; + + provide_controller_receiver_ = false; + + app_config_manager_.AddAppMapping(kCastChannelAppId, + test_server_.GetURL(kCastChannelAppPath)); + + fuchsia::sys::ComponentControllerPtr component_controller = + StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId)); + + // Spin the message loop to handle creation of the component state. + base::RunLoop().RunUntilIdle(); + ASSERT_TRUE(component_state_); + EXPECT_FALSE(component_state_->controller_receiver()->controller()); +} + +// Verify that we can connect to the Lifecycle interface and terminate the +// component. Service connection is sent immediately after the component start +// request, so this test also verifies that the component doesn't start handling +// incoming service requests before the service has been published. +TEST_F(CastRunnerIntegrationTest, Lifecycle) { + const char kCastChannelAppId[] = "00000001"; + const char kCastChannelAppPath[] = "/defaultresponse"; + + provide_controller_receiver_ = false; + + app_config_manager_.AddAppMapping(kCastChannelAppId, + test_server_.GetURL(kCastChannelAppPath)); + + fuchsia::sys::ComponentControllerPtr component_controller = + StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId)); + + fuchsia::modular::LifecyclePtr lifecycle; + component_services_client_->ConnectToService(lifecycle.NewRequest()); + lifecycle->Terminate(); + + base::RunLoop run_loop; + component_controller.set_error_handler([&run_loop](zx_status_t status) { + EXPECT_EQ(status, ZX_ERR_PEER_CLOSED); + run_loop.Quit(); + }); + run_loop.Run(); +} + } // namespace castrunner diff --git a/chromium/fuchsia/runners/cast/fake_queryable_data.cc b/chromium/fuchsia/runners/cast/fake_queryable_data.cc deleted file mode 100644 index 4f3758dea5a..00000000000 --- a/chromium/fuchsia/runners/cast/fake_queryable_data.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 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 "fuchsia/runners/cast/fake_queryable_data.h" - -#include <string> -#include <utility> -#include <vector> - -#include "base/json/json_writer.h" - -FakeQueryableData::FakeQueryableData() = default; - -FakeQueryableData::~FakeQueryableData() = default; - -void FakeQueryableData::SendChanges( - std::vector<std::pair<base::StringPiece, base::Value&&>> changes) { - for (const auto& change : changes) { - std::string value_json; - CHECK(base::JSONWriter::Write(change.second, &value_json)); - changes_.push_back({change.first.as_string(), value_json}); - } - - if (get_changed_callback_) - DeliverChanges(); -} - -void FakeQueryableData::GetChangedEntries(GetChangedEntriesCallback callback) { - DCHECK(!get_changed_callback_); - get_changed_callback_ = std::move(callback); - - if (!changes_.empty()) - DeliverChanges(); -} - -void FakeQueryableData::DeliverChanges() { - (*get_changed_callback_)(std::move(changes_)); - get_changed_callback_ = base::nullopt; -} diff --git a/chromium/fuchsia/runners/cast/fake_queryable_data.h b/chromium/fuchsia/runners/cast/fake_queryable_data.h deleted file mode 100644 index 1c3f14d3afa..00000000000 --- a/chromium/fuchsia/runners/cast/fake_queryable_data.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 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 FUCHSIA_RUNNERS_CAST_FAKE_QUERYABLE_DATA_H_ -#define FUCHSIA_RUNNERS_CAST_FAKE_QUERYABLE_DATA_H_ - -#include <string> -#include <utility> -#include <vector> - -#include "base/macros.h" -#include "base/optional.h" -#include "base/strings/string_piece.h" -#include "base/values.h" -#include "fuchsia/fidl/chromium/cast/cpp/fidl.h" - -// A simple STL map-backed implementation of QueryableData, for testing -// purposes. -class FakeQueryableData : public chromium::cast::QueryableData { - public: - FakeQueryableData(); - ~FakeQueryableData() override; - - // Sends, or prepares to send, a set of changed values to the page. - void SendChanges( - std::vector<std::pair<base::StringPiece, base::Value&&>> changes); - - private: - void DeliverChanges(); - - // chromium::cast::QueryableData implementation. - void GetChangedEntries(GetChangedEntriesCallback callback) override; - - base::Optional<GetChangedEntriesCallback> get_changed_callback_; - std::vector<chromium::cast::QueryableDataEntry> changes_; - - DISALLOW_COPY_AND_ASSIGN(FakeQueryableData); -}; - -#endif // FUCHSIA_RUNNERS_CAST_FAKE_QUERYABLE_DATA_H_ diff --git a/chromium/fuchsia/runners/cast/main.cc b/chromium/fuchsia/runners/cast/main.cc index a4b791e7202..65c55ddc350 100644 --- a/chromium/fuchsia/runners/cast/main.cc +++ b/chromium/fuchsia/runners/cast/main.cc @@ -2,18 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/fuchsia/service_directory.h" -#include "base/fuchsia/service_directory_client.h" +#include <lib/sys/cpp/component_context.h> + +#include "base/fuchsia/default_context.h" +#include "base/message_loop/message_pump_type.h" #include "base/run_loop.h" #include "base/task/single_thread_task_executor.h" #include "fuchsia/runners/cast/cast_runner.h" int main(int argc, char** argv) { - base::SingleThreadTaskExecutor io_task_executor(base::MessagePump::Type::IO); + base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO); base::RunLoop run_loop; - CastRunner runner(base::fuchsia::ServiceDirectory::GetDefault(), - WebContentRunner::CreateIncognitoWebContext()); + constexpr fuchsia::web::ContextFeatureFlags kCastRunnerFeatures = + fuchsia::web::ContextFeatureFlags::NETWORK | + fuchsia::web::ContextFeatureFlags::AUDIO | + fuchsia::web::ContextFeatureFlags::VULKAN | + fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER; + + CastRunner runner( + base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(), + WebContentRunner::CreateIncognitoWebContext(kCastRunnerFeatures)); + + base::fuchsia::ComponentContextForCurrentProcess() + ->outgoing() + ->ServeFromStartupInfo(); // Run until there are no Components, or the last service client channel is // closed. diff --git a/chromium/fuchsia/runners/cast/named_message_port_connector.cc b/chromium/fuchsia/runners/cast/named_message_port_connector.cc index 23e106125c9..a9c5306330c 100644 --- a/chromium/fuchsia/runners/cast/named_message_port_connector.cc +++ b/chromium/fuchsia/runners/cast/named_message_port_connector.cc @@ -20,8 +20,8 @@ namespace { -const char kBindingsJsPath[] = - FILE_PATH_LITERAL("chromecast/bindings/named_message_port_connector.js"); +const char kBindingsJsPath[] = FILE_PATH_LITERAL( + "chromecast/bindings/resources/named_message_port_connector.js"); const char kControlPortConnectMessage[] = "cast.master.connect"; } // namespace @@ -43,7 +43,8 @@ NamedMessagePortConnector::NamedMessagePortConnector(fuchsia::web::Frame* frame) frame_->AddBeforeLoadJavaScript( static_cast<uint64_t>( CastPlatformBindingsId::NAMED_MESSAGE_PORT_CONNECTOR), - std::move(origins), cr_fuchsia::CloneBuffer(bindings_script_), + std::move(origins), + cr_fuchsia::CloneBuffer(bindings_script_, "cast-bindings-js"), [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { CHECK(result.is_response()) << "Couldn't inject bindings."; }); @@ -81,7 +82,8 @@ void NamedMessagePortConnector::OnPageLoad() { control_port_.Unbind(); fuchsia::web::WebMessage message; - message.set_data(cr_fuchsia::MemBufferFromString(kControlPortConnectMessage)); + message.set_data(cr_fuchsia::MemBufferFromString(kControlPortConnectMessage, + "cast-connect-message")); std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector(1); outgoing_vector[0].set_message_port(control_port_.NewRequest()); message.set_outgoing_transfer(std::move(outgoing_vector)); diff --git a/chromium/fuchsia/runners/cast/named_message_port_connector_browsertest.cc b/chromium/fuchsia/runners/cast/named_message_port_connector_browsertest.cc index 30e702d2b41..01c9e76ef12 100644 --- a/chromium/fuchsia/runners/cast/named_message_port_connector_browsertest.cc +++ b/chromium/fuchsia/runners/cast/named_message_port_connector_browsertest.cc @@ -97,7 +97,7 @@ IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, EndToEnd) { fuchsia::web::MessagePortPtr message_port = message_port_receiver->Bind(); fuchsia::web::WebMessage msg; - msg.set_data(cr_fuchsia::MemBufferFromString("ping")); + msg.set_data(cr_fuchsia::MemBufferFromString("ping", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result> post_result; message_port->PostMessage( @@ -175,7 +175,7 @@ IN_PROC_BROWSER_TEST_F(NamedMessagePortConnectorTest, MultiplePorts) { for (fuchsia::web::MessagePortPtr* port : {&port_1, &port_2}) { fuchsia::web::WebMessage msg; - msg.set_data(cr_fuchsia::MemBufferFromString("ping")); + msg.set_data(cr_fuchsia::MemBufferFromString("ping", "test")); cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result> post_result; (*port)->PostMessage(std::move(msg), cr_fuchsia::CallbackToFitFunction( diff --git a/chromium/fuchsia/runners/cast/not_implemented_api_bindings.js b/chromium/fuchsia/runners/cast/not_implemented_api_bindings.js index debe43a23d8..c131e9b3257 100644 --- a/chromium/fuchsia/runners/cast/not_implemented_api_bindings.js +++ b/chromium/fuchsia/runners/cast/not_implemented_api_bindings.js @@ -58,33 +58,33 @@ if (!cast.__platform__._notImplemented) { } }; - + // TODO(b/139230885) if (!cast.__platform__.canDisplayType) { cast.__platform__.canDisplayType = cast.__platform__._notImplemented('canDisplayType', true); } + // TODO(b/139229713) if (!cast.__platform__.setAssistantMessageHandler) { cast.__platform__.setAssistantMessageHandler = cast.__platform__._notImplemented('setAssistantMessageHandler'); } - if (!cast.__platform__.sendAssistantRequest) { cast.__platform__.sendAssistantRequest = cast.__platform__._notImplemented('sendAssistantRequest'); } + // TODO(b/139228475) if (!cast.__platform__.setWindowRequestHandler) { cast.__platform__.setWindowRequestHandler = cast.__platform__._notImplemented('setWindowRequestHandler'); } - if (!cast.__platform__.takeScreenshot) { cast.__platform__.takeScreenshot = cast.__platform__._notImplemented('takeScreenshot'); } - + // TODO(b/139232520) if (!cast.__platform__.crypto) { cast.__platform__.crypto = {}; @@ -124,8 +124,6 @@ if (!cast.__platform__._notImplemented) { undefined, cast.__platform__.ReturnType.PROMISE_REJECTED); } - - if (!cast.__platform__.cryptokeys) { cast.__platform__.cryptokeys = {}; @@ -136,7 +134,7 @@ if (!cast.__platform__._notImplemented) { cast.__platform__.ReturnType.PROMISE_REJECTED); } - + // TODO(b/139232101) if (!cast.__platform__.display) { cast.__platform__.display = {}; @@ -153,7 +151,7 @@ if (!cast.__platform__._notImplemented) { cast.__platform__.ReturnType.PROMISE_RESOLVED); } - + // TODO(b/139232160) if (!cast.__platform__.accessibility) { cast.__platform__.accessibility = {}; @@ -172,7 +170,7 @@ if (!cast.__platform__._notImplemented) { 'accessibility.setMagnificationGesture'); } - + // TODO(b/139229752) if (!cast.__platform__.windowManager) { cast.__platform__.windowManager = {}; diff --git a/chromium/fuchsia/runners/cast/not_implemented_api_bindings_browsertest.cc b/chromium/fuchsia/runners/cast/not_implemented_api_bindings_browsertest.cc index 6d019e02a0e..d0867c56960 100644 --- a/chromium/fuchsia/runners/cast/not_implemented_api_bindings_browsertest.cc +++ b/chromium/fuchsia/runners/cast/not_implemented_api_bindings_browsertest.cc @@ -178,7 +178,8 @@ IN_PROC_BROWSER_TEST_F(StubBindingsTest, ApiCoverage) { {"*"}, cr_fuchsia::MemBufferFromString( base::StringPrintf("try { cast.__platform__.%s(); } catch {}", - expectation.function_name.c_str())), + expectation.function_name.c_str()), + "test"), [](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) { ASSERT_TRUE(result.is_response()); }); @@ -194,7 +195,7 @@ IN_PROC_BROWSER_TEST_F(StubBindingsTest, FunctionArgumentsInLogMessage) { frame_->ExecuteJavaScriptNoResult( {"*"}, cr_fuchsia::MemBufferFromString( - "cast.__platform__.sendAssistantRequest(1,2,'foo');"), + "cast.__platform__.sendAssistantRequest(1,2,'foo');", "test"), [](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) { ASSERT_TRUE(result.is_response()); }); diff --git a/chromium/fuchsia/runners/cast/touch_input_bindings.cc b/chromium/fuchsia/runners/cast/touch_input_bindings.cc index ea27f6d5705..3138586401c 100644 --- a/chromium/fuchsia/runners/cast/touch_input_bindings.cc +++ b/chromium/fuchsia/runners/cast/touch_input_bindings.cc @@ -140,7 +140,8 @@ void TouchInputBindings::OnControlMessageReceived( std::string response_json; CHECK(base::JSONWriter::Write(response, &response_json)); fuchsia::web::WebMessage response_message = {}; - response_message.set_data(cr_fuchsia::MemBufferFromString(response_json)); + response_message.set_data(cr_fuchsia::MemBufferFromString( + response_json, "cast-touch-message-response")); port_->PostMessage(std::move(response_message), [](fuchsia::web::MessagePort_PostMessage_Result result) { LOG_IF(ERROR, result.is_err()) diff --git a/chromium/fuchsia/runners/cast/touch_input_bindings.js b/chromium/fuchsia/runners/cast/touch_input_bindings.js index 6ab7c2d16dc..9ecd9c9f37d 100644 --- a/chromium/fuchsia/runners/cast/touch_input_bindings.js +++ b/chromium/fuchsia/runners/cast/touch_input_bindings.js @@ -4,70 +4,73 @@ 'use strict'; -cast.__platform__.__touchInput__ = new class { - constructor() { - this.port_ = cast.__platform__.PortConnector.bind( - 'cast.__platform__.__touchInput__'); +// Don't supercede an Agent-provided API function. +if (!cast.__platform__.setTouchInputSupport) { + cast.__platform__.__touchInput__ = new class { + constructor() { + this.port_ = cast.__platform__.PortConnector.bind( + 'cast.__platform__.__touchInput__'); - this.port_.onmessage = function(message) { - var responseParsed = JSON.parse(message.data); - if (responseParsed) { - this.onAck(responseParsed.requestId, - responseParsed.displayControls); - } - }.bind(this); - } - - // Receives an acknowledgement from the native bindings layer and relays the - // result to the Promise. - onAck(requestId, displayControls) { - if (!this.pendingRequests_.hasOwnProperty(requestId)) { - console.error('Received ack for unknown request ID: ' + requestId); - return; + this.port_.onmessage = function(message) { + var responseParsed = JSON.parse(message.data); + if (responseParsed) { + this.onAck(responseParsed.requestId, + responseParsed.displayControls); + } + }.bind(this); } - var request = this.pendingRequests_[requestId]; - delete this.pendingRequests_[requestId]; + // Receives an acknowledgement from the native bindings layer and relays the + // result to the Promise. + onAck(requestId, displayControls) { + if (!this.pendingRequests_.hasOwnProperty(requestId)) { + console.error('Received ack for unknown request ID: ' + requestId); + return; + } + + var request = this.pendingRequests_[requestId]; + delete this.pendingRequests_[requestId]; - if (displayControls === undefined) { - request.reject(); - } else { - request.resolve({'displayControls': displayControls}); + if (displayControls === undefined) { + request.reject(); + } else { + request.resolve({'displayControls': displayControls}); + } } - } - // Requests touch input support from the native bindings layer and returns a - // Promise with the result. - // If the request was successful, the Promise will be resolved with a - // dictionary indicating whether onscreen controls should be shown for the - // application. - // If the request was rejected, then the Promise will be rejected. - setTouchInputSupport(touchEnabled) { - return new Promise((resolve, reject) => { - var requestId = this.currentRequestId_++; - this.pendingRequests_[requestId] = { - 'resolve': resolve, - 'reject': reject, - }; - this.port_.postMessage(JSON.stringify({ - 'requestId': requestId, - 'touchEnabled': touchEnabled, - })); - }); - } + // Requests touch input support from the native bindings layer and returns a + // Promise with the result. + // If the request was successful, the Promise will be resolved with a + // dictionary indicating whether onscreen controls should be shown for the + // application. + // If the request was rejected, then the Promise will be rejected. + setTouchInputSupport(touchEnabled) { + return new Promise((resolve, reject) => { + var requestId = this.currentRequestId_++; + this.pendingRequests_[requestId] = { + 'resolve': resolve, + 'reject': reject, + }; + this.port_.postMessage(JSON.stringify({ + 'requestId': requestId, + 'touchEnabled': touchEnabled, + })); + }); + } - // Port for sending requests and receiving acknowledgements with the native - // bindings layer. - port_ = null; + // Port for sending requests and receiving acknowledgements with the native + // bindings layer. + port_ = null; - // A dictionary relating inflight request IDs to their Promise resolve/reject - // functions. - pendingRequests_ = {}; + // A dictionary relating inflight request IDs to their Promise + // resolve/reject functions. + pendingRequests_ = {}; - // Unique ID of the next request. - currentRequestId_ = 0; -}; + // Unique ID of the next request. + currentRequestId_ = 0; + }; -cast.__platform__.setTouchInputSupport = - cast.__platform__.__touchInput__.setTouchInputSupport.bind( - cast.__platform__.__touchInput__); + cast.__platform__.setTouchInputSupport = + cast.__platform__.__touchInput__.setTouchInputSupport.bind( + cast.__platform__.__touchInput__); +} diff --git a/chromium/fuchsia/runners/common/web_component.cc b/chromium/fuchsia/runners/common/web_component.cc index b700f04768b..8cdcab7f528 100644 --- a/chromium/fuchsia/runners/common/web_component.cc +++ b/chromium/fuchsia/runners/common/web_component.cc @@ -8,8 +8,10 @@ #include <fuchsia/ui/views/cpp/fidl.h> #include <lib/fidl/cpp/binding_set.h> #include <lib/fit/function.h> +#include <lib/sys/cpp/component_context.h> #include <utility> +#include "base/fuchsia/default_context.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/logging.h" #include "fuchsia/runners/common/web_content_runner.h" @@ -51,16 +53,16 @@ WebComponent::WebComponent( DestroyComponent(0, fuchsia::sys::TerminationReason::EXITED); }); - if (startup_context()->public_services()) { - // Publish services before returning control to the message-loop, to ensure - // that it is available before the ServiceDirectory starts processing - // requests. + if (startup_context()->has_outgoing_directory_request()) { + // Publish outgoing services and start serving component's outgoing + // directory. view_provider_binding_ = std::make_unique< base::fuchsia::ScopedServiceBinding<fuchsia::ui::app::ViewProvider>>( - startup_context()->public_services(), this); + startup_context()->component_context()->outgoing().get(), this); lifecycle_ = std::make_unique<cr_fuchsia::LifecycleImpl>( - startup_context_->public_services(), + startup_context()->component_context()->outgoing().get(), base::BindOnce(&WebComponent::Kill, base::Unretained(this))); + startup_context()->ServeOutgoingDirectory(); } } diff --git a/chromium/fuchsia/runners/common/web_content_runner.cc b/chromium/fuchsia/runners/common/web_content_runner.cc index 2ffe589cd95..2469821b610 100644 --- a/chromium/fuchsia/runners/common/web_content_runner.cc +++ b/chromium/fuchsia/runners/common/web_content_runner.cc @@ -6,16 +6,16 @@ #include <fuchsia/sys/cpp/fidl.h> #include <lib/fidl/cpp/binding_set.h> +#include <lib/sys/cpp/component_context.h> #include <utility> #include "base/bind.h" #include "base/files/file.h" #include "base/files/file_util.h" +#include "base/fuchsia/default_context.h" #include "base/fuchsia/file_utils.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/scoped_service_binding.h" -#include "base/fuchsia/service_directory.h" -#include "base/fuchsia/service_directory_client.h" #include "base/fuchsia/startup_context.h" #include "base/logging.h" #include "fuchsia/runners/buildflags.h" @@ -32,10 +32,11 @@ fidl::InterfaceHandle<fuchsia::io::Directory> OpenDirectoryOrFail( } fuchsia::web::ContextPtr CreateWebContextWithDataDirectory( - fidl::InterfaceHandle<fuchsia::io::Directory> data_directory) { - auto web_context_provider = - base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToService<fuchsia::web::ContextProvider>(); + fidl::InterfaceHandle<fuchsia::io::Directory> data_directory, + fuchsia::web::ContextFeatureFlags features) { + auto web_context_provider = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::web::ContextProvider>(); fuchsia::web::CreateContextParams create_params; @@ -45,6 +46,8 @@ fuchsia::web::ContextPtr CreateWebContextWithDataDirectory( if (data_directory) create_params.set_data_directory(std::move(data_directory)); + create_params.set_features(features); + // Set |remote_debugging_port| on the context, if set. if (BUILDFLAG(ENABLE_REMOTE_DEBUGGING_ON_PORT) != 0) { create_params.set_remote_debugging_port( @@ -66,21 +69,24 @@ fuchsia::web::ContextPtr CreateWebContextWithDataDirectory( } // namespace // static -fuchsia::web::ContextPtr WebContentRunner::CreateDefaultWebContext() { - return CreateWebContextWithDataDirectory(OpenDirectoryOrFail( - base::FilePath(base::fuchsia::kPersistedDataDirectoryPath))); +fuchsia::web::ContextPtr WebContentRunner::CreateDefaultWebContext( + fuchsia::web::ContextFeatureFlags features) { + return CreateWebContextWithDataDirectory( + OpenDirectoryOrFail( + base::FilePath(base::fuchsia::kPersistedDataDirectoryPath)), + features); } // static -fuchsia::web::ContextPtr WebContentRunner::CreateIncognitoWebContext() { +fuchsia::web::ContextPtr WebContentRunner::CreateIncognitoWebContext( + fuchsia::web::ContextFeatureFlags features) { return CreateWebContextWithDataDirectory( - fidl::InterfaceHandle<fuchsia::io::Directory>()); + fidl::InterfaceHandle<fuchsia::io::Directory>(), features); } -WebContentRunner::WebContentRunner( - base::fuchsia::ServiceDirectory* service_directory, - fuchsia::web::ContextPtr context) - : context_(std::move(context)), service_binding_(service_directory, this) { +WebContentRunner::WebContentRunner(sys::OutgoingDirectory* outgoing_directory, + fuchsia::web::ContextPtr context) + : context_(std::move(context)), service_binding_(outgoing_directory, this) { DCHECK(context_); } diff --git a/chromium/fuchsia/runners/common/web_content_runner.h b/chromium/fuchsia/runners/common/web_content_runner.h index f8add75b44b..b1c6323e93e 100644 --- a/chromium/fuchsia/runners/common/web_content_runner.h +++ b/chromium/fuchsia/runners/common/web_content_runner.h @@ -13,7 +13,6 @@ #include "base/callback.h" #include "base/containers/unique_ptr_adapters.h" #include "base/fuchsia/scoped_service_binding.h" -#include "base/fuchsia/service_directory.h" #include "base/macros.h" class WebComponent; @@ -24,20 +23,22 @@ class WebContentRunner : public fuchsia::sys::Runner { // Creates and returns a web.Context with a default path and parameters, // and with access to the same services as this Runner. The returned binding // is configured to exit this process on error. - static fuchsia::web::ContextPtr CreateDefaultWebContext(); + static fuchsia::web::ContextPtr CreateDefaultWebContext( + fuchsia::web::ContextFeatureFlags features); // Creates and returns an incognito web.Context with access to the same // services as this Runner. The returned binding is configured to exit this // process on error. - static fuchsia::web::ContextPtr CreateIncognitoWebContext(); + static fuchsia::web::ContextPtr CreateIncognitoWebContext( + fuchsia::web::ContextFeatureFlags features); - // |service_directory|: ServiceDirectory into which this Runner will be + // |outgoing_directory|: OutgoingDirectory into which this Runner will be // published. |on_idle_closure| will be invoked when the final client of the // published service disconnects, even if one or more Components are still // active. - // |content|: Context (e.g. persisted profile storage) under which all web + // |context|: Context (e.g. persisted profile storage) under which all web // content launched through this Runner instance will be run. - WebContentRunner(base::fuchsia::ServiceDirectory* service_directory, + WebContentRunner(sys::OutgoingDirectory* outgoing_directory, fuchsia::web::ContextPtr context); ~WebContentRunner() override; diff --git a/chromium/fuchsia/runners/web/main.cc b/chromium/fuchsia/runners/web/main.cc index dd9ad4a76b3..ed0d1f75c27 100644 --- a/chromium/fuchsia/runners/web/main.cc +++ b/chromium/fuchsia/runners/web/main.cc @@ -2,17 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/fuchsia/service_directory.h" +#include <lib/sys/cpp/component_context.h> + +#include "base/fuchsia/default_context.h" +#include "base/message_loop/message_pump_type.h" #include "base/run_loop.h" #include "base/task/single_thread_task_executor.h" #include "fuchsia/runners/common/web_content_runner.h" int main(int argc, char** argv) { - base::SingleThreadTaskExecutor io_task_executor(base::MessagePump::Type::IO); + base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO); base::RunLoop run_loop; - WebContentRunner runner(base::fuchsia::ServiceDirectory::GetDefault(), - WebContentRunner::CreateDefaultWebContext()); + constexpr fuchsia::web::ContextFeatureFlags kWebRunnerFeatures = + fuchsia::web::ContextFeatureFlags::NETWORK | + fuchsia::web::ContextFeatureFlags::AUDIO | + fuchsia::web::ContextFeatureFlags::VULKAN | + fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER; + + WebContentRunner runner( + base::fuchsia::ComponentContextForCurrentProcess()->outgoing().get(), + WebContentRunner::CreateDefaultWebContext(kWebRunnerFeatures)); + + base::fuchsia::ComponentContextForCurrentProcess() + ->outgoing() + ->ServeFromStartupInfo(); // Run until there are no Components, or the last service client channel is // closed. diff --git a/chromium/fuchsia/runners/web/web_runner.cmx b/chromium/fuchsia/runners/web/web_runner.cmx index 83648b53840..8acaf9a0c83 100644 --- a/chromium/fuchsia/runners/web/web_runner.cmx +++ b/chromium/fuchsia/runners/web/web_runner.cmx @@ -10,10 +10,9 @@ "fuchsia.fonts.Provider", "fuchsia.logger.LogSink", "fuchsia.media.Audio", - "fuchsia.media.drm.WidevineContentDecryptionModule", + "fuchsia.media.drm.Widevine", "fuchsia.mediacodec.CodecFactory", "fuchsia.net.NameLookup", - "fuchsia.net.SocketProvider", "fuchsia.netstack.Netstack", "fuchsia.posix.socket.Provider", "fuchsia.process.Launcher", @@ -21,6 +20,7 @@ "fuchsia.ui.input.ImeService", "fuchsia.ui.input.ImeVisibilityService", "fuchsia.ui.scenic.Scenic", + "fuchsia.vulkan.loader.Loader", "fuchsia.web.ContextProvider" ] } diff --git a/chromium/fuchsia/runners/web/web_runner_smoke_test.cc b/chromium/fuchsia/runners/web/web_runner_smoke_test.cc index 03d804e8da7..dadc81f3a1c 100644 --- a/chromium/fuchsia/runners/web/web_runner_smoke_test.cc +++ b/chromium/fuchsia/runners/web/web_runner_smoke_test.cc @@ -5,13 +5,14 @@ #include <fuchsia/modular/cpp/fidl.h> #include <fuchsia/modular/cpp/fidl_test_base.h> #include <fuchsia/sys/cpp/fidl.h> +#include <lib/sys/cpp/component_context.h> #include "base/bind.h" +#include "base/fuchsia/default_context.h" #include "base/fuchsia/scoped_service_binding.h" -#include "base/fuchsia/service_directory.h" -#include "base/fuchsia/service_directory_client.h" #include "base/fuchsia/service_provider_impl.h" #include "base/test/bind_test_util.h" +#include "base/test/task_environment.h" #include "base/test/test_timeouts.h" #include "base/threading/thread_task_runner_handle.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -36,8 +37,10 @@ class WebRunnerSmokeTest : public testing::Test { ASSERT_TRUE(test_server_.Start()); fidl::InterfaceHandle<fuchsia::io::Directory> directory; - service_directory_ = std::make_unique<base::fuchsia::ServiceDirectory>( - directory.NewRequest()); + outgoing_directory_.GetOrCreateDirectory("svc")->Serve( + fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE, + directory.NewRequest().TakeChannel()); + service_provider_ = std::make_unique<base::fuchsia::ServiceProviderImpl>( std::move(directory)); } @@ -84,9 +87,11 @@ class WebRunnerSmokeTest : public testing::Test { bool test_html_requested_ = false; bool test_image_requested_ = false; - base::MessageLoopForIO message_loop_; + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY, + base::test::TaskEnvironment::MainThreadType::IO}; - std::unique_ptr<base::fuchsia::ServiceDirectory> service_directory_; + sys::OutgoingDirectory outgoing_directory_; std::unique_ptr<base::fuchsia::ServiceProviderImpl> service_provider_; net::EmbeddedTestServer test_server_; @@ -101,8 +106,9 @@ TEST_F(WebRunnerSmokeTest, RequestHtmlAndImage) { fuchsia::sys::LaunchInfo launch_info = LaunchInfoWithServices(); launch_info.url = test_server_.GetURL("/test.html").spec(); - auto launcher = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToServiceSync<fuchsia::sys::Launcher>(); + auto launcher = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::sys::Launcher>(); fuchsia::sys::ComponentControllerSyncPtr controller; launcher->CreateComponent(std::move(launch_info), controller.NewRequest()); @@ -121,8 +127,9 @@ TEST_F(WebRunnerSmokeTest, LifecycleTerminate) { launch_info.url = test_server_.GetURL("/test.html").spec(); launch_info.directory_request = directory.NewRequest().TakeChannel(); - auto launcher = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToServiceSync<fuchsia::sys::Launcher>(); + auto launcher = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::sys::Launcher>(); fuchsia::sys::ComponentControllerPtr controller; launcher->CreateComponent(std::move(launch_info), controller.NewRequest()); @@ -151,8 +158,9 @@ TEST_F(WebRunnerSmokeTest, ComponentExitOnFrameClose) { fuchsia::sys::LaunchInfo launch_info = LaunchInfoWithServices(); launch_info.url = test_server_.GetURL("/window_close.html").spec(); - auto launcher = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToService<fuchsia::sys::Launcher>(); + auto launcher = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::sys::Launcher>(); fuchsia::sys::ComponentControllerPtr controller; launcher->CreateComponent(std::move(launch_info), controller.NewRequest()); @@ -193,12 +201,13 @@ TEST_F(WebRunnerSmokeTest, RemoveSelfFromStoryOnFrameClose) { MockModuleContext module_context; EXPECT_CALL(module_context, RemoveSelfFromStory); base::fuchsia::ScopedServiceBinding<fuchsia::modular::ModuleContext> binding( - service_directory_.get(), &module_context); + &outgoing_directory_, &module_context); launch_info.additional_services->names.emplace_back( fuchsia::modular::ModuleContext::Name_); - auto launcher = base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() - ->ConnectToService<fuchsia::sys::Launcher>(); + auto launcher = base::fuchsia::ComponentContextForCurrentProcess() + ->svc() + ->Connect<fuchsia::sys::Launcher>(); fuchsia::sys::ComponentControllerPtr controller; launcher->CreateComponent(std::move(launch_info), controller.NewRequest()); |