diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-09-01 11:08:40 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-01 12:16:21 +0000 |
commit | 03c549e0392f92c02536d3f86d5e1d8dfa3435ac (patch) | |
tree | fe49d170a929b34ba82cd10db1a0bd8e3760fa4b /chromium/fuchsia | |
parent | 5d013f5804a0d91fcf6c626b2d6fb6eca5c845b0 (diff) | |
download | qtwebengine-chromium-03c549e0392f92c02536d3f86d5e1d8dfa3435ac.tar.gz |
BASELINE: Update Chromium to 91.0.4472.160
Change-Id: I0def1f08a2412aeed79a9ab95dd50eb5c3f65f31
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/fuchsia')
73 files changed, 2201 insertions, 919 deletions
diff --git a/chromium/fuchsia/BUILD.gn b/chromium/fuchsia/BUILD.gn index eed02881545..47057485af4 100644 --- a/chromium/fuchsia/BUILD.gn +++ b/chromium/fuchsia/BUILD.gn @@ -44,6 +44,9 @@ group("gn_all") { "runners:cast_runner_unittests", "runners:web_runner", "//chromecast/bindings:bindings_manager_fuchsia", + + # Ensure this target, which is used by cipd, is built in all configurations. + "//media/cdm/library_cdm/clear_key_cdm:clear_key_cdm", ] if (is_official_build) { diff --git a/chromium/fuchsia/base/fake_component_context.cc b/chromium/fuchsia/base/fake_component_context.cc index 1120748dee8..3e523fc7b7f 100644 --- a/chromium/fuchsia/base/fake_component_context.cc +++ b/chromium/fuchsia/base/fake_component_context.cc @@ -4,8 +4,6 @@ #include "fuchsia/base/fake_component_context.h" -#include <fuchsia/base/agent_impl.h> - #include <memory> #include <string> #include <utility> @@ -13,6 +11,7 @@ #include "base/check.h" #include "base/notreached.h" #include "base/run_loop.h" +#include "fuchsia/base/agent_impl.h" namespace cr_fuchsia { diff --git a/chromium/fuchsia/base/fake_component_context.h b/chromium/fuchsia/base/fake_component_context.h index b7af7311606..35168ce126e 100644 --- a/chromium/fuchsia/base/fake_component_context.h +++ b/chromium/fuchsia/base/fake_component_context.h @@ -5,14 +5,15 @@ #ifndef FUCHSIA_BASE_FAKE_COMPONENT_CONTEXT_H_ #define FUCHSIA_BASE_FAKE_COMPONENT_CONTEXT_H_ -#include <fuchsia/base/agent_impl.h> #include <fuchsia/modular/cpp/fidl_test_base.h> + #include <map> #include <memory> #include <string> #include <utility> #include "base/strings/string_piece.h" +#include "fuchsia/base/agent_impl.h" namespace cr_fuchsia { diff --git a/chromium/fuchsia/base/init_logging.cc b/chromium/fuchsia/base/init_logging.cc index ed0a60a099c..920cfb03add 100644 --- a/chromium/fuchsia/base/init_logging.cc +++ b/chromium/fuchsia/base/init_logging.cc @@ -9,6 +9,9 @@ namespace cr_fuchsia { +// These values must match content/public/common/content_switches.cc so that +// the values will be passed to child processes in projects that Chromium's +// Content layer. constexpr char kEnableLogging[] = "enable-logging"; constexpr char kLogFile[] = "log-file"; @@ -29,4 +32,14 @@ bool InitLoggingFromCommandLine(const base::CommandLine& command_line) { return logging::InitLogging(settings); } +bool InitLoggingFromCommandLineDefaultingToStderrForTest( + base::CommandLine* command_line) { + // Set logging to stderr if not specified. + if (!command_line->HasSwitch(cr_fuchsia::kEnableLogging)) { + command_line->AppendSwitchNative(cr_fuchsia::kEnableLogging, "stderr"); + } + + return InitLoggingFromCommandLine(*command_line); +} + } // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/init_logging.h b/chromium/fuchsia/base/init_logging.h index c08ff6e1cdc..0eac356d653 100644 --- a/chromium/fuchsia/base/init_logging.h +++ b/chromium/fuchsia/base/init_logging.h @@ -11,15 +11,16 @@ class CommandLine; namespace cr_fuchsia { -// These are intended to match those in content_switches.cc. -extern const char kEnableLogging[]; -extern const char kLogFile[]; - // Configures logging for the current process based on the supplied // |command_line|. Returns false if a logging output stream could not // be created. bool InitLoggingFromCommandLine(const base::CommandLine& command_line); +// Same as InitLoggingFromCommandLine but defaults to "stderr" if the logging +// target is not specified. +bool InitLoggingFromCommandLineDefaultingToStderrForTest( + base::CommandLine* command_line); + } // namespace cr_fuchsia #endif // FUCHSIA_BASE_INIT_LOGGING_H_ diff --git a/chromium/fuchsia/base/legacymetrics_client.cc b/chromium/fuchsia/base/legacymetrics_client.cc index 431c2f73cf2..f79d4af2c33 100644 --- a/chromium/fuchsia/base/legacymetrics_client.cc +++ b/chromium/fuchsia/base/legacymetrics_client.cc @@ -13,6 +13,7 @@ #include <utility> #include <vector> +#include "base/callback_helpers.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/process_context.h" #include "base/logging.h" @@ -34,6 +35,35 @@ LegacyMetricsClient::~LegacyMetricsClient() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } +void LegacyMetricsClient::DisableAutoConnect() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(auto_connect_); + DCHECK_EQ(report_interval_, base::TimeDelta()) + << "DisableAutoConnect() must be called before Start()."; + + auto_connect_ = false; +} + +void LegacyMetricsClient::SetMetricsRecorder( + fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder> + metrics_recorder) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!auto_connect_); + + auto weak_this = weak_factory_.GetWeakPtr(); + ResetMetricsRecorderState(); + + // ResetMetricsRecorderState() may call |on_flush_complete_closures_|, which + // may destroy LegacyMetricsClient. + if (!weak_this) + return; + + SetMetricsRecorderInternal(std::move(metrics_recorder)); + + if (report_interval_ > base::TimeDelta()) + ScheduleNextReport(); +} + void LegacyMetricsClient::Start(base::TimeDelta report_interval) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_GT(report_interval, base::TimeDelta::FromSeconds(0)); @@ -42,7 +72,12 @@ void LegacyMetricsClient::Start(base::TimeDelta report_interval) { user_events_recorder_ = std::make_unique<LegacyMetricsUserActionRecorder>(); report_interval_ = report_interval; - ConnectAndStartReporting(); + + if (auto_connect_) + ConnectFromComponentContext(); + + if (metrics_recorder_) + ScheduleNextReport(); } void LegacyMetricsClient::SetReportAdditionalMetricsCallback( @@ -65,22 +100,36 @@ void LegacyMetricsClient::SetNotifyFlushCallback(NotifyFlushCallback callback) { notify_flush_callback_ = std::move(callback); } -void LegacyMetricsClient::ConnectAndStartReporting() { +void LegacyMetricsClient::ConnectFromComponentContext() { DCHECK(!metrics_recorder_) << "Trying to connect when already connected."; DVLOG(1) << "Trying to connect to MetricsRecorder service."; - metrics_recorder_ = base::ComponentContextForProcess() - ->svc() - ->Connect<fuchsia::legacymetrics::MetricsRecorder>(); + DCHECK(auto_connect_); + + fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder> + metrics_recorder; + base::ComponentContextForProcess()->svc()->Connect( + metrics_recorder.NewRequest()); + SetMetricsRecorderInternal(std::move(metrics_recorder)); + + ScheduleNextReport(); +} + +void LegacyMetricsClient::SetMetricsRecorderInternal( + fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder> + metrics_recorder) { + metrics_recorder_.Bind(std::move(metrics_recorder)); metrics_recorder_.set_error_handler(fit::bind_member( this, &LegacyMetricsClient::OnMetricsRecorderDisconnected)); metrics_recorder_.events().OnCloseSoon = fit::bind_member(this, &LegacyMetricsClient::OnCloseSoon); - ScheduleNextReport(); } void LegacyMetricsClient::ScheduleNextReport() { DCHECK(!is_flushing_); + if (report_timer_.IsRunning()) + return; + DVLOG(1) << "Scheduling next report in " << report_interval_.InSeconds() << "seconds."; report_timer_.Start(FROM_HERE, report_interval_, this, @@ -141,11 +190,11 @@ void LegacyMetricsClient::DrainBuffer() { if (is_flushing_) { metrics_recorder_.Unbind(); - std::move(on_flush_complete_).Run(); - } else { - ScheduleNextReport(); + CompleteFlush(); + return; } + ScheduleNextReport(); return; } @@ -172,31 +221,40 @@ void LegacyMetricsClient::DrainBuffer() { void LegacyMetricsClient::OnMetricsRecorderDisconnected(zx_status_t status) { ZX_LOG(ERROR, status) << "MetricsRecorder connection lost."; - // Stop reporting metric events. - report_timer_.AbandonAndStop(); - - if (status == ZX_ERR_PEER_CLOSED) { + if (auto_connect_ && status == ZX_ERR_PEER_CLOSED) { DVLOG(1) << "Scheduling reconnect after " << reconnect_delay_; // Try to reconnect with exponential backoff. reconnect_timer_.Start(FROM_HERE, reconnect_delay_, this, - &LegacyMetricsClient::ConnectAndStartReporting); + &LegacyMetricsClient::ReconnectMetricsRecorder); // Increase delay exponentially. No random jittering since we don't expect // many clients overloading the service with simultaneous reconnections. reconnect_delay_ = std::min(reconnect_delay_ * kReconnectBackoffFactor, kMaxReconnectDelay); } + + ResetMetricsRecorderState(); +} + +void LegacyMetricsClient::ReconnectMetricsRecorder() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DVLOG(1) << __func__ << " called."; + + ConnectFromComponentContext(); + ScheduleNextReport(); } void LegacyMetricsClient::FlushAndDisconnect( base::OnceClosure on_flush_complete) { DVLOG(1) << __func__ << " called."; - DCHECK(on_flush_complete); + + if (on_flush_complete) + on_flush_complete_closures_.push_back(std::move(on_flush_complete)); + if (is_flushing_) return; - on_flush_complete_ = std::move(on_flush_complete); report_timer_.AbandonAndStop(); is_flushing_ = true; @@ -211,7 +269,31 @@ void LegacyMetricsClient::FlushAndDisconnect( } void LegacyMetricsClient::OnCloseSoon() { - FlushAndDisconnect(base::DoNothing::Once()); + FlushAndDisconnect(base::OnceClosure()); +} + +void LegacyMetricsClient::CompleteFlush() { + DCHECK(is_flushing_); + + is_flushing_ = false; + + // One of the callbacks may destroy |this|, so move them all to the stack + // first. + std::vector<base::OnceClosure> on_flush_complete_closures; + on_flush_complete_closures.swap(on_flush_complete_closures_); + for (auto& closure : on_flush_complete_closures) { + std::move(closure).Run(); + } +} + +void LegacyMetricsClient::ResetMetricsRecorderState() { + // Stop reporting metric events. + report_timer_.AbandonAndStop(); + + record_ack_pending_ = false; + + if (is_flushing_) + CompleteFlush(); } } // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/legacymetrics_client.h b/chromium/fuchsia/base/legacymetrics_client.h index ce59db86134..3ed2f2f15b6 100644 --- a/chromium/fuchsia/base/legacymetrics_client.h +++ b/chromium/fuchsia/base/legacymetrics_client.h @@ -48,6 +48,17 @@ class LegacyMetricsClient { explicit LegacyMetricsClient(const LegacyMetricsClient&) = delete; LegacyMetricsClient& operator=(const LegacyMetricsClient&) = delete; + // Disables automatic MetricsRecorder connection. Caller will have to supply + // MetricsRecorder by calling SetMetricsRecorder(). Must be called before + // Start(). + void DisableAutoConnect(); + + // Sets |metrics_recorder| to use. Should be called only after + // DisableAutoConnect(). + void SetMetricsRecorder( + fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder> + metrics_recorder); + // Starts buffering data and schedules metric reporting after every // |report_interval|. void Start(base::TimeDelta report_interval); @@ -71,12 +82,18 @@ class LegacyMetricsClient { void FlushAndDisconnect(base::OnceClosure on_flush_complete); private: - void ConnectAndStartReporting(); + void ConnectFromComponentContext(); + void SetMetricsRecorderInternal( + fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder> + metrics_recorder); void ScheduleNextReport(); void StartReport(); void Report(std::vector<fuchsia::legacymetrics::Event> additional_metrics); void OnMetricsRecorderDisconnected(zx_status_t status); + void ReconnectMetricsRecorder(); void OnCloseSoon(); + void CompleteFlush(); + void ResetMetricsRecorderState(); // Incrementally sends the contents of |to_send_| to |metrics_recorder_|. void DrainBuffer(); @@ -90,12 +107,14 @@ class LegacyMetricsClient { std::vector<fuchsia::legacymetrics::Event> to_send_; std::unique_ptr<LegacyMetricsUserActionRecorder> user_events_recorder_; - fuchsia::legacymetrics::MetricsRecorderPtr metrics_recorder_; + bool auto_connect_ = true; base::RetainingOneShotTimer reconnect_timer_; + + fuchsia::legacymetrics::MetricsRecorderPtr metrics_recorder_; base::RetainingOneShotTimer report_timer_; SEQUENCE_CHECKER(sequence_checker_); - base::OnceClosure on_flush_complete_; + std::vector<base::OnceClosure> on_flush_complete_closures_; // Prevents use-after-free if |report_additional_callback_| is invoked after // |this| is destroyed. diff --git a/chromium/fuchsia/base/legacymetrics_client_unittest.cc b/chromium/fuchsia/base/legacymetrics_client_unittest.cc index 7f0978d97dd..59bb5cf6ebf 100644 --- a/chromium/fuchsia/base/legacymetrics_client_unittest.cc +++ b/chromium/fuchsia/base/legacymetrics_client_unittest.cc @@ -8,6 +8,7 @@ #include <string> #include <utility> +#include "base/callback_helpers.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/test_component_context_for_process.h" #include "base/metrics/histogram_macros.h" @@ -38,6 +39,8 @@ class TestMetricsRecorder bool IsRecordInFlight() const { return ack_callback_.has_value(); } + bool IsEmpty() const { return recorded_events_.empty(); } + std::vector<fuchsia::legacymetrics::Event> WaitForEvents() { if (recorded_events_.empty()) { base::RunLoop run_loop; @@ -132,6 +135,13 @@ class LegacyMetricsClientTest : public testing::Test { EXPECT_TRUE(service_binding_->has_clients()) << "Expected delay: " << delay; } + void SetMetricsRecorder() { + fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder> + metrics_recorder; + direct_binding_.Bind(metrics_recorder.NewRequest()); + client_.SetMetricsRecorder(std::move(metrics_recorder)); + } + protected: base::test::TaskEnvironment task_environment_; base::TestComponentContextForProcess test_context_; @@ -139,6 +149,9 @@ class LegacyMetricsClientTest : public testing::Test { std::unique_ptr<base::ScopedSingleClientServiceBinding< fuchsia::legacymetrics::MetricsRecorder>> service_binding_; + fidl::Binding<fuchsia::legacymetrics::MetricsRecorder> direct_binding_{ + &test_recorder_}; + LegacyMetricsClient client_; }; @@ -264,6 +277,51 @@ TEST_F(LegacyMetricsClientTest, ReconnectAfterServiceDisconnect) { EXPECT_FALSE(service_binding_->has_clients()); task_environment_.FastForwardBy(LegacyMetricsClient::kInitialReconnectDelay); EXPECT_TRUE(service_binding_->has_clients()); + + base::RecordComputedAction("foo"); + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + test_recorder_.SendAck(); + EXPECT_FALSE(test_recorder_.IsRecordInFlight()); +} + +TEST_F(LegacyMetricsClientTest, ServiceDisconnectWhileRecordPending) { + StartClientAndExpectConnection(); + + base::RecordComputedAction("foo"); + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + + DisconnectAndRestartMetricsService(); + EXPECT_FALSE(service_binding_->has_clients()); + test_recorder_.DropAck(); + + task_environment_.FastForwardBy(LegacyMetricsClient::kInitialReconnectDelay); + EXPECT_TRUE(service_binding_->has_clients()); + + base::RecordComputedAction("foo"); + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); +} + +TEST_F(LegacyMetricsClientTest, ServiceDisconnectWhileFlushing) { + StartClientAndExpectConnection(); + + base::RecordComputedAction("foo"); + client_.FlushAndDisconnect(base::OnceClosure()); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + + DisconnectAndRestartMetricsService(); + test_recorder_.DropAck(); + EXPECT_FALSE(service_binding_->has_clients()); + + task_environment_.FastForwardBy(LegacyMetricsClient::kInitialReconnectDelay); + EXPECT_TRUE(service_binding_->has_clients()); + + base::RecordComputedAction("foo"); + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); } TEST_F(LegacyMetricsClientTest, @@ -494,6 +552,33 @@ TEST_F(LegacyMetricsClientTest, ExplicitFlush) { EXPECT_TRUE(called); } +TEST_F(LegacyMetricsClientTest, DoubleFlush) { + client_.Start(kReportInterval); + + base::RecordComputedAction("bar"); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(test_recorder_.IsRecordInFlight()); + + bool called = false; + client_.FlushAndDisconnect( + base::BindLambdaForTesting([&called] { called = true; })); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + EXPECT_FALSE(called); + + bool called2 = false; + client_.FlushAndDisconnect( + base::BindLambdaForTesting([&called2] { called2 = true; })); + + test_recorder_.WaitForEvents(); + test_recorder_.SendAck(); + base::RunLoop().RunUntilIdle(); + + // Verify that both FlushAndDisconnect() callbacks were called. + EXPECT_TRUE(called); + EXPECT_TRUE(called2); +} + TEST_F(LegacyMetricsClientTest, ExplicitFlushMultipleBatches) { const size_t kSizeForMultipleBatches = LegacyMetricsClient::kMaxBatchSize * 2; client_.Start(kReportInterval); @@ -512,5 +597,111 @@ TEST_F(LegacyMetricsClientTest, ExplicitFlushMultipleBatches) { EXPECT_EQ("bar", events[i].user_action_event().name()); } +TEST_F(LegacyMetricsClientTest, UseInjectedMetricsRecorder) { + client_.DisableAutoConnect(); + SetMetricsRecorder(); + + client_.Start(kReportInterval); + + base::RecordComputedAction("bar"); + + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + + auto events = test_recorder_.WaitForEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ("bar", events[0].user_action_event().name()); + + // Verify that /svc wasn't used. + EXPECT_FALSE(service_binding_->has_clients()); + + // Verify that LegacyMetricsClient doesn't try to reconnect after + // MetricsRecorder has been disconnected. + direct_binding_.Unbind(); + task_environment_.FastForwardBy(LegacyMetricsClient::kInitialReconnectDelay * + 2); + EXPECT_FALSE(service_binding_->has_clients()); +} + +TEST_F(LegacyMetricsClientTest, UseInjectedMetricsRecorderReconnect) { + client_.DisableAutoConnect(); + SetMetricsRecorder(); + + client_.Start(kReportInterval); + + bool flush_complete = false; + client_.FlushAndDisconnect( + base::BindLambdaForTesting([&flush_complete] { flush_complete = true; })); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(flush_complete); + + EXPECT_TRUE(test_recorder_.IsEmpty()); + + // Set recorder again and verify that it receives metrics now. + SetMetricsRecorder(); + + base::RecordComputedAction("bar"); + + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + + auto events = test_recorder_.WaitForEvents(); + EXPECT_EQ(1u, events.size()); +} + +TEST_F(LegacyMetricsClientTest, SetMetricsRecorderDuringRecord) { + client_.DisableAutoConnect(); + SetMetricsRecorder(); + + client_.Start(kReportInterval); + + base::RecordComputedAction("bar"); + + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + test_recorder_.DropAck(); + + // Set recorder again and verify that it can receive metrics. + SetMetricsRecorder(); + + base::RecordComputedAction("bar"); + + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + + auto events = test_recorder_.WaitForEvents(); + EXPECT_EQ(2u, events.size()); +} + +TEST_F(LegacyMetricsClientTest, SetMetricsRecorderDuringFlush) { + client_.DisableAutoConnect(); + SetMetricsRecorder(); + + client_.Start(kReportInterval); + + base::RecordComputedAction("bar"); + + bool flush_complete = false; + client_.FlushAndDisconnect( + base::BindLambdaForTesting([&flush_complete] { flush_complete = true; })); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + test_recorder_.DropAck(); + EXPECT_FALSE(flush_complete); + + // Set recorder again. It's expected to complete the Flush(). + SetMetricsRecorder(); + EXPECT_TRUE(flush_complete); + + // Verify that metrics are sent to the new MetricsRecorder instance. + base::RecordComputedAction("bar"); + + task_environment_.FastForwardBy(kReportInterval); + EXPECT_TRUE(test_recorder_.IsRecordInFlight()); + + auto events = test_recorder_.WaitForEvents(); + EXPECT_EQ(2u, events.size()); +} + } // namespace } // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/mem_buffer_util.cc b/chromium/fuchsia/base/mem_buffer_util.cc index cc448f97725..2a84d0a7c53 100644 --- a/chromium/fuchsia/base/mem_buffer_util.cc +++ b/chromium/fuchsia/base/mem_buffer_util.cc @@ -20,7 +20,7 @@ namespace cr_fuchsia { bool ReadUTF8FromVMOAsUTF16(const fuchsia::mem::Buffer& buffer, - base::string16* output) { + std::u16string* output) { std::string output_utf8; if (!StringFromMemBuffer(buffer, &output_utf8)) return false; @@ -48,7 +48,7 @@ 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(char16_t)), name); } diff --git a/chromium/fuchsia/base/mem_buffer_util.h b/chromium/fuchsia/base/mem_buffer_util.h index fe7129f5a25..9a2b4613e35 100644 --- a/chromium/fuchsia/base/mem_buffer_util.h +++ b/chromium/fuchsia/base/mem_buffer_util.h @@ -19,7 +19,7 @@ namespace cr_fuchsia { // Reads the contents of |buffer|, encoded in UTF-8, to a UTF-16 string. // Returns |false| if |buffer| is not valid UTF-8. bool ReadUTF8FromVMOAsUTF16(const fuchsia::mem::Buffer& buffer, - base::string16* output); + std::u16string* output); // Creates a Fuchsia memory buffer from |data|. fuchsia::mem::Buffer MemBufferFromString(base::StringPiece data, diff --git a/chromium/fuchsia/base/message_port.cc b/chromium/fuchsia/base/message_port.cc index 891ec699665..c20f1378858 100644 --- a/chromium/fuchsia/base/message_port.cc +++ b/chromium/fuchsia/base/message_port.cc @@ -35,7 +35,7 @@ base::Optional<fuchsia::web::FrameError> BlinkMessageFromFidl( return fuchsia::web::FrameError::NO_DATA_IN_MESSAGE; } - base::string16 data_utf16; + std::u16string data_utf16; if (!cr_fuchsia::ReadUTF8FromVMOAsUTF16(fidl_message.data(), &data_utf16)) { return fuchsia::web::FrameError::BUFFER_NOT_UTF8; } @@ -359,7 +359,7 @@ base::Optional<fuchsia::web::WebMessage> FidlWebMessageFromBlink( blink_message.ports.clear(); } - base::string16 data_utf16 = std::move(blink_message.data); + std::u16string data_utf16 = std::move(blink_message.data); std::string data_utf8; if (!base::UTF16ToUTF8(data_utf16.data(), data_utf16.size(), &data_utf8)) return base::nullopt; diff --git a/chromium/fuchsia/base/test_devtools_list_fetcher.cc b/chromium/fuchsia/base/test_devtools_list_fetcher.cc index 1969f504765..79d5bbf8170 100644 --- a/chromium/fuchsia/base/test_devtools_list_fetcher.cc +++ b/chromium/fuchsia/base/test_devtools_list_fetcher.cc @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" #include "net/http/http_status_code.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" diff --git a/chromium/fuchsia/base/test_navigation_listener.cc b/chromium/fuchsia/base/test_navigation_listener.cc index f45a65fcc3f..37a3655c1ea 100644 --- a/chromium/fuchsia/base/test_navigation_listener.cc +++ b/chromium/fuchsia/base/test_navigation_listener.cc @@ -15,19 +15,6 @@ #include "fuchsia/base/mem_buffer_util.h" namespace cr_fuchsia { -namespace { - -void QuitRunLoopAndRunCallback( - base::OnceClosure quit_run_loop_closure, - TestNavigationListener::BeforeAckCallback before_ack_callback, - const fuchsia::web::NavigationState& change, - fuchsia::web::NavigationEventListener::OnNavigationStateChangedCallback - ack_callback) { - std::move(quit_run_loop_closure).Run(); - before_ack_callback.Run(change, std::move(ack_callback)); -} - -} // namespace TestNavigationListener::TestNavigationListener() { // Set up the default acknowledgement handling behavior. @@ -41,13 +28,17 @@ void TestNavigationListener::RunUntilNavigationStateMatches( DCHECK(before_ack_); // Spin the runloop until the expected conditions are met. - while (!AllFieldsMatch(expected_state)) { - base::RunLoop run_loop; - base::AutoReset<BeforeAckCallback> callback_setter( - &before_ack_, base::BindRepeating(&QuitRunLoopAndRunCallback, - run_loop.QuitClosure(), before_ack_)); - run_loop.Run(); - } + if (AllFieldsMatch(expected_state)) + return; + + base::RunLoop run_loop; + base::AutoReset<BeforeAckCallback> callback_setter( + &before_ack_, + base::BindRepeating(&TestNavigationListener::QuitLoopIfAllFieldsMatch, + base::Unretained(this), + base::Unretained(&expected_state), + run_loop.QuitClosure(), before_ack_)); + run_loop.Run(); } void TestNavigationListener::RunUntilLoaded() { @@ -106,6 +97,16 @@ void TestNavigationListener::RunUntilUrlTitleBackForwardEquals( RunUntilNavigationStateMatches(state); } +void TestNavigationListener::SetBeforeAckHook(BeforeAckCallback send_ack_cb) { + if (send_ack_cb) { + before_ack_ = send_ack_cb; + } else { + before_ack_ = base::BindRepeating( + [](const fuchsia::web::NavigationState&, + OnNavigationStateChangedCallback callback) { callback(); }); + } +} + void TestNavigationListener::OnNavigationStateChanged( fuchsia::web::NavigationState change, OnNavigationStateChangedCallback callback) { @@ -172,16 +173,6 @@ void TestNavigationListener::OnNavigationStateChanged( before_ack_.Run(change, std::move(callback)); } -void TestNavigationListener::SetBeforeAckHook(BeforeAckCallback send_ack_cb) { - if (send_ack_cb) { - before_ack_ = send_ack_cb; - } else { - before_ack_ = base::BindRepeating( - [](const fuchsia::web::NavigationState&, - OnNavigationStateChangedCallback callback) { callback(); }); - } -} - bool TestNavigationListener::AllFieldsMatch( const fuchsia::web::NavigationState& expected) { if (expected.has_url() && @@ -222,4 +213,16 @@ bool TestNavigationListener::AllFieldsMatch( return true; } +void TestNavigationListener::QuitLoopIfAllFieldsMatch( + const fuchsia::web::NavigationState* expected_state, + base::RepeatingClosure quit_run_loop_closure, + TestNavigationListener::BeforeAckCallback before_ack_callback, + const fuchsia::web::NavigationState& change, + fuchsia::web::NavigationEventListener::OnNavigationStateChangedCallback + ack_callback) { + if (AllFieldsMatch(*expected_state)) + quit_run_loop_closure.Run(); + before_ack_callback.Run(change, std::move(ack_callback)); +} + } // namespace cr_fuchsia diff --git a/chromium/fuchsia/base/test_navigation_listener.h b/chromium/fuchsia/base/test_navigation_listener.h index 6d94d5914b6..951a722a2fe 100644 --- a/chromium/fuchsia/base/test_navigation_listener.h +++ b/chromium/fuchsia/base/test_navigation_listener.h @@ -25,6 +25,9 @@ class TestNavigationListener : public fuchsia::web::NavigationEventListener { TestNavigationListener(); ~TestNavigationListener() final; + TestNavigationListener(const TestNavigationListener&) = delete; + TestNavigationListener& operator=(const TestNavigationListener&) = delete; + // Spins a RunLoop until the navigation state of the page matches the fields // of |expected_state| that have been set. void RunUntilNavigationStateMatches( @@ -78,14 +81,20 @@ class TestNavigationListener : public fuchsia::web::NavigationEventListener { fuchsia::web::NavigationState change, OnNavigationStateChangedCallback callback) final; - fuchsia::web::NavigationState current_state_; - - BeforeAckCallback before_ack_; - // Compare the current state with all fields of |expected| that have been set. bool AllFieldsMatch(const fuchsia::web::NavigationState& expected); - DISALLOW_COPY_AND_ASSIGN(TestNavigationListener); + void QuitLoopIfAllFieldsMatch( + const fuchsia::web::NavigationState* expected_state, + base::RepeatingClosure quit_run_loop_closure, + BeforeAckCallback before_ack_callback, + const fuchsia::web::NavigationState& change, + fuchsia::web::NavigationEventListener::OnNavigationStateChangedCallback + ack_callback); + + fuchsia::web::NavigationState current_state_; + + BeforeAckCallback before_ack_; }; } // namespace cr_fuchsia diff --git a/chromium/fuchsia/engine/BUILD.gn b/chromium/fuchsia/engine/BUILD.gn index 20cd491b429..32c27392368 100644 --- a/chromium/fuchsia/engine/BUILD.gn +++ b/chromium/fuchsia/engine/BUILD.gn @@ -62,7 +62,7 @@ repack("web_engine_pak") { visibility = [ ":*" ] } -foreach(locale, locales_with_fake_bidi) { +foreach(locale, locales_with_pseudolocales) { repack("web_engine_locale_${locale}_pak") { # WebEngine requires the following locale-specific resources: # 1. Locale settings (e.g. default encoding, accept-languages per locale). @@ -153,7 +153,7 @@ component("web_engine_core") { # package. deps += [ ":web_engine_pak" ] data = [ "$root_gen_dir/common_resources.pak" ] - foreach(locale, locales_with_fake_bidi) { + foreach(locale, locales_with_pseudolocales) { deps += [ ":web_engine_locale_${locale}_pak" ] data += [ "$root_gen_dir/locales/${locale}.pak" ] } @@ -283,101 +283,6 @@ _web_engine_excluded_files = [ "lib/libswiftshader_libGLESv2.so", ] -# TODO(crbug.com/1174013): Remove exclusions when DevTools discontinues the -# use of "data". -# Devtools' build configuration currently generates "data" entries that -# are inert in production but still increase the size of the production -# package. -_devtools_excluded_files = [ - "common/App.js", - "common/App.js.map", - "common/AppProvider.js", - "common/AppProvider.js.map", - "common/Base64.js", - "common/Base64.js.map", - "common/CharacterIdMap.js", - "common/CharacterIdMap.js.map", - "common/Color.js", - "common/Color.js.map", - "common/ColorUtils.js", - "common/ColorUtils.js.map", - "common/common.js", - "common/Console.js", - "common/Console.js.map", - "common/Debouncer.js", - "common/Debouncer.js.map", - "common/EventTarget.js", - "common/EventTarget.js.map", - "common/JavaScriptMetaData.js", - "common/JavaScriptMetaData.js.map", - "common/Lazy.js", - "common/Lazy.js.map", - "common/Linkifier.js", - "common/Linkifier.js.map", - "common/Object.js", - "common/Object.js.map", - "common/ParsedURL.js", - "common/ParsedURL.js.map", - "common/Progress.js", - "common/Progress.js.map", - "common/QueryParamHandler.js", - "common/QueryParamHandler.js.map", - "common/ResourceType.js", - "common/ResourceType.js.map", - "common/Revealer.js", - "common/Revealer.js.map", - "common/Runnable.js", - "common/Runnable.js.map", - "common/SegmentedRange.js", - "common/SegmentedRange.js.map", - "common/SettingRegistration.js", - "common/SettingRegistration.js.map", - "common/Settings.js", - "common/Settings.js.map", - "common/SimpleHistoryManager.js", - "common/SimpleHistoryManager.js.map", - "common/StringOutputStream.js", - "common/StringOutputStream.js.map", - "common/TextDictionary.js", - "common/TextDictionary.js.map", - "common/Throttler.js", - "common/Throttler.js.map", - "common/Trie.js", - "common/Trie.js.map", - "common/WasmDisassembly.js", - "common/WasmDisassembly.js.map", - "common/Worker.js", - "common/Worker.js.map", - "platform/array-utilities.js", - "platform/array-utilities.js.map", - "platform/date-utilities.js", - "platform/date-utilities.js.map", - "platform/keyboard-utilities.js", - "platform/keyboard-utilities.js.map", - "platform/map-utilities.js", - "platform/map-utilities.js.map", - "platform/number-utilities.js", - "platform/number-utilities.js.map", - "platform/platform.js", - "platform/set-utilities.js", - "platform/set-utilities.js.map", - "platform/string-utilities.js", - "platform/string-utilities.js.map", - "platform/typescript-utilities.js", - "platform/typescript-utilities.js.map", - "platform/UIString.js", - "platform/UIString.js.map", - "platform/utilities.js", - "platform/utilities.js.map", - "root/root.js", - "root/Runtime.js", - "root/Runtime.js.map", -] -foreach(excluded, _devtools_excluded_files) { - _web_engine_excluded_files += - [ "third_party/devtools-frontend/src/front_end/${excluded}" ] -} - # Definitions for the main web_engine package. The package contains the # context_provider component definition, and its dependencies. An installer # script is declared for the package, for ease of development. @@ -456,7 +361,9 @@ test("web_engine_browsertests") { "browser/headless_browsertest.cc", "browser/media_browsertest.cc", "browser/navigation_policy_browsertest.cc", + "browser/permissions_browsertest.cc", "browser/theme_manager_browsertest.cc", + "browser/virtual_keyboard_browsertest.cc", ] # TODO(crbug.com/1157909): Move to the list above when the bug is fixed. @@ -483,10 +390,18 @@ test("web_engine_browsertests") { "//testing/gmock", "//testing/gtest", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.accessibility.semantics", + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.input.virtualkeyboard", "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", "//ui/gfx", "//ui/ozone", ] + additional_manifest_fragments = [ + "//build/config/fuchsia/test/jit_capabilities.test-cmx", + "//build/config/fuchsia/test/network_capabilities.test-cmx", + "//build/config/fuchsia/test/present_view_capabilities.test-cmx", + "//build/config/fuchsia/test/vulkan_capabilities.test-cmx", + "//build/config/fuchsia/test/web_engine_required_capabilities.test-cmx", + ] } test("web_engine_unittests") { @@ -524,10 +439,18 @@ test("web_engine_unittests") { "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.web", "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp", ] + additional_manifest_fragments = [ + "//build/config/fuchsia/test/access_test_data_dir.test-cmx", + + # TODO(crbug.com/1185811): Figure out why jit_capabilities is needed. + "//build/config/fuchsia/test/jit_capabilities.test-cmx", + + "//build/config/fuchsia/test/network_capabilities.test-cmx", + "//build/config/fuchsia/test/vulkan_capabilities.test-cmx", + ] } test("web_engine_integration_tests") { - manifest = "web_engine_integration_tests.cmx" sources = [ "test_debug_listener.cc", "test_debug_listener.h", @@ -555,6 +478,12 @@ test("web_engine_integration_tests") { ":web_engine", "web_engine", ] ] + additional_manifest_fragments = [ + "//build/config/fuchsia/test/network_capabilities.test-cmx", + "//build/config/fuchsia/test/read_debug_data.test-cmx", + "//build/config/fuchsia/test/vulkan_capabilities.test-cmx", + "//build/config/fuchsia/test/web_engine_required_capabilities.test-cmx", + ] } cr_fuchsia_package("web_engine_shell_pkg") { @@ -581,6 +510,7 @@ fuchsia_package_runner("web_engine_shell") { } executable("web_engine_shell_exec") { + testonly = true sources = [ "test/web_engine_shell.cc" ] data = [ "test/shell_data" ] @@ -594,6 +524,8 @@ executable("web_engine_shell_exec") { "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp", "//url", ] + + visibility = [ ":*" ] } if (is_official_build) { diff --git a/chromium/fuchsia/engine/browser/accessibility_bridge.cc b/chromium/fuchsia/engine/browser/accessibility_bridge.cc index f69d6835760..4c7fb5ed3da 100644 --- a/chromium/fuchsia/engine/browser/accessibility_bridge.cc +++ b/chromium/fuchsia/engine/browser/accessibility_bridge.cc @@ -11,6 +11,7 @@ #include "base/fuchsia/fuchsia_logging.h" #include "base/logging.h" +#include "content/public/browser/global_routing_id.h" #include "content/public/browser/render_widget_host_view.h" #include "ui/accessibility/ax_action_data.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -24,6 +25,16 @@ constexpr size_t kMaxNodesPerUpdate = 16; // Error allowed for each edge when converting from gfx::RectF to gfx::Rect. constexpr float kRectConversionError = 0.5; +// Returns the id of the offset container for |node|, or the root node id if +// |node| does not specify an offset container. +int32_t GetOffsetContainerId(const ui::AXTree* tree, + const ui::AXNodeData& node_data) { + int32_t offset_container_id = node_data.relative_bounds.offset_container_id; + if (offset_container_id == -1) + return tree->root()->id(); + return offset_container_id; +} + } // namespace AccessibilityBridge::AccessibilityBridge( @@ -50,6 +61,18 @@ AccessibilityBridge::~AccessibilityBridge() { ax_trees_.clear(); } +void AccessibilityBridge::RemoveNodeFromOffsetMapping( + ui::AXTree* tree, + const ui::AXNodeData& node_data) { + auto offset_container_children_it = + offset_container_children_.find(std::make_pair( + tree->GetAXTreeID(), GetOffsetContainerId(tree, node_data))); + if (offset_container_children_it != offset_container_children_.end()) { + offset_container_children_it->second.erase( + std::make_pair(tree->GetAXTreeID(), node_data.id)); + } +} + void AccessibilityBridge::TryCommit() { if (commit_inflight_ || (to_delete_.empty() && to_update_.empty()) || ShouldHoldCommit()) @@ -242,11 +265,60 @@ void AccessibilityBridge::OnSemanticsModeChanged( callback(); } +void AccessibilityBridge::OnNodeWillBeDeleted(ui::AXTree* tree, + ui::AXNode* node) { + // Remove the node from its offset container's list of children. + RemoveNodeFromOffsetMapping(tree, node->data()); + + // Also remove the mapping from deleted node to its offset children. + offset_container_children_.erase( + std::make_pair(tree->GetAXTreeID(), node->data().id)); +} + void AccessibilityBridge::OnNodeDeleted(ui::AXTree* tree, int32_t node_id) { to_delete_.push_back( id_mapper_->ToFuchsiaNodeID(tree->GetAXTreeID(), node_id, false)); } +void AccessibilityBridge::OnNodeDataChanged( + ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) { + if (!tree) + return; + + // If this node's bounds have changed, then we should update its offset + // children's transforms to reflect the new bounds. + auto offset_container_children_it = offset_container_children_.find( + std::make_pair(tree->GetAXTreeID(), old_node_data.id)); + + // If any descendants have this node as their offset containers, and this + // node's bounds have changed, then we need to update those descendants' + // transforms to reflect the new bounds. + if (offset_container_children_it != offset_container_children_.end() && + old_node_data.relative_bounds.bounds != + new_node_data.relative_bounds.bounds) { + for (auto offset_child_id : offset_container_children_it->second) { + auto* child_node = tree->GetFromId(offset_child_id.second); + if (!child_node) { + continue; + } + + auto child_node_data = child_node->data(); + to_update_.push_back(AXNodeDataToSemanticNode( + child_node_data, new_node_data, tree->GetAXTreeID(), false, + id_mapper_.get())); + } + } + + // If this node's offset container has changed, then we should remove it from + // its old offset container's offset children. + if (old_node_data.relative_bounds.offset_container_id != + new_node_data.relative_bounds.offset_container_id) { + RemoveNodeFromOffsetMapping(tree, old_node_data); + } +} + void AccessibilityBridge::OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, @@ -266,9 +338,20 @@ void AccessibilityBridge::OnAtomicUpdateFinished( // |to_update_| are going to be executed after |to_delete_|. for (const ui::AXTreeObserver::Change& change : changes) { const auto& node = change.node->data(); + + int32_t offset_container_id = + GetOffsetContainerId(tree, change.node->data()); + const auto* container = tree->GetFromId(offset_container_id); + DCHECK(container); + + offset_container_children_[std::make_pair(tree->GetAXTreeID(), + offset_container_id)] + .insert(std::make_pair(tree->GetAXTreeID(), node.id)); + const bool is_root = is_main_frame_tree ? node.id == root_id_ : false; - to_update_.push_back(AXNodeDataToSemanticNode(node, tree->GetAXTreeID(), - is_root, id_mapper_.get())); + to_update_.push_back(AXNodeDataToSemanticNode(node, container->data(), + tree->GetAXTreeID(), is_root, + id_mapper_.get())); if (node.HasStringAttribute(ax::mojom::StringAttribute::kChildTreeId)) { const auto child_tree_id = ui::AXTreeID::FromString( node.GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId)); @@ -298,7 +381,7 @@ float AccessibilityBridge::GetDeviceScaleFactor() { return web_contents_->GetRenderWidgetHostView()->GetDeviceScaleFactor(); } -const ui::AXSerializableTree* AccessibilityBridge::ax_tree_for_test() { +ui::AXSerializableTree* AccessibilityBridge::ax_tree_for_test() { if (ax_trees_.empty()) return nullptr; @@ -349,8 +432,14 @@ void AccessibilityBridge::UpdateTreeConnections() { if (kv.second.is_connected) continue; // No work to do, trees connected and present. - auto fuchsia_node = AXNodeDataToSemanticNode( - ax_node->data(), parent_ax_tree_id, false, id_mapper_.get()); + int32_t offset_container_id = + GetOffsetContainerId(parent_tree, ax_node->data()); + const auto* container = parent_tree->GetFromId(offset_container_id); + DCHECK(container); + + auto fuchsia_node = + AXNodeDataToSemanticNode(ax_node->data(), container->data(), + parent_ax_tree_id, false, id_mapper_.get()); // Now, the connection really happens: // This node, from the parent tree, will have a child that points to the diff --git a/chromium/fuchsia/engine/browser/accessibility_bridge.h b/chromium/fuchsia/engine/browser/accessibility_bridge.h index 7b6973049cb..f3ec2de6ba8 100644 --- a/chromium/fuchsia/engine/browser/accessibility_bridge.h +++ b/chromium/fuchsia/engine/browser/accessibility_bridge.h @@ -10,8 +10,9 @@ #include <fuchsia/ui/views/cpp/fidl.h> #include <lib/fidl/cpp/binding.h> -#include <base/containers/flat_map.h> #include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" #include "base/macros.h" #include "base/optional.h" #include "content/public/browser/ax_event_notification_details.h" @@ -53,7 +54,7 @@ class WEB_ENGINE_EXPORT AccessibilityBridge AccessibilityBridge(const AccessibilityBridge&) = delete; AccessibilityBridge& operator=(const AccessibilityBridge&) = delete; - const ui::AXSerializableTree* ax_tree_for_test(); + ui::AXSerializableTree* ax_tree_for_test(); void set_event_received_callback_for_test(base::OnceClosure callback) { event_received_callback_for_test_ = std::move(callback); @@ -67,6 +68,14 @@ class WEB_ENGINE_EXPORT AccessibilityBridge FRIEND_TEST_ALL_PREFIXES(AccessibilityBridgeTest, OnSemanticsModeChanged); FRIEND_TEST_ALL_PREFIXES(AccessibilityBridgeTest, TreeModificationsAreForwarded); + FRIEND_TEST_ALL_PREFIXES(AccessibilityBridgeTest, + TransformAccountsForOffsetContainerBounds); + FRIEND_TEST_ALL_PREFIXES(AccessibilityBridgeTest, + UpdateTransformWhenContainerBoundsChange); + FRIEND_TEST_ALL_PREFIXES(AccessibilityBridgeTest, + OffsetContainerBookkeepingIsUpdated); + + using AXNodeID = std::pair<ui::AXTreeID, int32_t>; // Represents a connection between two AXTrees that are in different frames. struct TreeConnection { @@ -110,6 +119,11 @@ class WEB_ENGINE_EXPORT AccessibilityBridge // in tests. float GetDeviceScaleFactor(); + // Helper method to remove a node id from its offset container's offset + // children mapping. + void RemoveNodeFromOffsetMapping(ui::AXTree* tree, + const ui::AXNodeData& node_data); + // content::WebContentsObserver implementation. void AccessibilityEventReceived( const content::AXEventNotificationDetails& details) override; @@ -126,11 +140,15 @@ class WEB_ENGINE_EXPORT AccessibilityBridge OnSemanticsModeChangedCallback callback) final; // ui::AXTreeObserver implementation. + void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; void OnNodeDeleted(ui::AXTree* tree, int32_t node_id) override; void OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, const std::vector<ui::AXTreeObserver::Change>& changes) override; + void OnNodeDataChanged(ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) override; fuchsia::accessibility::semantics::SemanticTreePtr semantic_tree_; fidl::Binding<fuchsia::accessibility::semantics::SemanticListener> binding_; @@ -149,6 +167,10 @@ class WEB_ENGINE_EXPORT AccessibilityBridge // tree. base::flat_map<ui::AXTreeID, TreeConnection> tree_connections_; + // Maintain a map of callbacks as multiple hit test events can happen at + // once. These are keyed by the request_id field of ui::AXActionData. + base::flat_map<int, HitTestCallback> pending_hit_test_callbacks_; + // Whether semantic updates are enabled. bool enable_semantic_updates_ = false; @@ -157,9 +179,9 @@ class WEB_ENGINE_EXPORT AccessibilityBridge std::vector<fuchsia::accessibility::semantics::Node> to_update_; bool commit_inflight_ = false; - // Maintain a map of callbacks as multiple hit test events can happen at - // once. These are keyed by the request_id field of ui::AXActionData. - base::flat_map<int, HitTestCallback> pending_hit_test_callbacks_; + // Maintain a map from AXNode IDs to a list of the AXNode IDs of descendant + // nodes that have the key node ID as their offset containers. + std::map<AXNodeID, base::flat_set<AXNodeID>> offset_container_children_; // Run in the case of an internal error that cannot be recovered from. This // will cause the frame |this| is owned by to be torn down. diff --git a/chromium/fuchsia/engine/browser/accessibility_bridge_browsertest.cc b/chromium/fuchsia/engine/browser/accessibility_bridge_browsertest.cc index 778adc27323..704e90c5be7 100644 --- a/chromium/fuchsia/engine/browser/accessibility_bridge_browsertest.cc +++ b/chromium/fuchsia/engine/browser/accessibility_bridge_browsertest.cc @@ -17,6 +17,7 @@ #include "fuchsia/engine/test/web_engine_browser_test.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/ax_tree_observer.h" #include "ui/gfx/switches.h" #include "ui/ozone/public/ozone_switches.h" @@ -34,6 +35,14 @@ const char kButtonName3[] = "button 3"; const char kNodeName[] = "last node"; const char kParagraphName[] = "a third paragraph"; const char kOffscreenNodeName[] = "offscreen node"; +const char kUpdate1Name[] = "update1"; +const char kUpdate2Name[] = "update2"; +const char kUpdate3Name[] = "update3"; +const char kUpdate4Name[] = "update4"; +const char kUpdate5Name[] = "update5"; +const char kUpdate6Name[] = "update6"; +const char kUpdate7Name[] = "update7"; +const char kUpdate8Name[] = "update8"; const size_t kPage1NodeCount = 29; const size_t kPage2NodeCount = 190; const size_t kInitialRangeValue = 51; @@ -145,17 +154,6 @@ class AccessibilityBridgeTest : public cr_fuchsia::WebEngineBrowserTest { navigation_listener_.RunUntilUrlAndTitleEquals(page_url, page_title); } - // Helper function that checks if |num_deletes|, |num_updates| and - // |num_commits| match the ones in the FakeSemanticTree. - void CheckCallsToFakeSemanticTree(size_t num_deletes, - size_t num_updates, - size_t num_commits) { - auto* tree = semantics_manager_.semantic_tree(); - EXPECT_EQ(tree->num_delete_calls(), num_deletes); - EXPECT_EQ(tree->num_update_calls(), num_updates); - EXPECT_EQ(tree->num_commit_calls(), num_commits); - } - protected: fuchsia::web::FramePtr frame_ptr_; FrameImpl* frame_impl_; @@ -182,9 +180,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, CorrectDataSent) { // Batching is performed when the number of nodes to send or delete exceeds the // maximum, as set on the Fuchsia side. Check that all nodes are received by the // Semantic Tree when batching is performed. -// TODO(1168126): Number of commit calls is not deterministic in general, -// leading to flakiness. -IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_DataSentWithBatching) { +IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DataSentWithBatching) { LoadPage(kPage2Path, kPage2Title); // Run until we expect more than a batch's worth of nodes to be present. @@ -194,19 +190,17 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_DataSentWithBatching) { // Checks if the actual batching happened. EXPECT_GE(semantics_manager_.semantic_tree()->num_update_calls(), 18u); - EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u); + + // Checks if one or more commit calls were made to send the data. + EXPECT_GE(semantics_manager_.semantic_tree()->num_commit_calls(), 1u); } // Check that semantics information is correctly sent when navigating from page // to page. -// TODO(1168126): Number of commit calls is not deterministic in general, -// leading to flakiness. -IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, - DISABLED_NavigateFromPageToPage) { +IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, NavigateFromPageToPage) { LoadPage(kPage1Path, kPage1Title); semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount); - EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u); EXPECT_TRUE( semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage1Title)); @@ -217,8 +211,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, LoadPage(kPage2Path, kPage2Title); - semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage2NodeCount); - EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 2u); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kPage2Title); EXPECT_TRUE( semantics_manager_.semantic_tree()->GetNodeFromLabel(kPage2Title)); @@ -413,15 +407,10 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_Slider) { // This test makes sure that when semantic updates toggle on / off / on, the // full semantic tree is sent in the first update when back on. -// TODO(1168126): Number of commit calls is not deterministic in general, -// leading to flakiness. -IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, - DISABLED_TogglesSemanticsUpdates) { +IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TogglesSemanticsUpdates) { LoadPage(kPage1Path, kPage1Title); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(1); semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount); - EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 1u); semantics_manager_.SetSemanticsModeEnabled(false); base::RunLoop().RunUntilIdle(); @@ -434,7 +423,6 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, semantics_manager_.SetSemanticsModeEnabled(true); base::RunLoop().RunUntilIdle(); semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount); - EXPECT_EQ(semantics_manager_.semantic_tree()->num_commit_calls(), 2u); ASSERT_TRUE(frame_impl_->web_contents_for_test() ->IsWebContentsOnlyAccessibilityModeForTesting()); @@ -445,15 +433,12 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, // also forward the nodes in a way that leaves the tree in the Fuchsia side in a // valid state. Note that every time that a new tree is sent to Fuchsia, the // FakeSemantiTree checks if the tree is valid. -// TODO(1168126): Number of commit calls is not deterministic in general, -// leading to flakiness. -IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, - DISABLED_TreeModificationsAreForwarded) { +IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, TreeModificationsAreForwarded) { // Loads a page, so a real frame is created for this test. Then, several tree // operations are applied on top of it, using the AXTreeID that corresponds to // that frame. LoadPage(kPage1Path, kPage1Title); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(1); + semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount); // Fetch the AXTreeID of the main frame (the page just loaded). This ID will // be used in the operations that follow to simulate new data coming in. @@ -472,11 +457,15 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, tree_accessibility_event.updates[0].node_id_to_clear = bridge->ax_tree_for_test()->root()->id(); + // Set a name in a node so we can wait for this node to appear. This pattern + // is used throughout this test to ensure that the new data we are waiting for + // arrived. + tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name); + bridge->AccessibilityEventReceived(tree_accessibility_event); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(2); - CheckCallsToFakeSemanticTree(/*num_deletes=*/1, /*num_updates=*/3, - /*num_commits=*/2); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate1Name); // Adds a new node with ID 6. // (1 (2 (3 (4 (5 6))))) @@ -488,12 +477,12 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, update.nodes[0].child_ids.push_back(5); update.nodes[0].child_ids.push_back(6); update.nodes[1].id = 6; + update.nodes[0].SetName(kUpdate2Name); bridge->AccessibilityEventReceived( CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(3); - CheckCallsToFakeSemanticTree(/*num_deletes=*/1, /*num_updates=*/4, - /*num_commits=*/3); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate2Name); } // Removes the added node 6. @@ -507,13 +496,13 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, update.nodes[0].child_ids.push_back(5); update.nodes[1].id = 5; + update.nodes[0].SetName(kUpdate3Name); bridge->AccessibilityEventReceived( CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(4); - CheckCallsToFakeSemanticTree(/*num_deletes=*/2, /*num_updates=*/5, - /*num_commits=*/4); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate3Name); EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size); } @@ -530,13 +519,13 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, update.nodes[1].id = 4; update.nodes[2].id = 5; + update.nodes[0].SetName(kUpdate4Name); bridge->AccessibilityEventReceived( CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(5); - CheckCallsToFakeSemanticTree(/*num_deletes=*/2, /*num_updates=*/6, - /*num_commits=*/5); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate4Name); EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size); } @@ -558,13 +547,13 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, update.nodes[3].id = 4; update.nodes[4].id = 5; + update.nodes[0].SetName(kUpdate5Name); bridge->AccessibilityEventReceived( CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(6); - CheckCallsToFakeSemanticTree(/*num_deletes=*/2, /*num_updates=*/7, - /*num_commits=*/6); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate5Name); EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), tree_size); } @@ -579,13 +568,13 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, update.nodes[0].child_ids.push_back(2); update.nodes[1].id = 2; + update.nodes[0].SetName(kUpdate6Name); bridge->AccessibilityEventReceived( CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(7); - CheckCallsToFakeSemanticTree(/*num_deletes=*/3, /*num_updates=*/8, - /*num_commits=*/7); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate6Name); EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 2u); } @@ -600,13 +589,13 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, update.nodes[0].child_ids.push_back(2); update.nodes[1].id = 2; + update.nodes[0].SetName(kUpdate7Name); bridge->AccessibilityEventReceived( CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(8); - CheckCallsToFakeSemanticTree(/*num_deletes=*/4, /*num_updates=*/9, - /*num_commits=*/8); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate7Name); EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 2u); } @@ -618,21 +607,335 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, update.node_id_to_clear = 7; update.nodes.resize(1); update.nodes[0].id = 1; + update.nodes[0].SetName(kUpdate8Name); bridge->AccessibilityEventReceived( CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(9); - CheckCallsToFakeSemanticTree(/*num_deletes=*/5, /*num_updates=*/10, - /*num_commits=*/9); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate8Name); EXPECT_EQ(semantics_manager_.semantic_tree()->tree_size(), 1u); } } -// TODO(crbug.com/1167266): Number of commit calls is not deterministic in -// general, leading to flakiness. Hard-wired wait for 200ms may also lead to -// expectations failures if the system running tests is overloaded. -IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_OutOfProcessIframe) { +// Verifies that offset container bookkeeping is updated correctly. +IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, + OffsetContainerBookkeepingIsUpdated) { + // Loads a page, so a real frame is created for this test. Then, several tree + // operations are applied on top of it, using the AXTreeID that corresponds to + // that frame. + LoadPage(kPage1Path, kPage1Title); + semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount); + + // Fetch the AXTreeID of the main frame (the page just loaded). This ID will + // be used in the operations that follow to simulate new data coming in. + auto tree_id = + frame_impl_->web_contents_for_test()->GetMainFrame()->GetAXTreeID(); + + AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test(); + size_t tree_size = 5; + + // The tree has the following form: (1 (2 (3 (4 (5))))) + auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size); + tree_accessibility_event.ax_tree_id = tree_id; + + // The root of this tree needs to be cleared (because it holds the page just + // loaded, and we are loading something completely new). + tree_accessibility_event.updates[0].node_id_to_clear = + bridge->ax_tree_for_test()->root()->id(); + + // Set a name in a node so we can wait for this node to appear. This pattern + // is used throughout this test to ensure that the new data we are waiting for + // arrived. + tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name); + + bridge->AccessibilityEventReceived(tree_accessibility_event); + + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate1Name); + + // Adds a new node with ID 6. + // (1 (2 (3 (4 (5 6))))) + { + ui::AXTreeUpdate update; + update.root_id = 1; + update.nodes.resize(2); + update.nodes[0].id = 4; + update.nodes[0].child_ids.push_back(5); + update.nodes[0].child_ids.push_back(6); + update.nodes[1].id = 6; + update.nodes[1].relative_bounds.offset_container_id = 3; + update.nodes[1].SetName(kUpdate2Name); + + bridge->AccessibilityEventReceived( + CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate2Name); + } + + // Then, verify that offset container bookkeeping was updated. + { + auto* tree = bridge->ax_tree_for_test(); + auto offset_container_children_it = bridge->offset_container_children_.find( + std::make_pair(tree->GetAXTreeID(), 3)); + EXPECT_NE(offset_container_children_it, + bridge->offset_container_children_.end()); + const auto& offset_children = offset_container_children_it->second; + EXPECT_EQ(offset_children.size(), 1u); + EXPECT_TRUE(offset_children.count(std::make_pair(tree->GetAXTreeID(), 6))); + } + + // Now, change node 6's offset container to be node 4. + { + ui::AXTreeUpdate update; + update.root_id = 1; + update.nodes.resize(1); + update.nodes[0].id = 6; + update.nodes[0].relative_bounds.offset_container_id = 4; + update.nodes[0].SetName(kUpdate3Name); + + bridge->AccessibilityEventReceived( + CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate3Name); + } + + // Then, verify that offset container bookkeeping was updated. + { + // Check that node 6 was deleted from node 3's offset children. + auto* tree = bridge->ax_tree_for_test(); + auto offset_container_children_it = bridge->offset_container_children_.find( + std::make_pair(tree->GetAXTreeID(), 3)); + EXPECT_NE(offset_container_children_it, + bridge->offset_container_children_.end()); + const auto& offset_children = offset_container_children_it->second; + EXPECT_TRUE(offset_children.empty()); + + // Check that node 6 was added to node 4's offset children. + auto new_offset_container_children_it = + bridge->offset_container_children_.find( + std::make_pair(tree->GetAXTreeID(), 4)); + EXPECT_NE(offset_container_children_it, + bridge->offset_container_children_.end()); + const auto& new_offset_children = new_offset_container_children_it->second; + EXPECT_EQ(new_offset_children.size(), 1u); + EXPECT_TRUE( + new_offset_children.count(std::make_pair(tree->GetAXTreeID(), 6))); + } + + // Removes the added node 6. + // (1 (2 (3 (4 (5))))) + { + ui::AXTreeUpdate update; + update.root_id = 1; + update.node_id_to_clear = 4; + update.nodes.resize(2); + update.nodes[0].id = 4; + update.nodes[0].child_ids.push_back(5); + + update.nodes[1].id = 5; + update.nodes[1].SetName(kUpdate4Name); + + bridge->AccessibilityEventReceived( + CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate4Name); + } + + // Verify that node 6 was removed as an offset child of node 4. + { + auto* tree = bridge->ax_tree_for_test(); + auto offset_container_children_it = bridge->offset_container_children_.find( + std::make_pair(tree->GetAXTreeID(), 4)); + EXPECT_NE(offset_container_children_it, + bridge->offset_container_children_.end()); + const auto& offset_children = offset_container_children_it->second; + EXPECT_TRUE(offset_children.empty()); + } + + // Removes node 4. + // (1 (2 (3 ))) + { + ui::AXTreeUpdate update; + update.root_id = 1; + update.node_id_to_clear = 3; + update.nodes.resize(1); + update.nodes[0].id = 3; + update.nodes[0].SetName(kUpdate5Name); + + bridge->AccessibilityEventReceived( + CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate5Name); + } + + // Verify that node 4 was cleared from the offset children map. + { + auto* tree = bridge->ax_tree_for_test(); + EXPECT_FALSE(bridge->offset_container_children_.count( + std::make_pair(tree->GetAXTreeID(), 4))); + } +} + +// This test verifies that a node's transform includes a translation for its +// offset container's bounds. +IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, + TransformAccountsForOffsetContainerBounds) { + // Loads a page, so a real frame is created for this test. Then, several tree + // operations are applied on top of it, using the AXTreeID that corresponds to + // that frame. + LoadPage(kPage1Path, kPage1Title); + semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount); + + // Fetch the AXTreeID of the main frame (the page just loaded). This ID will + // be used in the operations that follow to simulate new data coming in. + auto tree_id = + frame_impl_->web_contents_for_test()->GetMainFrame()->GetAXTreeID(); + + AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test(); + size_t tree_size = 5; + + // The tree has the following form: (1 (2 (3 (4 (5))))) + auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size); + tree_accessibility_event.ax_tree_id = tree_id; + + // The root of this tree needs to be cleared (because it holds the page just + // loaded, and we are loading something completely new). + tree_accessibility_event.updates[0].node_id_to_clear = + bridge->ax_tree_for_test()->root()->id(); + + // Set a name in a node so we can wait for this node to appear. This pattern + // is used throughout this test to ensure that the new data we are waiting for + // arrived. + tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name); + + bridge->AccessibilityEventReceived(tree_accessibility_event); + + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate1Name); + + const char kNodeName[] = "transfrom should update"; + // Changes the bounds of node 1. + // (1 (2 (3 (4 (5))))) + ui::AXTreeUpdate update; + update.root_id = 1; + update.nodes.resize(2); + update.nodes[0].id = 1; + // Update the relative bounds of node 1, which is node 2's offset container. + update.nodes[0].relative_bounds.bounds = gfx::RectF(2, 3, 4, 5); + update.nodes[0].child_ids = {2}; + update.nodes[0].SetName(kUpdate2Name); + update.nodes[1].id = 2; + update.nodes[1].SetName(kNodeName); + update.nodes[1].relative_bounds.offset_container_id = 1; + // Node 2 should have non-trivial relative bounds to ensure that the + // accessibility bridge correctly composes node 2's transform and the + // translation for node 1's bounds. + update.nodes[1].relative_bounds.bounds = gfx::RectF(10, 11, 10, 11); + update.nodes[1].relative_bounds.transform = std::make_unique<gfx::Transform>( + 5, 0, 0, 100, 0, 5, 0, 200, 0, 0, 5, 0, 0, 0, 0, 1); + bridge->AccessibilityEventReceived( + CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate2Name); + + auto* tree = bridge->ax_tree_for_test(); + auto* updated_node = tree->GetFromId(2); + ASSERT_TRUE(updated_node); + + // Verify that the transform for the Fuchsia semantic node corresponding to + // node 2 reflects the new bounds of node 1. + fuchsia::accessibility::semantics::Node* fuchsia_node = + semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName); + ASSERT_TRUE(fuchsia_node); + // A Fuchsia node's semantic transform should include an offset for its parent + // node as a post-translation on top of its existing transform. Therefore, the + // x, y, and z scale (indices 0, 5, and 10, respectively) should remain + // unchanged, and the x and y bounds of the offset container should be added + // to the node's existing translation entries (indices 12 and 13). + EXPECT_EQ(fuchsia_node->transform().matrix[0], 5); + EXPECT_EQ(fuchsia_node->transform().matrix[5], 5); + EXPECT_EQ(fuchsia_node->transform().matrix[10], 5); + EXPECT_EQ(fuchsia_node->transform().matrix[12], 102); + EXPECT_EQ(fuchsia_node->transform().matrix[13], 203); +} + +// This test verifies that a node's transform is updated correctly when its +// container's relative bounds change. +// NOTE: This test is distinct from the above test case. +IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, + UpdateTransformWhenContainerBoundsChange) { + // Loads a page, so a real frame is created for this test. Then, several tree + // operations are applied on top of it, using the AXTreeID that corresponds to + // that frame. + LoadPage(kPage1Path, kPage1Title); + semantics_manager_.semantic_tree()->RunUntilNodeCountAtLeast(kPage1NodeCount); + + // Fetch the AXTreeID of the main frame (the page just loaded). This ID will + // be used in the operations that follow to simulate new data coming in. + auto tree_id = + frame_impl_->web_contents_for_test()->GetMainFrame()->GetAXTreeID(); + + AccessibilityBridge* bridge = frame_impl_->accessibility_bridge_for_test(); + size_t tree_size = 5; + + // The tree has the following form: (1 (2 (3 (4 (5))))) + auto tree_accessibility_event = CreateTreeAccessibilityEvent(tree_size); + tree_accessibility_event.ax_tree_id = tree_id; + + // The root of this tree needs to be cleared (because it holds the page just + // loaded, and we are loading something completely new). + tree_accessibility_event.updates[0].node_id_to_clear = + bridge->ax_tree_for_test()->root()->id(); + + // Set a name in a node so we can wait for this node to appear. This pattern + // is used throughout this test to ensure that the new data we are waiting for + // arrived. + tree_accessibility_event.updates[0].nodes[0].SetName(kUpdate1Name); + + bridge->AccessibilityEventReceived(tree_accessibility_event); + + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate1Name); + + // Ensure that the accessibility bridge's offset container bookkeeping is up + // to date. + bridge->offset_container_children_[std::make_pair(tree_id, 1)].insert( + std::make_pair(tree_id, 2)); + + const char kNodeName[] = "transfrom should update"; + // Changes the bounds of node 1. + // (1 (2 (3 (4 (5))))) + ui::AXTreeUpdate update; + update.root_id = 1; + update.nodes.resize(2); + update.nodes[0].id = 1; + // Update the relative bounds of node 1, which is node 2's offset container. + update.nodes[0].relative_bounds.bounds = gfx::RectF(2, 3, 4, 5); + update.nodes[0].child_ids = {2}; + update.nodes[0].SetName(kUpdate2Name); + update.nodes[1].id = 2; + update.nodes[1].SetName(kNodeName); + bridge->AccessibilityEventReceived( + CreateAccessibilityEventWithUpdate(std::move(update), tree_id)); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kUpdate2Name); + + // Verify that the transform for the Fuchsia semantic node corresponding to + // node 2 reflects the new bounds of node 1. + fuchsia::accessibility::semantics::Node* fuchsia_node = + semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName); + + // A Fuchsia node's semantic transform should include an offset for its parent + // node as a post-translation on top of its existing transform. Therefore, the + // x, y, and z scale (indices 0, 5, and 10, respectively) should remain + // unchanged, and the x and y bounds of the offset container should be added + // to the node's existing translation entries (indices 12 and 13). + EXPECT_EQ(fuchsia_node->transform().matrix[12], 2); + EXPECT_EQ(fuchsia_node->transform().matrix[13], 3); +} + +IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, OutOfProcessIframe) { constexpr int64_t kBindingsId = 1234; // Start a different embedded test server, and load a page on it. The URL for @@ -657,15 +960,11 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_OutOfProcessIframe) { }); LoadPage(kPageIframePath, "iframe loaded"); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(1); - - // Run message loop for 200ms to ensure that all AX updates from the iframes - // are processed. - base::RunLoop run_loop; - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, run_loop.QuitClosure(), - base::TimeDelta::FromMilliseconds(200)); - run_loop.Run(); + // Run until the title of the iframe page is in the semantic tree. Because + // the iframe's semantic tree is only sent when it is connected to the parent + // tree, it is guaranteed that both trees will be present. + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kPage1Title); // Two frames should be present. int num_frames = frame_impl_->web_contents_for_test()->GetAllFrames().size(); @@ -691,7 +990,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_OutOfProcessIframe) { CHECK(result.is_response()); }); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(4); + semantics_manager_.semantic_tree()->RunUntilNodeWithLabelIsInTree( + kPage2Title); // check that the iframe navigated to a different page. EXPECT_TRUE(semantics_manager_.semantic_tree()->GetNodeFromLabel(kNodeName)); @@ -704,7 +1004,12 @@ IN_PROC_BROWSER_TEST_F(AccessibilityBridgeTest, DISABLED_OutOfProcessIframe) { // away. LoadPage(kPage2Path, kPage2Title); - semantics_manager_.semantic_tree()->RunUntilCommitCountIs(5); + // Wait for the root to be updated, which means that we navigated to a new + // page. + base::RunLoop run_loop; + semantics_manager_.semantic_tree()->SetNodeUpdatedCallback( + 0u, run_loop.QuitClosure()); + run_loop.Run(); // We've navigated to a different page that has no iframes. Only one frame // should be present. diff --git a/chromium/fuchsia/engine/browser/ax_tree_converter.cc b/chromium/fuchsia/engine/browser/ax_tree_converter.cc index 5a73bcd06f5..f315544e941 100644 --- a/chromium/fuchsia/engine/browser/ax_tree_converter.cc +++ b/chromium/fuchsia/engine/browser/ax_tree_converter.cc @@ -213,6 +213,7 @@ fuchsia::ui::gfx::mat4 ConvertTransform(gfx::Transform* transform) { fuchsia::accessibility::semantics::Node AXNodeDataToSemanticNode( const ui::AXNodeData& node, + const ui::AXNodeData& container_node, const ui::AXTreeID& tree_id, bool is_root, NodeIDMapper* id_mapper) { @@ -226,9 +227,19 @@ fuchsia::accessibility::semantics::Node AXNodeDataToSemanticNode( fuchsia_node.set_child_ids( ConvertChildIds(node.child_ids, tree_id, id_mapper)); fuchsia_node.set_location(ConvertBoundingBox(node.relative_bounds.bounds)); + fuchsia_node.set_container_id( + id_mapper->ToFuchsiaNodeID(tree_id, container_node.id, false)); + + // The transform field must be handled carefully to account for + // the offsetting implied by the offset container's relative bounds. + gfx::Transform transform; if (node.relative_bounds.transform) { - fuchsia_node.set_transform( - ConvertTransform(node.relative_bounds.transform.get())); + transform = *node.relative_bounds.transform; + } + transform.PostTranslate(container_node.relative_bounds.bounds.x(), + container_node.relative_bounds.bounds.y()); + if (!transform.IsIdentity()) { + fuchsia_node.set_transform(ConvertTransform(&transform)); } return fuchsia_node; diff --git a/chromium/fuchsia/engine/browser/ax_tree_converter.h b/chromium/fuchsia/engine/browser/ax_tree_converter.h index bcef03e27d6..a7ec5340e26 100644 --- a/chromium/fuchsia/engine/browser/ax_tree_converter.h +++ b/chromium/fuchsia/engine/browser/ax_tree_converter.h @@ -5,12 +5,12 @@ #ifndef FUCHSIA_ENGINE_BROWSER_AX_TREE_CONVERTER_H_ #define FUCHSIA_ENGINE_BROWSER_AX_TREE_CONVERTER_H_ -#include <base/containers/flat_map.h> -#include <base/optional.h> #include <fuchsia/accessibility/semantics/cpp/fidl.h> #include <unordered_map> +#include "base/containers/flat_map.h" +#include "base/optional.h" #include "content/public/browser/ax_event_notification_details.h" #include "fuchsia/engine/web_engine_export.h" @@ -65,6 +65,7 @@ class WEB_ENGINE_EXPORT NodeIDMapper { // accepts partial updates, so |node| does not require all fields to be set. WEB_ENGINE_EXPORT fuchsia::accessibility::semantics::Node AXNodeDataToSemanticNode(const ui::AXNodeData& node, + const ui::AXNodeData& container_node, const ui::AXTreeID& tree_id, bool is_root, NodeIDMapper* id_mapper); diff --git a/chromium/fuchsia/engine/browser/ax_tree_converter_unittest.cc b/chromium/fuchsia/engine/browser/ax_tree_converter_unittest.cc index 6c50b85deb2..d6fd990d31c 100644 --- a/chromium/fuchsia/engine/browser/ax_tree_converter_unittest.cc +++ b/chromium/fuchsia/engine/browser/ax_tree_converter_unittest.cc @@ -23,6 +23,7 @@ const char kLabel1[] = "label nodes, not people"; const char kLabel2[] = "fancy stickers"; const char kDescription1[] = "this node does some stuff"; const char kValue1[] = "user entered value"; +const int32_t kRootId = 182; const int32_t kChildId1 = 23901; const int32_t kChildId2 = 484345; const int32_t kChildId3 = 4156877; @@ -30,8 +31,8 @@ const int32_t kRectX = 1; const int32_t kRectY = 2; const int32_t kRectWidth = 7; const int32_t kRectHeight = 8; -const std::array<float, 16> k4DIdentityMatrix = {1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1}; +const std::array<float, 16> k4DIdentityMatrixWithDefaultOffset = { + 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 0, 1}; class MockNodeIDMapper : public NodeIDMapper { public: @@ -55,7 +56,10 @@ ui::AXNodeData CreateAXNodeData(ax::mojom::Role role, ui::AXNodeData node; node.id = 2; node.role = role; - node.AddAction(action); + if (action != ax::mojom::Action::kNone) { + node.AddAction(action); + } + node.AddIntAttribute(ax::mojom::IntAttribute::kCheckedState, static_cast<int32_t>(checked_state)); node.child_ids = child_ids; @@ -86,6 +90,7 @@ Node CreateSemanticNode(uint32_t id, node.set_child_ids(child_ids); node.set_location(location); node.set_transform(transform); + node.set_container_id(kRootId); return node; } @@ -96,6 +101,7 @@ std::pair<ui::AXNodeData, Node> CreateSemanticNodeAllFieldsSet() { relative_bounds.transform = std::make_unique<gfx::Transform>(gfx::Transform::kSkipInitialization); relative_bounds.transform->MakeIdentity(); + relative_bounds.offset_container_id = -1; auto ax_node_data = CreateAXNodeData( ax::mojom::Role::kButton, ax::mojom::Action::kFocus, std::vector<int32_t>{kChildId1, kChildId2, kChildId3}, relative_bounds, @@ -114,7 +120,7 @@ std::pair<ui::AXNodeData, Node> CreateSemanticNodeAllFieldsSet() { box.max = scenic::NewVector3({kRectX + kRectWidth, kRectY + kRectHeight, 0.0f}); fuchsia::ui::gfx::Matrix4Value mat = - scenic::NewMatrix4Value(k4DIdentityMatrix); + scenic::NewMatrix4Value(k4DIdentityMatrixWithDefaultOffset); States states; states.set_checked_state(CheckedState::MIXED); states.set_hidden(false); @@ -133,10 +139,24 @@ std::pair<ui::AXNodeData, Node> CreateSemanticNodeAllFieldsSet() { class AXTreeConverterTest : public testing::Test { public: - AXTreeConverterTest() = default; + AXTreeConverterTest() { + ui::AXRelativeBounds relative_bounds = ui::AXRelativeBounds(); + relative_bounds.bounds = + gfx::RectF(kRectX, kRectY, kRectWidth, kRectHeight); + root_node_data_ = + CreateAXNodeData(ax::mojom::Role::kNone, ax::mojom::Action::kNone, + std::vector<int32_t>{}, relative_bounds, "", "", + ax::mojom::CheckedState::kNone); + root_node_data_.id = kRootId; + } ~AXTreeConverterTest() override = default; + ui::AXNodeData& root_node() { return root_node_data_; } + DISALLOW_COPY_AND_ASSIGN(AXTreeConverterTest); + + private: + ui::AXNodeData root_node_data_; }; TEST_F(AXTreeConverterTest, AllFieldsSetAndEqual) { @@ -146,10 +166,40 @@ TEST_F(AXTreeConverterTest, AllFieldsSetAndEqual) { MockNodeIDMapper mapper; auto converted_node = AXNodeDataToSemanticNode( - source_node_data, ui::AXTreeID::CreateNewAXTreeID(), false, &mapper); + source_node_data, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, + &mapper); EXPECT_TRUE(fidl::Equals(converted_node, expected_node)); } +TEST_F(AXTreeConverterTest, TransformAccountsForContainerOffset) { + ui::AXNodeData container_node_data; + container_node_data.id = 0; + container_node_data.relative_bounds = ui::AXRelativeBounds(); + container_node_data.relative_bounds.bounds = + gfx::RectF(100 /* x */, 200 /* y */, 10 /* width */, 20 /* height */); + + ui::AXNodeData child_node_data; + child_node_data.id = 1; + child_node_data.relative_bounds.transform = std::make_unique<gfx::Transform>( + 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + MockNodeIDMapper mapper; + auto converted_node = AXNodeDataToSemanticNode( + child_node_data, container_node_data, ui::AXTreeID::CreateNewAXTreeID(), + false, &mapper); + + Node expected_node; + expected_node.set_node_id(1); + fuchsia::ui::gfx::BoundingBox box; + expected_node.set_location(std::move(box)); + // The fuchsia node transform should include a post-translation for the + // container node's relative bounds. + auto expected_transform = scenic::NewMatrix4Value( + {2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 100, 200, 0, 1}); + expected_node.set_transform(expected_transform.value); + EXPECT_EQ(converted_node.transform().matrix, + expected_node.transform().matrix); +} + TEST_F(AXTreeConverterTest, SomeFieldsSetAndEqual) { ui::AXNodeData source_node_data; source_node_data.id = 0; @@ -162,7 +212,8 @@ TEST_F(AXTreeConverterTest, SomeFieldsSetAndEqual) { MockNodeIDMapper mapper; auto converted_node = AXNodeDataToSemanticNode( - source_node_data, ui::AXTreeID::CreateNewAXTreeID(), false, &mapper); + source_node_data, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, + &mapper); Node expected_node; expected_node.set_node_id(0); @@ -178,7 +229,10 @@ TEST_F(AXTreeConverterTest, SomeFieldsSetAndEqual) { expected_node.set_attributes(std::move(attributes)); fuchsia::ui::gfx::BoundingBox box; expected_node.set_location(std::move(box)); - + expected_node.set_container_id(kRootId); + fuchsia::ui::gfx::Matrix4Value mat = + scenic::NewMatrix4Value(k4DIdentityMatrixWithDefaultOffset); + expected_node.set_transform(mat.value); EXPECT_TRUE(fidl::Equals(converted_node, expected_node)); } @@ -195,7 +249,8 @@ TEST_F(AXTreeConverterTest, FieldMismatch) { MockNodeIDMapper mapper; auto converted_node = AXNodeDataToSemanticNode( - source_node_data, ui::AXTreeID::CreateNewAXTreeID(), false, &mapper); + source_node_data, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, + &mapper); Attributes attributes; attributes.set_label(kLabel1); @@ -208,7 +263,7 @@ TEST_F(AXTreeConverterTest, FieldMismatch) { box.max = scenic::NewVector3({kRectX + kRectWidth, kRectY + kRectHeight, 0.0f}); fuchsia::ui::gfx::Matrix4Value mat = - scenic::NewMatrix4Value(k4DIdentityMatrix); + scenic::NewMatrix4Value(k4DIdentityMatrixWithDefaultOffset); auto expected_node = CreateSemanticNode( source_node_data.id, Role::HEADER, std::move(attributes), std::move(states), std::vector<Action>{Action::SET_VALUE}, @@ -223,15 +278,17 @@ TEST_F(AXTreeConverterTest, FieldMismatch) { modified_node_data.AddStringAttribute(ax::mojom::StringAttribute::kName, kLabel2); - converted_node = AXNodeDataToSemanticNode( - modified_node_data, ui::AXTreeID::CreateNewAXTreeID(), false, &mapper); + converted_node = AXNodeDataToSemanticNode(modified_node_data, root_node(), + ui::AXTreeID::CreateNewAXTreeID(), + false, &mapper); EXPECT_FALSE(fidl::Equals(converted_node, expected_node)); // The same as above, this time changing |child_ids|. modified_node_data = source_node_data; modified_node_data.child_ids = std::vector<int32_t>{}; - converted_node = AXNodeDataToSemanticNode( - modified_node_data, ui::AXTreeID::CreateNewAXTreeID(), false, &mapper); + converted_node = AXNodeDataToSemanticNode(modified_node_data, root_node(), + ui::AXTreeID::CreateNewAXTreeID(), + false, &mapper); EXPECT_FALSE(fidl::Equals(converted_node, expected_node)); } @@ -248,7 +305,8 @@ TEST_F(AXTreeConverterTest, LocationFieldRespectsTypeInvariants) { MockNodeIDMapper mapper; auto converted_node = AXNodeDataToSemanticNode( - source_node_data, ui::AXTreeID::CreateNewAXTreeID(), false, &mapper); + source_node_data, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, + &mapper); // The type definition of the location field requires that in order to be // interpreted as having non-zero length in a dimension, the min must be less @@ -272,7 +330,8 @@ TEST_F(AXTreeConverterTest, DefaultAction) { MockNodeIDMapper mapper; auto converted_node = AXNodeDataToSemanticNode( - source_node_data, ui::AXTreeID::CreateNewAXTreeID(), false, &mapper); + source_node_data, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, + &mapper); EXPECT_TRUE(fidl::Equals(converted_node, expected_node)); } @@ -323,58 +382,67 @@ TEST_F(AXTreeConverterTest, ConvertRoles) { ui::AXNodeData node; node.id = 0; node.role = ax::mojom::Role::kButton; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::BUTTON, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::BUTTON, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); node.role = ax::mojom::Role::kCheckBox; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::CHECK_BOX, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::CHECK_BOX, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); node.role = ax::mojom::Role::kHeader; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::HEADER, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::HEADER, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); node.role = ax::mojom::Role::kImage; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::IMAGE, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::IMAGE, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); node.role = ax::mojom::Role::kLink; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::LINK, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::LINK, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); node.role = ax::mojom::Role::kRadioButton; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::RADIO_BUTTON, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::RADIO_BUTTON, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); node.role = ax::mojom::Role::kSlider; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::SLIDER, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::SLIDER, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); node.role = ax::mojom::Role::kTextField; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::TEXT_FIELD, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::TEXT_FIELD, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); node.role = ax::mojom::Role::kStaticText; - EXPECT_EQ(fuchsia::accessibility::semantics::Role::STATIC_TEXT, - AXNodeDataToSemanticNode(node, ui::AXTreeID::CreateNewAXTreeID(), - false, &mapper) - .role()); + EXPECT_EQ( + fuchsia::accessibility::semantics::Role::STATIC_TEXT, + AXNodeDataToSemanticNode( + node, root_node(), ui::AXTreeID::CreateNewAXTreeID(), false, &mapper) + .role()); } } // namespace diff --git a/chromium/fuchsia/engine/browser/content_directory_loader_factory.cc b/chromium/fuchsia/engine/browser/content_directory_loader_factory.cc index 0a81d4ae30e..80ed7ccd8de 100644 --- a/chromium/fuchsia/engine/browser/content_directory_loader_factory.cc +++ b/chromium/fuchsia/engine/browser/content_directory_loader_factory.cc @@ -401,7 +401,6 @@ net::Error ContentDirectoryLoaderFactory::OpenFileFromDirectory( void ContentDirectoryLoaderFactory::CreateLoaderAndStart( mojo::PendingReceiver<network::mojom::URLLoader> loader, - int32_t routing_id, int32_t request_id, uint32_t options, const network::ResourceRequest& request, @@ -454,11 +453,10 @@ void ContentDirectoryLoaderFactory::CreateLoaderAndStart( // Load the resource on a blocking-capable TaskRunner. task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ContentDirectoryURLLoader::CreateAndStart, - base::Passed(std::move(loader)), request, - base::Passed(std::move(client)), - base::Passed(std::move(file_handle)), - base::Passed(std::move(metadata_handle)))); + FROM_HERE, + base::BindOnce(&ContentDirectoryURLLoader::CreateAndStart, + std::move(loader), request, std::move(client), + std::move(file_handle), std::move(metadata_handle))); } void ContentDirectoryLoaderFactory::SetContentDirectoriesForTest( diff --git a/chromium/fuchsia/engine/browser/content_directory_loader_factory.h b/chromium/fuchsia/engine/browser/content_directory_loader_factory.h index 218b90eb35b..1fc4e8795a9 100644 --- a/chromium/fuchsia/engine/browser/content_directory_loader_factory.h +++ b/chromium/fuchsia/engine/browser/content_directory_loader_factory.h @@ -45,7 +45,6 @@ class ContentDirectoryLoaderFactory // network::mojom::URLLoaderFactory: void CreateLoaderAndStart( mojo::PendingReceiver<network::mojom::URLLoader> loader, - int32_t routing_id, int32_t request_id, uint32_t options, const network::ResourceRequest& request, diff --git a/chromium/fuchsia/engine/browser/context_impl.cc b/chromium/fuchsia/engine/browser/context_impl.cc index 5aadde8d4aa..92ae702522f 100644 --- a/chromium/fuchsia/engine/browser/context_impl.cc +++ b/chromium/fuchsia/engine/browser/context_impl.cc @@ -22,14 +22,13 @@ #include "third_party/blink/public/common/web_preferences/web_preferences.h" #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h" -ContextImpl::ContextImpl(content::BrowserContext* browser_context, - WebEngineDevToolsController* devtools_controller) - : browser_context_(browser_context), +ContextImpl::ContextImpl( + std::unique_ptr<content::BrowserContext> browser_context, + WebEngineDevToolsController* devtools_controller) + : browser_context_(std::move(browser_context)), devtools_controller_(devtools_controller), - cookie_manager_(base::BindRepeating( - &content::StoragePartition::GetNetworkContext, - base::Unretained(content::BrowserContext::GetDefaultStoragePartition( - browser_context_)))) { + cookie_manager_(base::BindRepeating(&ContextImpl::GetNetworkContext, + base::Unretained(this))) { DCHECK(browser_context_); DCHECK(devtools_controller_); devtools_controller_->OnContextCreated(); @@ -73,7 +72,8 @@ void ContextImpl::CreateFrameWithParams( } // Create a WebContents to host the new Frame. - content::WebContents::CreateParams create_params(browser_context_, nullptr); + content::WebContents::CreateParams create_params(browser_context_.get(), + nullptr); create_params.initially_hidden = true; auto web_contents = content::WebContents::Create(create_params); @@ -211,6 +211,7 @@ FrameImpl* ContextImpl::GetFrameImplForTest( network::mojom::NetworkContext* ContextImpl::GetNetworkContext() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - return content::BrowserContext::GetDefaultStoragePartition(browser_context_) + return content::BrowserContext::GetDefaultStoragePartition( + browser_context_.get()) ->GetNetworkContext(); } diff --git a/chromium/fuchsia/engine/browser/context_impl.h b/chromium/fuchsia/engine/browser/context_impl.h index 1c314860f23..6d41ccaa850 100644 --- a/chromium/fuchsia/engine/browser/context_impl.h +++ b/chromium/fuchsia/engine/browser/context_impl.h @@ -34,8 +34,8 @@ class WebEngineDevToolsController; // All created Frames are owned by this object. class WEB_ENGINE_EXPORT ContextImpl : public fuchsia::web::Context { public: - // |browser_context| and |devtools_controller| must outlive ContextImpl. - ContextImpl(content::BrowserContext* browser_context, + // |devtools_controller| must outlive ContextImpl. + ContextImpl(std::unique_ptr<content::BrowserContext> browser_context, WebEngineDevToolsController* devtools_controller); // Tears down the Context, destroying any active Frames in the process. @@ -76,8 +76,8 @@ class WEB_ENGINE_EXPORT ContextImpl : public fuchsia::web::Context { // |frame_ptr| client. FrameImpl* GetFrameImplForTest(fuchsia::web::FramePtr* frame_ptr) const; - content::BrowserContext* browser_context_for_test() const { - return browser_context_; + content::BrowserContext* browser_context() const { + return browser_context_.get(); } private: @@ -85,7 +85,7 @@ class WEB_ENGINE_EXPORT ContextImpl : public fuchsia::web::Context { network::mojom::NetworkContext* GetNetworkContext(); // Reference to the browser implementation for this Context. - content::BrowserContext* const browser_context_; + std::unique_ptr<content::BrowserContext> const browser_context_; // Reference to the class managing the DevTools remote debugging service. WebEngineDevToolsController* const devtools_controller_; diff --git a/chromium/fuchsia/engine/browser/explicit_sites_filter_browsertest.cc b/chromium/fuchsia/engine/browser/explicit_sites_filter_browsertest.cc index 7c57d3a4fb3..2b03e18f17d 100644 --- a/chromium/fuchsia/engine/browser/explicit_sites_filter_browsertest.cc +++ b/chromium/fuchsia/engine/browser/explicit_sites_filter_browsertest.cc @@ -50,7 +50,7 @@ class ExplicitSitesFilterTest : public FrameImplTestBaseWithServer { FrameImplTestBaseWithServer::SetUpOnMainThread(); SafeSearchFactory::GetInstance() - ->GetForBrowserContext(context_impl()->browser_context_for_test()) + ->GetForBrowserContext(context_impl()->browser_context()) ->SetSafeSearchURLCheckerForTest( stub_url_checker_.BuildURLChecker(kUrlCheckerCacheSize)); } diff --git a/chromium/fuchsia/engine/browser/fake_semantic_tree.cc b/chromium/fuchsia/engine/browser/fake_semantic_tree.cc index 1c3c006f07d..87c49e0f586 100644 --- a/chromium/fuchsia/engine/browser/fake_semantic_tree.cc +++ b/chromium/fuchsia/engine/browser/fake_semantic_tree.cc @@ -61,6 +61,21 @@ void FakeSemanticTree::RunUntilNodeCountAtLeast(size_t count) { run_loop.Run(); } +void FakeSemanticTree::RunUntilNodeWithLabelIsInTree(base::StringPiece label) { + DCHECK(!on_commit_updates_); + if (GetNodeFromLabel(label)) + return; + + base::RunLoop run_loop; + base::AutoReset<base::RepeatingClosure> auto_reset( + &on_commit_updates_, + base::BindLambdaForTesting([this, label, &run_loop]() { + if (GetNodeFromLabel(label)) + run_loop.Quit(); + })); + run_loop.Run(); +} + void FakeSemanticTree::RunUntilCommitCountIs(size_t count) { DCHECK(!on_commit_updates_); if (count == num_commit_calls_) diff --git a/chromium/fuchsia/engine/browser/fake_semantic_tree.h b/chromium/fuchsia/engine/browser/fake_semantic_tree.h index da880efd3d4..8c79858b6a8 100644 --- a/chromium/fuchsia/engine/browser/fake_semantic_tree.h +++ b/chromium/fuchsia/engine/browser/fake_semantic_tree.h @@ -36,6 +36,7 @@ class FakeSemanticTree void Disconnect(); void RunUntilNodeCountAtLeast(size_t count); + void RunUntilNodeWithLabelIsInTree(base::StringPiece label); void RunUntilCommitCountIs(size_t count); void SetNodeUpdatedCallback(uint32_t node_id, base::OnceClosure node_updated_callback); diff --git a/chromium/fuchsia/engine/browser/frame_impl.cc b/chromium/fuchsia/engine/browser/frame_impl.cc index 781cdcb53ae..55e40649835 100644 --- a/chromium/fuchsia/engine/browser/frame_impl.cc +++ b/chromium/fuchsia/engine/browser/frame_impl.cc @@ -328,7 +328,7 @@ void FrameImpl::ExecuteJavaScriptInternal(std::vector<std::string> origins, return; } - base::string16 script_utf16; + std::u16string script_utf16; if (!cr_fuchsia::ReadUTF8FromVMOAsUTF16(script, &script_utf16)) { result.set_err(fuchsia::web::FrameError::BUFFER_NOT_UTF8); callback(std::move(result)); @@ -720,11 +720,11 @@ void FrameImpl::PostMessage(std::string origin, return; } - base::Optional<base::string16> origin_utf16; + base::Optional<std::u16string> origin_utf16; if (origin != kWildcardOrigin) origin_utf16 = base::UTF8ToUTF16(origin); - base::string16 data_utf16; + std::u16string data_utf16; if (!cr_fuchsia::ReadUTF8FromVMOAsUTF16(message.data(), &data_utf16)) { result.set_err(fuchsia::web::FrameError::BUFFER_NOT_UTF8); callback(std::move(result)); @@ -757,7 +757,7 @@ void FrameImpl::PostMessage(std::string origin, } content::MessagePortProvider::PostMessageToFrame( - web_contents_.get(), base::string16(), origin_utf16, + web_contents_.get(), std::u16string(), origin_utf16, std::move(data_utf16), std::move(message_ports)); result.set_response(fuchsia::web::Frame_PostMessage_Response()); callback(std::move(result)); @@ -1005,9 +1005,9 @@ void FrameImpl::SetBlockMediaLoading(bool blocked) { bool FrameImpl::DidAddMessageToConsole( content::WebContents* source, blink::mojom::ConsoleMessageLevel log_level, - const base::string16& message, + const std::u16string& message, int32_t line_no, - const base::string16& source_id) { + const std::u16string& source_id) { fx_log_severity_t severity = BlinkConsoleMessageLevelToFxLogSeverity(log_level); if (severity < log_level_) { @@ -1130,6 +1130,7 @@ void FrameImpl::DidFinishLoad(content::RenderFrameHost* render_frame_host, void FrameImpl::RenderFrameCreated(content::RenderFrameHost* frame_host) { // The top-level frame is given a transparent background color. + // GetView() is guaranteed to be non-null until |frame_host| teardown. if (frame_host == web_contents()->GetMainFrame()) frame_host->GetView()->SetBackgroundColor(SK_AlphaTRANSPARENT); } diff --git a/chromium/fuchsia/engine/browser/frame_impl.h b/chromium/fuchsia/engine/browser/frame_impl.h index 9bdcbcc9fed..fea7e90aa11 100644 --- a/chromium/fuchsia/engine/browser/frame_impl.h +++ b/chromium/fuchsia/engine/browser/frame_impl.h @@ -104,6 +104,9 @@ class FrameImpl : public fuchsia::web::Frame, CastStreamingSessionClient* cast_streaming_session_client_for_test() { return cast_streaming_session_client_.get(); } + FrameWindowTreeHost* window_tree_host_for_test() { + return window_tree_host_.get(); + } // Enables explicit sites filtering and set the error page. If |error_page| is // empty, the default error page will be used. @@ -227,9 +230,9 @@ class FrameImpl : public fuchsia::web::Frame, void CloseContents(content::WebContents* source) override; bool DidAddMessageToConsole(content::WebContents* source, blink::mojom::ConsoleMessageLevel log_level, - const base::string16& message, + const std::u16string& message, int32_t line_no, - const base::string16& source_id) override; + const std::u16string& source_id) override; bool IsWebContentsCreationOverridden( content::SiteInstance* source_site_instance, content::mojom::WindowContainerType window_container_type, diff --git a/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc b/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc index b3881d67cac..d9da3e66831 100644 --- a/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc +++ b/chromium/fuchsia/engine/browser/frame_impl_browsertest.cc @@ -11,6 +11,7 @@ #include <string> #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/containers/span.h" #include "base/fuchsia/process_context.h" #include "base/macros.h" diff --git a/chromium/fuchsia/engine/browser/frame_window_tree_host.cc b/chromium/fuchsia/engine/browser/frame_window_tree_host.cc index edc008c430b..41a469f71c1 100644 --- a/chromium/fuchsia/engine/browser/frame_window_tree_host.cc +++ b/chromium/fuchsia/engine/browser/frame_window_tree_host.cc @@ -5,6 +5,7 @@ #include "fuchsia/engine/browser/frame_window_tree_host.h" #include "base/fuchsia/fuchsia_logging.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/client/window_parenting_client.h" @@ -95,3 +96,12 @@ void FrameWindowTreeHost::OnWindowStateChanged( web_contents_->WasShown(); } } + +void FrameWindowTreeHost::OnWindowBoundsChanged(const BoundsChange& bounds) { + aura::WindowTreeHostPlatform::OnBoundsChanged(bounds); + + if (web_contents_->GetMainFrame()->IsRenderFrameLive()) { + web_contents_->GetMainFrame()->GetView()->SetInsets( + bounds.system_ui_overlap); + } +} diff --git a/chromium/fuchsia/engine/browser/frame_window_tree_host.h b/chromium/fuchsia/engine/browser/frame_window_tree_host.h index 9ca6ea24a3b..e4066c23c0b 100644 --- a/chromium/fuchsia/engine/browser/frame_window_tree_host.h +++ b/chromium/fuchsia/engine/browser/frame_window_tree_host.h @@ -34,6 +34,7 @@ class FrameWindowTreeHost : public aura::WindowTreeHostPlatform { // aura::WindowTreeHostPlatform overrides. void OnActivationChanged(bool active) final; void OnWindowStateChanged(ui::PlatformWindowState new_state) final; + void OnWindowBoundsChanged(const BoundsChange& bounds); const fuchsia::ui::views::ViewRef view_ref_; std::unique_ptr<WindowParentingClientImpl> window_parenting_client_; diff --git a/chromium/fuchsia/engine/browser/media_player_impl.cc b/chromium/fuchsia/engine/browser/media_player_impl.cc index eb22d8b5f77..ec90a4c4ff8 100644 --- a/chromium/fuchsia/engine/browser/media_player_impl.cc +++ b/chromium/fuchsia/engine/browser/media_player_impl.cc @@ -47,6 +47,12 @@ fuchsia::media::sessions2::PlayerCapabilityFlags ActionToCapabilityFlag( return {}; // PlayerControl assumes that stop is always supported. case MediaSessionAction::kSwitchAudioDevice: return {}; // PlayerControl does not support switching audio device. + case MediaSessionAction::kToggleMicrophone: + return {}; // PlayerControl does not support toggling microphone. + case MediaSessionAction::kToggleCamera: + return {}; // PlayerControl does not support toggling camera. + case MediaSessionAction::kHangUp: + return {}; // PlayerControl does not support hanging up. } } diff --git a/chromium/fuchsia/engine/browser/media_player_impl_unittest.cc b/chromium/fuchsia/engine/browser/media_player_impl_unittest.cc index 5eea168dd8e..cf81e993e50 100644 --- a/chromium/fuchsia/engine/browser/media_player_impl_unittest.cc +++ b/chromium/fuchsia/engine/browser/media_player_impl_unittest.cc @@ -37,6 +37,9 @@ class FakeMediaSession : public content::MediaSession { MOCK_METHOD0(EnterPictureInPicture, void()); MOCK_METHOD0(ExitPictureInPicture, void()); MOCK_METHOD1(SetAudioSinkId, void(const base::Optional<std::string>& id)); + MOCK_METHOD0(ToggleMicrophone, void()); + MOCK_METHOD0(ToggleCamera, void()); + MOCK_METHOD0(HangUp, void()); // content::MediaSession APIs faked to implement testing behaviour. MOCK_METHOD1(DidReceiveAction, diff --git a/chromium/fuchsia/engine/browser/navigation_controller_impl.cc b/chromium/fuchsia/engine/browser/navigation_controller_impl.cc index e273491da44..7c22ca98719 100644 --- a/chromium/fuchsia/engine/browser/navigation_controller_impl.cc +++ b/chromium/fuchsia/engine/browser/navigation_controller_impl.cc @@ -216,7 +216,8 @@ void NavigationControllerImpl::TitleWasSet(content::NavigationEntry* entry) { OnNavigationEntryChanged(); } -void NavigationControllerImpl::DocumentAvailableInMainFrame() { +void NavigationControllerImpl::DocumentAvailableInMainFrame( + content::RenderFrameHost* render_frame_host) { // The main document is loaded, but not necessarily all the subresources. Some // fields like "title" will change here. @@ -260,11 +261,8 @@ void NavigationControllerImpl::DidStartNavigation( void NavigationControllerImpl::DidFinishNavigation( content::NavigationHandle* navigation_handle) { - if (!navigation_handle->IsInMainFrame() || - navigation_handle->IsSameDocument() || - navigation_handle != active_navigation_) { + if (navigation_handle != active_navigation_) return; - } active_navigation_ = nullptr; uncommitted_load_error_ = !navigation_handle->HasCommitted() && diff --git a/chromium/fuchsia/engine/browser/navigation_controller_impl.h b/chromium/fuchsia/engine/browser/navigation_controller_impl.h index 1b69519065e..88239ad3a05 100644 --- a/chromium/fuchsia/engine/browser/navigation_controller_impl.h +++ b/chromium/fuchsia/engine/browser/navigation_controller_impl.h @@ -58,7 +58,8 @@ class NavigationControllerImpl : public fuchsia::web::NavigationController, // content::WebContentsObserver implementation. void TitleWasSet(content::NavigationEntry*) final; - void DocumentAvailableInMainFrame() final; + void DocumentAvailableInMainFrame( + content::RenderFrameHost* render_frame_host) final; void DidFinishLoad(content::RenderFrameHost* render_frame_host, const GURL& validated_url) final; void RenderProcessGone(base::TerminationStatus status) final; diff --git a/chromium/fuchsia/engine/browser/permissions_browsertest.cc b/chromium/fuchsia/engine/browser/permissions_browsertest.cc new file mode 100644 index 00000000000..8a8d1089566 --- /dev/null +++ b/chromium/fuchsia/engine/browser/permissions_browsertest.cc @@ -0,0 +1,144 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_util.h" +#include "base/strings/stringprintf.h" +#include "base/threading/scoped_blocking_call.h" +#include "content/public/test/browser_test.h" +#include "fuchsia/base/frame_test_util.h" +#include "fuchsia/base/mem_buffer_util.h" +#include "fuchsia/engine/browser/frame_impl_browser_test_base.h" +#include "fuchsia/engine/test/test_data.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const char kIframeTestPagePath[] = "/iframe.html"; +static const char kMicTestPagePath[] = "/mic.html"; +static const char kMicNoPermissionTestPagePath[] = "/mic.html?NoPermission"; + +class PermissionsBrowserTest : public FrameImplTestBaseWithServer { + public: + PermissionsBrowserTest() = default; + ~PermissionsBrowserTest() override = default; + + void SetUpOnMainThread() override { + FrameImplTestBaseWithServer::SetUpOnMainThread(); + frame_ = CreateFrame(); + } + + void GrantPermission(fuchsia::web::PermissionType type, + const std::string& origin) { + fuchsia::web::PermissionDescriptor permission; + permission.set_type(type); + frame_->SetPermissionState(std::move(permission), origin, + fuchsia::web::PermissionState::GRANTED); + } + + protected: + void InjectBeforeLoadJs(const std::string& code); + + // Loads iframe.html with the specified URL used for the embedded page. + void LoadPageInIframe(const std::string& url); + + std::unique_ptr<net::test_server::HttpResponse> + RequestHandlerWithPermissionPolicy( + const net::test_server::HttpRequest& request); + + uint64_t before_load_js_id_ = 1; + fuchsia::web::FramePtr frame_; +}; + +void PermissionsBrowserTest::InjectBeforeLoadJs(const std::string& code) { + frame_->AddBeforeLoadJavaScript( + before_load_js_id_++, {"*"}, + cr_fuchsia::MemBufferFromString(code, "test"), + [](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) { + CHECK(result.is_response()); + }); +} + +void PermissionsBrowserTest::LoadPageInIframe(const std::string& url) { + // Before loading a page on the default embedded test server, set the iframe + // src to be |url|. + InjectBeforeLoadJs(base::StringPrintf("iframeSrc = '%s';", url.c_str())); + + fuchsia::web::NavigationControllerPtr controller; + frame_->GetNavigationController(controller.NewRequest()); + + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), {}, + embedded_test_server()->GetURL(kIframeTestPagePath).spec())); +} + +IN_PROC_BROWSER_TEST_F(PermissionsBrowserTest, PermissionInSameOriginIframe) { + GrantPermission(fuchsia::web::PermissionType::MICROPHONE, + embedded_test_server()->GetURL("/").GetOrigin().spec()); + + // Mic permission is expected to be granted since the iframe is loaded from + // the same origin. + GURL iframe_src = embedded_test_server()->GetURL(kMicTestPagePath); + + ASSERT_NO_FATAL_FAILURE(LoadPageInIframe(iframe_src.spec())); + + navigation_listener_.RunUntilTitleEquals("ended"); +} + +IN_PROC_BROWSER_TEST_F(PermissionsBrowserTest, NoPermissionInSameOriginIframe) { + // Mic permission is expected to be denied since it wasn't granted to the + // parent frame. + GURL iframe_src = + embedded_test_server()->GetURL(kMicNoPermissionTestPagePath); + + ASSERT_NO_FATAL_FAILURE(LoadPageInIframe(iframe_src.spec())); + + navigation_listener_.RunUntilTitleEquals("ended-NotFoundError"); +} + +IN_PROC_BROWSER_TEST_F(PermissionsBrowserTest, PermissionInCrossOriginIframe) { + GrantPermission(fuchsia::web::PermissionType::MICROPHONE, + embedded_test_server()->GetURL("/").GetOrigin().spec()); + + // Start a second embedded test server. It's used to load the page inside + // the <iframe> from an origin different from the origin of the embedding + // page. + net::EmbeddedTestServer second_test_server; + second_test_server.ServeFilesFromSourceDirectory( + base::FilePath(cr_fuchsia::kTestServerRoot)); + ASSERT_TRUE(second_test_server.Start()); + + // Mic permissions are expected to be denied since the page is cross-origin. + GURL iframe_src = second_test_server.GetURL(kMicNoPermissionTestPagePath); + + ASSERT_NO_FATAL_FAILURE(LoadPageInIframe(iframe_src.spec())); + + navigation_listener_.RunUntilTitleEquals("ended-NotAllowedError"); +} + +IN_PROC_BROWSER_TEST_F(PermissionsBrowserTest, + PermissionInCrossOriginIframeWithPermissionPolicy) { + GrantPermission(fuchsia::web::PermissionType::MICROPHONE, + embedded_test_server()->GetURL("/").GetOrigin().spec()); + + // Start a second embedded test server. It's used to load the page inside + // the <iframe> from an origin different from the origin of the embedding + // page. + net::EmbeddedTestServer second_test_server; + second_test_server.ServeFilesFromSourceDirectory( + base::FilePath(cr_fuchsia::kTestServerRoot)); + ASSERT_TRUE(second_test_server.Start()); + + // Mic permissions are expected to be granted because the parent frame has + // access to the microphone and it's delegated to the child by the permission + // policy (see code below). + GURL iframe_src = second_test_server.GetURL(kMicTestPagePath); + + InjectBeforeLoadJs( + base::StringPrintf("iframePermissionPolicy = 'microphone %s';", + iframe_src.GetOrigin().spec().c_str())); + + ASSERT_NO_FATAL_FAILURE(LoadPageInIframe(iframe_src.spec())); + + navigation_listener_.RunUntilTitleEquals("ended"); +} diff --git a/chromium/fuchsia/engine/browser/theme_manager.cc b/chromium/fuchsia/engine/browser/theme_manager.cc index 983a7e17146..cccca3c9a40 100644 --- a/chromium/fuchsia/engine/browser/theme_manager.cc +++ b/chromium/fuchsia/engine/browser/theme_manager.cc @@ -4,6 +4,7 @@ #include "fuchsia/engine/browser/theme_manager.h" +#include "base/callback_helpers.h" #include "base/check.h" #include "base/fuchsia/fuchsia_logging.h" #include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom.h" diff --git a/chromium/fuchsia/engine/browser/virtual_keyboard_browsertest.cc b/chromium/fuchsia/engine/browser/virtual_keyboard_browsertest.cc new file mode 100644 index 00000000000..6c646f955ed --- /dev/null +++ b/chromium/fuchsia/engine/browser/virtual_keyboard_browsertest.cc @@ -0,0 +1,376 @@ +// Copyright 2021 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/input/virtualkeyboard/cpp/fidl.h> +#include <lib/fit/function.h> +#include <lib/ui/scenic/cpp/view_ref_pair.h> +#include <lib/ui/scenic/cpp/view_token_pair.h> + +#include "base/callback.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/scoped_service_binding.h" +#include "base/fuchsia/test_component_context_for_process.h" +#include "base/strings/stringprintf.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "fuchsia/base/frame_test_util.h" +#include "fuchsia/base/test_navigation_listener.h" +#include "fuchsia/engine/browser/frame_impl.h" +#include "fuchsia/engine/browser/frame_window_tree_host.h" +#include "fuchsia/engine/test/test_data.h" +#include "fuchsia/engine/test/web_engine_browser_test.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace virtualkeyboard = fuchsia::input::virtualkeyboard; + +namespace { + +const gfx::Rect kBounds = {1000, 1000}; +const gfx::Point kNoTarget = {999, 999}; + +constexpr char kInputFieldText[] = "input-text"; +constexpr char kInputFieldTel[] = "input-tel"; +constexpr char kInputFieldNumeric[] = "input-numeric"; +constexpr char kInputFieldUrl[] = "input-url"; +constexpr char kInputFieldEmail[] = "input-email"; +constexpr char kInputFieldDecimal[] = "input-decimal"; +constexpr char kInputFieldSearch[] = "input-search"; + +zx_handle_t GetKoidFromEventPair(const zx::eventpair& object) { + zx_info_handle_basic_t handle_info{}; + zx_status_t status = + object.get_info(ZX_INFO_HANDLE_BASIC, &handle_info, + sizeof(zx_info_handle_basic_t), nullptr, nullptr); + ZX_CHECK(status == ZX_OK, status) << "zx_object_get_info"; + return handle_info.koid; +} + +class MockVirtualKeyboardController : public virtualkeyboard::Controller { + public: + MockVirtualKeyboardController() : binding_(this) {} + ~MockVirtualKeyboardController() override {} + + MockVirtualKeyboardController(MockVirtualKeyboardController&) = delete; + MockVirtualKeyboardController operator=(MockVirtualKeyboardController&) = + delete; + + void Bind(fuchsia::ui::views::ViewRef view_ref, + virtualkeyboard::TextType text_type, + fidl::InterfaceRequest<fuchsia::input::virtualkeyboard::Controller> + controller_request) { + text_type_ = text_type; + view_ref_ = std::move(view_ref); + binding_.Bind(std::move(controller_request)); + } + + // Spins a RunLoop until the client calls WatchVisibility(). + void AwaitWatchAndRespondWith(bool is_visible) { + if (!watch_vis_callback_) { + base::RunLoop run_loop; + on_watch_visibility_ = run_loop.QuitClosure(); + run_loop.Run(); + ASSERT_TRUE(watch_vis_callback_); + } + + (*watch_vis_callback_)(is_visible); + watch_vis_callback_ = {}; + } + + const fuchsia::ui::views::ViewRef& view_ref() const { return view_ref_; } + virtualkeyboard::TextType text_type() const { return text_type_; } + + // virtualkeyboard::Controller implementation. + MOCK_METHOD0(RequestShow, void()); + MOCK_METHOD0(RequestHide, void()); + MOCK_METHOD1(SetTextType, void(virtualkeyboard::TextType text_type)); + + private: + // virtualkeyboard::Controller implementation. + void WatchVisibility( + virtualkeyboard::Controller::WatchVisibilityCallback callback) final { + watch_vis_callback_ = std::move(callback); + + if (on_watch_visibility_) + std::move(on_watch_visibility_).Run(); + } + + base::OnceClosure on_watch_visibility_; + base::Optional<virtualkeyboard::Controller::WatchVisibilityCallback> + watch_vis_callback_; + fuchsia::ui::views::ViewRef view_ref_; + virtualkeyboard::TextType text_type_; + fidl::Binding<fuchsia::input::virtualkeyboard::Controller> binding_; +}; + +// Services connection requests for MockVirtualKeyboardControllers. +class MockVirtualKeyboardControllerCreator + : public virtualkeyboard::ControllerCreator { + public: + explicit MockVirtualKeyboardControllerCreator( + base::TestComponentContextForProcess* component_context) + : binding_(component_context->additional_services(), this) {} + + ~MockVirtualKeyboardControllerCreator() override { + CHECK(!pending_controller_); + } + + MockVirtualKeyboardControllerCreator(MockVirtualKeyboardControllerCreator&) = + delete; + MockVirtualKeyboardControllerCreator operator=( + MockVirtualKeyboardControllerCreator&) = delete; + + // Returns an unbound MockVirtualKeyboardController, which will later be + // connected when |this| handles a call to the FIDL method Create(). + std::unique_ptr<MockVirtualKeyboardController> CreateController() { + DCHECK(!pending_controller_); + + auto controller = std::make_unique<MockVirtualKeyboardController>(); + pending_controller_ = controller.get(); + return controller; + } + + private: + // fuchsia::input::virtualkeyboard implementation. + void Create( + fuchsia::ui::views::ViewRef view_ref, + fuchsia::input::virtualkeyboard::TextType text_type, + fidl::InterfaceRequest<fuchsia::input::virtualkeyboard::Controller> + controller_request) final { + CHECK(pending_controller_); + pending_controller_->Bind(std::move(view_ref), text_type, + std::move(controller_request)); + pending_controller_ = nullptr; + } + + MockVirtualKeyboardController* pending_controller_ = nullptr; + base::ScopedServiceBinding<virtualkeyboard::ControllerCreator> binding_; +}; + +class VirtualKeyboardTest : public cr_fuchsia::WebEngineBrowserTest { + public: + VirtualKeyboardTest() { + set_test_server_root(base::FilePath(cr_fuchsia::kTestServerRoot)); + } + ~VirtualKeyboardTest() override = default; + + void SetUpOnMainThread() override { + cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread(); + ASSERT_TRUE(embedded_test_server()->Start()); + + component_context_.emplace( + base::TestComponentContextForProcess::InitialState::kCloneAll); + controller_creator_.emplace(&*component_context_); + controller_ = controller_creator_->CreateController(); + + frame_ = CreateFrame(&navigation_listener_); + frame_impl_ = context_impl()->GetFrameImplForTest(&frame_); + + // Navigate to the test page. + fuchsia::web::NavigationControllerPtr controller; + frame_->GetNavigationController(controller.NewRequest()); + const GURL test_url(embedded_test_server()->GetURL("/input_fields.html")); + EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse( + controller.get(), fuchsia::web::LoadUrlParams(), test_url.spec())); + navigation_listener_.RunUntilUrlEquals(test_url); + + // Simulate the creation of a Scenic View, except bypassing the actual + // construction of a Scenic PlatformWindow in favor of using an injected + // StubWindow. + scenic::ViewRefPair view_ref_pair = scenic::ViewRefPair::New(); + view_ref_ = std::move(view_ref_pair.view_ref); + fuchsia::ui::views::ViewRef view_ref_dup; + zx_status_t status = view_ref_.reference.duplicate(ZX_RIGHT_SAME_RIGHTS, + &view_ref_dup.reference); + ZX_CHECK(status == ZX_OK, status) << "zx_object_duplicate"; + + auto view_tokens = scenic::ViewTokenPair::New(); + frame_->CreateViewWithViewRef(std::move(view_tokens.view_token), + std::move(view_ref_pair.control_ref), + std::move(view_ref_dup)); + base::RunLoop().RunUntilIdle(); + frame_impl_->window_tree_host_for_test()->Show(); + + // Prepare the view for headless interaction by setting its focus state and + // size. + web_contents_ = frame_impl_->web_contents(); + content::RenderWidgetHostView* view = + web_contents_->GetMainFrame()->GetView(); + view->SetBounds(kBounds); + view->Focus(); + + controller_->AwaitWatchAndRespondWith(false); + + ASSERT_EQ(GetKoidFromEventPair(controller_->view_ref().reference), + GetKoidFromEventPair(view_ref_.reference)); + } + + void TearDownOnMainThread() override { + frame_.Unbind(); + base::RunLoop().RunUntilIdle(); + } + + gfx::Point GetCoordinatesOfInputField(base::StringPiece id) { + // Distance to click from the top/left extents of an input field. + constexpr int kInputFieldClickInset = 8; + + base::Optional<base::Value> result = cr_fuchsia::ExecuteJavaScript( + frame_.get(), + base::StringPrintf("getPointInsideText('%s')", id.data())); + CHECK(result); + + // Note that coordinates are floating point and must be retrieved as such + // from the Value, but we can cast them to integers and disregard the + // fractional value with no major consequences. + return gfx::Point(*result->FindDoublePath("x") + kInputFieldClickInset, + *result->FindDoublePath("y") + kInputFieldClickInset); + } + + protected: + // Used to publish fake virtual keyboard services for the InputMethod to use. + base::Optional<base::TestComponentContextForProcess> component_context_; + base::Optional<MockVirtualKeyboardControllerCreator> controller_creator_; + std::unique_ptr<MockVirtualKeyboardController> controller_; + + fuchsia::web::FramePtr frame_; + FrameImpl* frame_impl_ = nullptr; + content::WebContents* web_contents_ = nullptr; + cr_fuchsia::TestNavigationListener navigation_listener_; + fuchsia::ui::views::ViewRef view_ref_; +}; + +// Verifies that RequestShow() is invoked multiple times if the virtual +// keyboard service does not indicate that the keyboard is made visible. +IN_PROC_BROWSER_TEST_F(VirtualKeyboardTest, ShowAndHideCalledButIgnored) { + testing::InSequence s; + EXPECT_CALL(*controller_, RequestShow()).Times(2); + EXPECT_CALL(*controller_, RequestHide()); + base::RunLoop run_loop; + EXPECT_CALL(*controller_, RequestShow()) + .WillOnce(testing::InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); })); + + // Tap inside the text field. The IME should be summoned via a call to + // RequestShow(). + content::SimulateTapAt(web_contents_, + GetCoordinatesOfInputField(kInputFieldText)); + + // Change fields. RequestShow() should be called again, since the keyboard + // is not yet known to be visible. + content::SimulateTapAt(web_contents_, + GetCoordinatesOfInputField(kInputFieldNumeric)); + + // Tap outside the text field. The IME should be dismissed, which will result + // in a call to RequestHide(). + content::SimulateTapAt(web_contents_, kNoTarget); + + // Tap back on a text field. RequestShow should be called. + content::SimulateTapAt(web_contents_, + GetCoordinatesOfInputField(kInputFieldText)); + run_loop.Run(); +} + +// Verifies that RequestShow() is not called redundantly if the virtual +// keyboard is reported as visible. +IN_PROC_BROWSER_TEST_F(VirtualKeyboardTest, ShowAndHideWithVisibility) { + testing::InSequence s; + base::RunLoop on_show_run_loop; + EXPECT_CALL(*controller_, + SetTextType(virtualkeyboard::TextType::ALPHANUMERIC)); + EXPECT_CALL(*controller_, RequestShow()) + .WillOnce(testing::InvokeWithoutArgs( + [&on_show_run_loop]() { on_show_run_loop.Quit(); })); + EXPECT_CALL(*controller_, SetTextType(virtualkeyboard::TextType::NUMERIC)); + base::RunLoop on_hide_run_loop; + EXPECT_CALL(*controller_, RequestHide()) + .WillOnce(testing::InvokeWithoutArgs( + [&on_hide_run_loop]() { on_hide_run_loop.Quit(); })); + + // Give focus to an input field, which will result in RequestShow() being + // called. + content::SimulateTapAt(web_contents_, + GetCoordinatesOfInputField(kInputFieldText)); + on_show_run_loop.Run(); + + // Indicate that the virtual keyboard is now visible. + controller_->AwaitWatchAndRespondWith(true); + + // Tap on another text field. RequestShow should not be called a second time + // since the keyboard is already onscreen. + content::SimulateTapAt(web_contents_, + GetCoordinatesOfInputField(kInputFieldNumeric)); + base::RunLoop().RunUntilIdle(); + + content::SimulateTapAt(web_contents_, kNoTarget); + on_hide_run_loop.Run(); +} + +// Gives focus to a sequence of HTML <input> nodes with different InputModes, +// and verifies that the InputMode's FIDL equivalent is sent via SetTextType(). +IN_PROC_BROWSER_TEST_F(VirtualKeyboardTest, InputModeMappings) { + const std::vector<std::pair<base::StringPiece, virtualkeyboard::TextType>> + kInputTypeMappings = { + {kInputFieldText, virtualkeyboard::TextType::ALPHANUMERIC}, + {kInputFieldTel, virtualkeyboard::TextType::PHONE}, + {kInputFieldNumeric, virtualkeyboard::TextType::NUMERIC}, + {kInputFieldUrl, virtualkeyboard::TextType::ALPHANUMERIC}, + {kInputFieldEmail, virtualkeyboard::TextType::ALPHANUMERIC}, + {kInputFieldDecimal, virtualkeyboard::TextType::NUMERIC}, + {kInputFieldSearch, virtualkeyboard::TextType::ALPHANUMERIC}, + }; + + // Simulate like the keyboard is already shown, so that the test can focus on + // input type mappings without clutter from visibility management. + controller_->AwaitWatchAndRespondWith(true); + base::RunLoop().RunUntilIdle(); + + // GMock expectations must be set upfront, hence the redundant for-each loop. + for (const auto& field_type_pair : kInputTypeMappings) { + EXPECT_CALL(*controller_, SetTextType(field_type_pair.second)) + .RetiresOnSaturation(); + } + + base::RunLoop run_loop; + EXPECT_CALL(*controller_, RequestHide()) + .WillOnce(testing::InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); })); + + for (const auto& field_type_pair : kInputTypeMappings) { + content::SimulateTapAt(web_contents_, + GetCoordinatesOfInputField(field_type_pair.first)); + } + + // Dismiss the virtual keyboard and wait for RequestHide(). + content::SimulateTapAt(web_contents_, kNoTarget); + run_loop.Run(); +} + +IN_PROC_BROWSER_TEST_F(VirtualKeyboardTest, Disconnection) { + testing::InSequence s; + base::RunLoop on_show_run_loop; + EXPECT_CALL(*controller_, + SetTextType(virtualkeyboard::TextType::ALPHANUMERIC)); + EXPECT_CALL(*controller_, RequestShow()) + .WillOnce(testing::InvokeWithoutArgs( + [&on_show_run_loop]() { on_show_run_loop.Quit(); })); + + // Tapping inside the text field should show the IME and signal RequestShow. + content::SimulateTapAt(web_contents_, + GetCoordinatesOfInputField(kInputFieldText)); + on_show_run_loop.Run(); + + controller_->AwaitWatchAndRespondWith(true); + base::RunLoop().RunUntilIdle(); + + // Disconnect the FIDL service. + controller_.reset(); + base::RunLoop().RunUntilIdle(); + + // Focus on another text field, then defocus. Nothing should crash. + content::SimulateTapAt(web_contents_, + GetCoordinatesOfInputField(kInputFieldNumeric)); + content::SimulateTapAt(web_contents_, kNoTarget); +} + +} // namespace diff --git a/chromium/fuchsia/engine/browser/web_engine_browser_context.cc b/chromium/fuchsia/engine/browser/web_engine_browser_context.cc index 448bc10167c..bfaeccf85da 100644 --- a/chromium/fuchsia/engine/browser/web_engine_browser_context.cc +++ b/chromium/fuchsia/engine/browser/web_engine_browser_context.cc @@ -11,13 +11,11 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/file_util.h" -#include "base/i18n/rtl.h" #include "base/path_service.h" #include "base/system/sys_info.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/core/simple_key_map.h" #include "components/site_isolation/site_isolation_policy.h" -#include "components/strings/grit/components_locale_settings.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_context.h" @@ -27,7 +25,6 @@ #include "media/capabilities/in_memory_video_decode_stats_db_impl.h" #include "media/mojo/services/video_decode_perf_history.h" #include "services/network/public/cpp/network_switches.h" -#include "ui/base/l10n/l10n_util.h" namespace { @@ -189,36 +186,17 @@ WebEngineBrowserContext::GetBrowsingDataRemoverDelegate() { return nullptr; } -media::VideoDecodePerfHistory* -WebEngineBrowserContext::GetVideoDecodePerfHistory() { - if (IsOffTheRecord()) - return GetInMemoryVideoDecodePerfHistory(); - - // Delegate to the base class for stateful VideoDecodePerfHistory DB - // creation. - return BrowserContext::GetVideoDecodePerfHistory(); -} - -std::string WebEngineBrowserContext::GetPreferredLanguages() const { - return l10n_util::GetStringUTF8(IDS_ACCEPT_LANGUAGES); -} - -media::VideoDecodePerfHistory* -WebEngineBrowserContext::GetInMemoryVideoDecodePerfHistory() { - constexpr char kUserDataKeyName[] = "video-decode-perf-history"; - auto* decode_history = static_cast<media::VideoDecodePerfHistory*>( - GetUserData(kUserDataKeyName)); - - // Get, and potentially lazily create, the in-memory VideoDecodePerfHistory - // DB. - if (!decode_history) { - auto owned_decode_history = std::make_unique<media::VideoDecodePerfHistory>( - std::make_unique<media::InMemoryVideoDecodeStatsDBImpl>( - nullptr /* seed_db_provider */), - media::learning::FeatureProviderFactoryCB()); - decode_history = owned_decode_history.get(); - SetUserData(kUserDataKeyName, std::move(owned_decode_history)); +std::unique_ptr<media::VideoDecodePerfHistory> +WebEngineBrowserContext::CreateVideoDecodePerfHistory() { + if (!IsOffTheRecord()) { + // Delegate to the base class for stateful VideoDecodePerfHistory DB + // creation. + return BrowserContext::CreateVideoDecodePerfHistory(); } - return decode_history; + // Return in-memory VideoDecodePerfHistory. + return std::make_unique<media::VideoDecodePerfHistory>( + std::make_unique<media::InMemoryVideoDecodeStatsDBImpl>( + nullptr /* seed_db_provider */), + media::learning::FeatureProviderFactoryCB()); } diff --git a/chromium/fuchsia/engine/browser/web_engine_browser_context.h b/chromium/fuchsia/engine/browser/web_engine_browser_context.h index d20108d7ce3..76965a9d108 100644 --- a/chromium/fuchsia/engine/browser/web_engine_browser_context.h +++ b/chromium/fuchsia/engine/browser/web_engine_browser_context.h @@ -42,20 +42,13 @@ class WebEngineBrowserContext : public content::BrowserContext { content::BackgroundSyncController* GetBackgroundSyncController() override; content::BrowsingDataRemoverDelegate* GetBrowsingDataRemoverDelegate() override; - media::VideoDecodePerfHistory* GetVideoDecodePerfHistory() override; - - // Returns a comma-separated list of language codes, in preference order. - // This is suitable for direct use setting the "sec-ch-lang" header, or - // passed to net::HttpUtil::GenerateAcceptLanguageHeader() to generate a - // legacy "accept-language" header value. - std::string GetPreferredLanguages() const; + std::unique_ptr<media::VideoDecodePerfHistory> CreateVideoDecodePerfHistory() + override; private: // Contains URLRequestContextGetter required for resource loading. class ResourceContext; - media::VideoDecodePerfHistory* GetInMemoryVideoDecodePerfHistory(); - base::FilePath data_dir_path_; std::unique_ptr<WebEngineNetLogObserver> net_log_observer_; 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 7e526f4ec79..167d2fc9f9c 100644 --- a/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.cc +++ b/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.cc @@ -15,11 +15,13 @@ #include "base/i18n/rtl.h" #include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" +#include "content/public/browser/content_browser_client.h" #include "content/public/browser/gpu_data_manager.h" #include "content/public/browser/histogram_fetcher.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/main_function_params.h" +#include "content/public/common/result_codes.h" #include "fuchsia/base/legacymetrics_client.h" #include "fuchsia/engine/browser/context_impl.h" #include "fuchsia/engine/browser/media_resource_provider_service.h" @@ -56,9 +58,12 @@ void FetchHistogramsFromChildProcesses( } // namespace WebEngineBrowserMainParts::WebEngineBrowserMainParts( + content::ContentBrowserClient* browser_client, const content::MainFunctionParams& parameters, fidl::InterfaceRequest<fuchsia::web::Context> request) - : parameters_(parameters), request_(std::move(request)) { + : browser_client_(browser_client), + parameters_(parameters), + request_(std::move(request)) { DCHECK(request_); } @@ -66,11 +71,20 @@ WebEngineBrowserMainParts::~WebEngineBrowserMainParts() { display::Screen::SetScreenInstance(nullptr); } +std::vector<content::BrowserContext*> +WebEngineBrowserMainParts::browser_contexts() const { + std::vector<content::BrowserContext*> contexts; + contexts.reserve(context_bindings_.size()); + for (auto& binding : context_bindings_.bindings()) + contexts.push_back(binding->impl()->browser_context()); + return contexts; +} + void WebEngineBrowserMainParts::PostEarlyInitialization() { base::ImportantFileWriterCleaner::GetInstance().Initialize(); } -void WebEngineBrowserMainParts::PreMainMessageLoopRun() { +int WebEngineBrowserMainParts::PreMainMessageLoopRun() { DCHECK(!screen_); // Watch for changes to the user's locale setting. @@ -92,16 +106,27 @@ void WebEngineBrowserMainParts::PreMainMessageLoopRun() { gpu_data_manager->DisableHardwareAcceleration(); } - DCHECK(!browser_context_); - browser_context_ = std::make_unique<WebEngineBrowserContext>( + DCHECK_EQ(context_bindings_.size(), 0u); + auto browser_context = std::make_unique<WebEngineBrowserContext>( base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kIncognito)); devtools_controller_ = WebEngineDevToolsController::CreateFromCommandLine( *base::CommandLine::ForCurrentProcess()); - context_service_ = std::make_unique<ContextImpl>(browser_context_.get(), - devtools_controller_.get()); - context_binding_ = std::make_unique<fidl::Binding<fuchsia::web::Context>>( - context_service_.get(), std::move(request_)); + + // TODO(crbug.com/1163073): Instead of creating a single ContextImpl instance + // the code below should public fuchsia.web.Context to the outgoing directory. + // Also it shouldn't quit main loop after Context disconnects. + context_bindings_.AddBinding( + std::make_unique<ContextImpl>(std::move(browser_context), + devtools_controller_.get()), + std::move(request_), /* dispatcher */ nullptr, + // Quit the browser main loop when the Context connection is dropped. + [this](zx_status_t status) { + ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status) + << " Context disconnected."; + // context_service_.reset(); + std::move(quit_closure_).Run(); + }); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseLegacyMetricsService)) { @@ -122,22 +147,13 @@ void WebEngineBrowserMainParts::PreMainMessageLoopRun() { media_resource_provider_service_ = std::make_unique<MediaResourceProviderService>(); - // Quit the browser main loop when the Context connection is dropped. - context_binding_->set_error_handler([this](zx_status_t status) { - ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status) - << " Context disconnected."; - context_service_.reset(); - std::move(quit_closure_).Run(); - }); - // Disable RenderFrameHost's Javascript injection restrictions so that the // Context and Frames can implement their own JS injection policy at a higher // level. content::RenderFrameHost::AllowInjectingJavaScript(); if (parameters_.ui_task) { - // Since the main loop won't run, there is nothing to quit in the - // |context_binding_| error handler. + // Since the main loop won't run, there is nothing to quit. quit_closure_ = base::DoNothing::Once(); std::move(*parameters_.ui_task).Run(); @@ -147,35 +163,37 @@ void WebEngineBrowserMainParts::PreMainMessageLoopRun() { // Make sure temporary files associated with this process are cleaned up. base::ImportantFileWriterCleaner::GetInstance().Start(); -} -void WebEngineBrowserMainParts::PreDefaultMainMessageLoopRun( - base::OnceClosure quit_closure) { - quit_closure_ = std::move(quit_closure); + return content::RESULT_CODE_NORMAL_EXIT; } -bool WebEngineBrowserMainParts::MainMessageLoopRun(int* result_code) { - return !run_message_loop_; +void WebEngineBrowserMainParts::WillRunMainMessageLoop( + std::unique_ptr<base::RunLoop>& run_loop) { + if (run_message_loop_) + quit_closure_ = run_loop->QuitClosure(); + else + run_loop.reset(); } void WebEngineBrowserMainParts::PostMainMessageLoopRun() { - // The service and its binding should have already been released by the error - // handler. - DCHECK(!context_service_); - DCHECK(!context_binding_->is_bound()); + // Main loop should quit only after all Context instances have been destroyed. + DCHECK_EQ(context_bindings_.size(), 0u); // These resources must be freed while a MessageLoop is still available, so // that they may post cleanup tasks during teardown. - // NOTE: Please destroy objects in the reverse order of their creation. + // NOTE: Objects are destroyed in the reverse order of their creation. legacy_metrics_client_.reset(); - context_binding_.reset(); - browser_context_.reset(); screen_.reset(); intl_profile_watcher_.reset(); base::ImportantFileWriterCleaner::GetInstance().Stop(); } +ContextImpl* WebEngineBrowserMainParts::context_for_test() const { + DCHECK_EQ(context_bindings_.size(), 1u); + return context_bindings_.bindings().front()->impl().get(); +} + void WebEngineBrowserMainParts::OnIntlProfileChanged( const fuchsia::intl::Profile& profile) { // Configure the ICU library in this process with the new primary locale. @@ -189,9 +207,14 @@ void WebEngineBrowserMainParts::OnIntlProfileChanged( base::i18n::GetConfiguredLocale()); VLOG(1) << "Reloaded locale resources: " << loaded_locale; - // Reconfigure the network process. - content::BrowserContext::GetDefaultStoragePartition(browser_context_.get()) - ->GetNetworkContext() - ->SetAcceptLanguage(net::HttpUtil::GenerateAcceptLanguageHeader( - browser_context_->GetPreferredLanguages())); + // Reconfigure each web.Context's NetworkContext with the new setting. + for (auto& binding : context_bindings_.bindings()) { + content::BrowserContext* const browser_context = + binding->impl()->browser_context(); + std::string accept_language = net::HttpUtil::GenerateAcceptLanguageHeader( + browser_client_->GetAcceptLangs(browser_context)); + content::BrowserContext::GetDefaultStoragePartition(browser_context) + ->GetNetworkContext() + ->SetAcceptLanguage(accept_language); + } } diff --git a/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.h b/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.h index be584748940..0e557eadb93 100644 --- a/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.h +++ b/chromium/fuchsia/engine/browser/web_engine_browser_main_parts.h @@ -15,6 +15,7 @@ #include "content/public/browser/browser_main_parts.h" #include "fuchsia/engine/browser/context_impl.h" #include "fuchsia/engine/browser/web_engine_browser_context.h" +#include "fuchsia/engine/web_engine_export.h" namespace base { class FuchsiaIntlProfileWatcher; @@ -25,6 +26,7 @@ class Screen; } namespace content { +class ContentBrowserClient; struct MainFunctionParams; } @@ -34,16 +36,16 @@ class LegacyMetricsClient; class MediaResourceProviderService; -class WebEngineBrowserMainParts : public content::BrowserMainParts { +class WEB_ENGINE_EXPORT WebEngineBrowserMainParts + : public content::BrowserMainParts { public: explicit WebEngineBrowserMainParts( + content::ContentBrowserClient* browser_client, const content::MainFunctionParams& parameters, fidl::InterfaceRequest<fuchsia::web::Context> request); ~WebEngineBrowserMainParts() override; - content::BrowserContext* browser_context() const { - return browser_context_.get(); - } + std::vector<content::BrowserContext*> browser_contexts() const; WebEngineDevToolsController* devtools_controller() const { return devtools_controller_.get(); } @@ -53,24 +55,24 @@ class WebEngineBrowserMainParts : public content::BrowserMainParts { // content::BrowserMainParts overrides. void PostEarlyInitialization() override; - void PreMainMessageLoopRun() override; - void PreDefaultMainMessageLoopRun(base::OnceClosure quit_closure) override; - bool MainMessageLoopRun(int* result_code) override; + int PreMainMessageLoopRun() override; + void WillRunMainMessageLoop( + std::unique_ptr<base::RunLoop>& run_loop) override; void PostMainMessageLoopRun() override; - ContextImpl* context_for_test() const { return context_service_.get(); } + ContextImpl* context_for_test() const; private: void OnIntlProfileChanged(const fuchsia::intl::Profile& profile); + content::ContentBrowserClient* const browser_client_; const content::MainFunctionParams& parameters_; fidl::InterfaceRequest<fuchsia::web::Context> request_; std::unique_ptr<display::Screen> screen_; - std::unique_ptr<WebEngineBrowserContext> browser_context_; - std::unique_ptr<ContextImpl> context_service_; - std::unique_ptr<fidl::Binding<fuchsia::web::Context>> context_binding_; + fidl::BindingSet<fuchsia::web::Context, std::unique_ptr<ContextImpl>> + context_bindings_; std::unique_ptr<WebEngineDevToolsController> devtools_controller_; std::unique_ptr<cr_fuchsia::LegacyMetricsClient> legacy_metrics_client_; std::unique_ptr<MediaResourceProviderService> 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 86ec922a9f5..1be5181e60d 100644 --- a/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc +++ b/chromium/fuchsia/engine/browser/web_engine_content_browser_client.cc @@ -14,6 +14,7 @@ #include "components/policy/content/safe_sites_navigation_throttle.h" #include "components/site_isolation/features.h" #include "components/site_isolation/preloaded_isolated_origins.h" +#include "components/strings/grit/components_locale_settings.h" #include "components/version_info/version_info.h" #include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/devtools_manager_delegate.h" @@ -37,32 +38,35 @@ #include "services/network/public/cpp/network_switches.h" #include "services/network/public/mojom/network_service.mojom.h" #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h" +#include "ui/base/l10n/l10n_util.h" namespace { class DevToolsManagerDelegate : public content::DevToolsManagerDelegate { public: - DevToolsManagerDelegate(content::BrowserContext* browser_context, - WebEngineDevToolsController* controller) - : browser_context_(browser_context), controller_(controller) { - DCHECK(browser_context_); - DCHECK(controller_); + explicit DevToolsManagerDelegate(WebEngineBrowserMainParts* main_parts) + : main_parts_(main_parts) { + DCHECK(main_parts_); } ~DevToolsManagerDelegate() final = default; + DevToolsManagerDelegate(const DevToolsManagerDelegate&) = delete; + DevToolsManagerDelegate& operator=(const DevToolsManagerDelegate&) = delete; + // content::DevToolsManagerDelegate implementation. + std::vector<content::BrowserContext*> GetBrowserContexts() final { + return main_parts_->browser_contexts(); + } content::BrowserContext* GetDefaultBrowserContext() final { - return browser_context_; + std::vector<content::BrowserContext*> contexts = GetBrowserContexts(); + return contexts.empty() ? nullptr : contexts.front(); } content::DevToolsAgentHost::List RemoteDebuggingTargets() final { - return controller_->RemoteDebuggingTargets(); + return main_parts_->devtools_controller()->RemoteDebuggingTargets(); } private: - content::BrowserContext* const browser_context_; - WebEngineDevToolsController* const controller_; - - DISALLOW_COPY_AND_ASSIGN(DevToolsManagerDelegate); + WebEngineBrowserMainParts* const main_parts_; }; std::vector<std::string> GetCorsExemptHeaders() { @@ -88,18 +92,17 @@ WebEngineContentBrowserClient::CreateBrowserMainParts( const content::MainFunctionParams& parameters) { DCHECK(request_); auto browser_main_parts = std::make_unique<WebEngineBrowserMainParts>( - parameters, std::move(request_)); + this, parameters, std::move(request_)); main_parts_ = browser_main_parts.get(); return browser_main_parts; } -content::DevToolsManagerDelegate* -WebEngineContentBrowserClient::GetDevToolsManagerDelegate() { +std::unique_ptr<content::DevToolsManagerDelegate> +WebEngineContentBrowserClient::CreateDevToolsManagerDelegate() { DCHECK(main_parts_); - return new DevToolsManagerDelegate(main_parts_->browser_context(), - main_parts_->devtools_controller()); + return std::make_unique<DevToolsManagerDelegate>(main_parts_); } std::string WebEngineContentBrowserClient::GetProduct() { @@ -197,9 +200,11 @@ std::string WebEngineContentBrowserClient::GetApplicationLocale() { std::string WebEngineContentBrowserClient::GetAcceptLangs( content::BrowserContext* context) { - DCHECK_EQ(main_parts_->browser_context(), context); - return static_cast<WebEngineBrowserContext*>(context) - ->GetPreferredLanguages(); + // Returns a comma-separated list of language codes, in preference order. + // This is suitable for direct use setting the "sec-ch-lang" header, or + // passed to net::HttpUtil::GenerateAcceptLanguageHeader() to generate a + // legacy "accept-language" header value. + return l10n_util::GetStringUTF8(IDS_ACCEPT_LANGUAGES); } base::OnceClosure WebEngineContentBrowserClient::SelectClientCertificate( @@ -270,9 +275,8 @@ void WebEngineContentBrowserClient::ConfigureNetworkContextParams( cert_verifier::mojom::CertVerifierCreationParams* cert_verifier_creation_params) { network_context_params->user_agent = GetUserAgent(); - network_context_params - ->accept_language = net::HttpUtil::GenerateAcceptLanguageHeader( - static_cast<WebEngineBrowserContext*>(context)->GetPreferredLanguages()); + network_context_params->accept_language = + net::HttpUtil::GenerateAcceptLanguageHeader(GetAcceptLangs(context)); // Set the list of cors_exempt_headers which may be specified in a URLRequest, // starting with the headers passed in via @@ -292,4 +296,4 @@ WebEngineContentBrowserClient::GetOriginsRequiringDedicatedProcess() { std::back_inserter(isolated_origin_list)); return isolated_origin_list; -}
\ No newline at end of file +} 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 cab50c3a4e1..eef19310697 100644 --- a/chromium/fuchsia/engine/browser/web_engine_content_browser_client.h +++ b/chromium/fuchsia/engine/browser/web_engine_content_browser_client.h @@ -12,7 +12,6 @@ #include <string> #include <vector> -#include "base/macros.h" #include "content/public/browser/content_browser_client.h" #include "fuchsia/engine/browser/content_directory_loader_factory.h" #include "mojo/public/cpp/bindings/binder_map.h" @@ -26,12 +25,15 @@ class WebEngineContentBrowserClient : public content::ContentBrowserClient { fidl::InterfaceRequest<fuchsia::web::Context> request); ~WebEngineContentBrowserClient() final; - WebEngineBrowserMainParts* main_parts_for_test() const { return main_parts_; } + WebEngineContentBrowserClient(const WebEngineContentBrowserClient&) = delete; + WebEngineContentBrowserClient& operator=( + const WebEngineContentBrowserClient&) = delete; // ContentBrowserClient overrides. std::unique_ptr<content::BrowserMainParts> CreateBrowserMainParts( const content::MainFunctionParams& parameters) final; - content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() final; + std::unique_ptr<content::DevToolsManagerDelegate> + CreateDevToolsManagerDelegate() final; std::string GetProduct() final; std::string GetUserAgent() final; void OverrideWebkitPrefs(content::WebContents* web_contents, @@ -76,6 +78,8 @@ class WebEngineContentBrowserClient : public content::ContentBrowserClient { cert_verifier_creation_params) final; std::vector<url::Origin> GetOriginsRequiringDedicatedProcess() final; + WebEngineBrowserMainParts* main_parts_for_test() const { return main_parts_; } + private: fidl::InterfaceRequest<fuchsia::web::Context> request_; @@ -84,8 +88,6 @@ class WebEngineContentBrowserClient : public content::ContentBrowserClient { // Owned by content::BrowserMainLoop. WebEngineBrowserMainParts* main_parts_; - - DISALLOW_COPY_AND_ASSIGN(WebEngineContentBrowserClient); }; #endif // FUCHSIA_ENGINE_BROWSER_WEB_ENGINE_CONTENT_BROWSER_CLIENT_H_ diff --git a/chromium/fuchsia/engine/browser/web_engine_devtools_controller.cc b/chromium/fuchsia/engine/browser/web_engine_devtools_controller.cc index 5deb754b232..ef8e9a87512 100644 --- a/chromium/fuchsia/engine/browser/web_engine_devtools_controller.cc +++ b/chromium/fuchsia/engine/browser/web_engine_devtools_controller.cc @@ -200,11 +200,10 @@ class UserModeController : public WebEngineDevToolsController { class DebugModeController : public WebEngineDevToolsController { public: explicit DebugModeController( - std::vector<fuchsia::web::DevToolsPerContextListenerPtr> listeners) { - for (auto& listener : listeners) { - devtools_listeners_.AddInterfacePtr(std::move(listener)); - } - } + std::vector<fuchsia::web::DevToolsPerContextListenerPtr> listeners) + : DebugModeController( + std::move(listeners), + net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0)) {} ~DebugModeController() override = default; DebugModeController(const DebugModeController&) = delete; @@ -215,7 +214,7 @@ class DebugModeController : public WebEngineDevToolsController { StartRemoteDebuggingServer( base::BindOnce(&DebugModeController::OnDevToolsPortChanged, base::Unretained(this)), - net::IPEndPoint(net::IPAddress::IPv4Localhost(), 0)); + ip_endpoint_); } void OnContextDestroyed() override { content::DevToolsAgentHost::StopRemoteDebuggingServer(); @@ -236,12 +235,25 @@ class DebugModeController : public WebEngineDevToolsController { std::move(callback).Run(0); } - private: - void OnDevToolsPortChanged(uint16_t port) { + protected: + DebugModeController( + std::vector<fuchsia::web::DevToolsPerContextListenerPtr> listeners, + net::IPEndPoint ip_endpoint) + : ip_endpoint_(std::move(ip_endpoint)) { + for (auto& listener : listeners) { + devtools_listeners_.AddInterfacePtr(std::move(listener)); + } + } + + virtual void OnDevToolsPortChanged(uint16_t port) { devtools_port_ = port; MaybeSendRemoteDebuggingCallbacks(); } + // Currently active DevTools port. Set to 0 on service startup error. + base::Optional<uint16_t> devtools_port_; + + private: void MaybeSendRemoteDebuggingCallbacks() { if (!frame_loaded_ || !devtools_port_) return; @@ -257,8 +269,7 @@ class DebugModeController : public WebEngineDevToolsController { } } - // Currently active DevTools port. Set to 0 on service startup error. - base::Optional<uint16_t> devtools_port_; + const net::IPEndPoint ip_endpoint_; bool frame_loaded_ = false; @@ -269,53 +280,30 @@ class DebugModeController : public WebEngineDevToolsController { // "Mixed-mode" is used when both user and debug remote debugging are active at // the same time. The service lifespan is tied to the Context and all Frames are // available for remote debugging. -class MixedModeController : public WebEngineDevToolsController { +class MixedModeController : public DebugModeController { public: - explicit MixedModeController( + MixedModeController( std::vector<fuchsia::web::DevToolsPerContextListenerPtr> listeners, uint16_t server_port) - : ip_endpoint_(net::IPAddress::IPv6AllZeros(), server_port) { - for (auto& listener : listeners) { - devtools_listeners_.AddInterfacePtr(std::move(listener)); - } - } + : DebugModeController( + std::move(listeners), + net::IPEndPoint(net::IPAddress::IPv6AllZeros(), server_port)) {} ~MixedModeController() override = default; - MixedModeController(const MixedModeController&) = delete; - MixedModeController& operator=(const MixedModeController&) = delete; - - // WebEngineDevToolsController implementation: - void OnContextCreated() override { - StartRemoteDebuggingServer( - base::BindOnce(&MixedModeController::OnDevToolsPortChanged, - base::Unretained(this)), - ip_endpoint_); - } - void OnContextDestroyed() override { - content::DevToolsAgentHost::StopRemoteDebuggingServer(); - } + // WebEngineDevToolsController overrides: bool OnFrameCreated(content::WebContents* contents, bool user_debugging) override { return true; } - void OnFrameLoaded(content::WebContents* contents) override { - frame_loaded_ = true; - MaybeSendRemoteDebuggingCallbacks(); - } - void OnFrameDestroyed(content::WebContents* contents) override {} - content::DevToolsAgentHost::List RemoteDebuggingTargets() override { - return content::DevToolsAgentHost::GetOrCreateAll(); - } void GetDevToolsPort(base::OnceCallback<void(uint16_t)> callback) override { get_port_callbacks_.emplace_back(std::move(callback)); MaybeNotifyGetPortCallbacks(); } - private: - void OnDevToolsPortChanged(uint16_t port) { - devtools_port_ = port; + // DebugModeController overrides: + void OnDevToolsPortChanged(uint16_t port) override { + DebugModeController::OnDevToolsPortChanged(port); MaybeNotifyGetPortCallbacks(); - MaybeSendRemoteDebuggingCallbacks(); } void MaybeNotifyGetPortCallbacks() { @@ -326,32 +314,7 @@ class MixedModeController : public WebEngineDevToolsController { get_port_callbacks_.clear(); } - void MaybeSendRemoteDebuggingCallbacks() { - if (!frame_loaded_ || !devtools_port_) - return; - - // If |devtools_port_| is valid then notify all listeners, otherwise - // disconnect them. - if (devtools_port_.value() == 0) { - devtools_listeners_.CloseAll(); - } else { - for (const auto& listener : devtools_listeners_.ptrs()) { - listener->get()->OnHttpPortOpen(devtools_port_.value()); - } - } - } - - const net::IPEndPoint ip_endpoint_; - - // Currently active DevTools port. Set to 0 on service startup error. - base::Optional<uint16_t> devtools_port_; - std::vector<base::OnceCallback<void(uint16_t)>> get_port_callbacks_; - - bool frame_loaded_ = false; - - fidl::InterfacePtrSet<fuchsia::web::DevToolsPerContextListener> - devtools_listeners_; }; } // namespace @@ -364,7 +327,7 @@ WebEngineDevToolsController::CreateFromCommandLine( if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) { // Set up DevTools to listen on all network routes on the command-line // provided port. - base::StringPiece command_line_port_value = + std::string command_line_port_value = command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort); int parsed_port = 0; diff --git a/chromium/fuchsia/engine/browser/web_engine_permission_delegate.cc b/chromium/fuchsia/engine/browser/web_engine_permission_delegate.cc index c18b8be7cdf..2ab7dd52502 100644 --- a/chromium/fuchsia/engine/browser/web_engine_permission_delegate.cc +++ b/chromium/fuchsia/engine/browser/web_engine_permission_delegate.cc @@ -4,6 +4,8 @@ #include "fuchsia/engine/browser/web_engine_permission_delegate.h" +#include <utility> + #include "base/callback.h" #include "base/check_op.h" #include "base/notreached.h" @@ -15,7 +17,7 @@ WebEnginePermissionDelegate::WebEnginePermissionDelegate() = default; WebEnginePermissionDelegate::~WebEnginePermissionDelegate() = default; -int WebEnginePermissionDelegate::RequestPermission( +void WebEnginePermissionDelegate::RequestPermission( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& origin, @@ -30,12 +32,10 @@ int WebEnginePermissionDelegate::RequestPermission( DCHECK_EQ(state.size(), 1U); std::move(callback).Run(state[0]); }, - base::Passed(std::move(callback)))); - - return content::PermissionController::kNoPendingOperation; + std::move(callback))); } -int WebEnginePermissionDelegate::RequestPermissions( +void WebEnginePermissionDelegate::RequestPermissions( const std::vector<content::PermissionType>& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, @@ -47,8 +47,6 @@ int WebEnginePermissionDelegate::RequestPermissions( frame->permission_controller()->RequestPermissions( permissions, url::Origin::Create(requesting_origin), user_gesture, std::move(callback)); - - return content::PermissionController::kNoPendingOperation; } void WebEnginePermissionDelegate::ResetPermission( diff --git a/chromium/fuchsia/engine/browser/web_engine_permission_delegate.h b/chromium/fuchsia/engine/browser/web_engine_permission_delegate.h index c39989b471c..adafc1d5704 100644 --- a/chromium/fuchsia/engine/browser/web_engine_permission_delegate.h +++ b/chromium/fuchsia/engine/browser/web_engine_permission_delegate.h @@ -20,13 +20,14 @@ class WebEnginePermissionDelegate WebEnginePermissionDelegate& operator=(WebEnginePermissionDelegate&) = delete; // content::PermissionControllerDelegate implementation: - int RequestPermission(content::PermissionType permission, - content::RenderFrameHost* render_frame_host, - const GURL& requesting_origin, - bool user_gesture, - base::OnceCallback<void(blink::mojom::PermissionStatus)> - callback) override; - int RequestPermissions( + void RequestPermission( + content::PermissionType permission, + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + base::OnceCallback<void(blink::mojom::PermissionStatus)> callback) + override; + void RequestPermissions( const std::vector<content::PermissionType>& permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, diff --git a/chromium/fuchsia/engine/common/web_engine_content_client.cc b/chromium/fuchsia/engine/common/web_engine_content_client.cc index 30d007dfe4c..1f9a18a3d33 100644 --- a/chromium/fuchsia/engine/common/web_engine_content_client.cc +++ b/chromium/fuchsia/engine/common/web_engine_content_client.cc @@ -14,7 +14,7 @@ WebEngineContentClient::WebEngineContentClient() = default; WebEngineContentClient::~WebEngineContentClient() = default; -base::string16 WebEngineContentClient::GetLocalizedString(int message_id) { +std::u16string WebEngineContentClient::GetLocalizedString(int message_id) { return l10n_util::GetStringUTF16(message_id); } diff --git a/chromium/fuchsia/engine/common/web_engine_content_client.h b/chromium/fuchsia/engine/common/web_engine_content_client.h index 95a9e1d6500..2383b07944d 100644 --- a/chromium/fuchsia/engine/common/web_engine_content_client.h +++ b/chromium/fuchsia/engine/common/web_engine_content_client.h @@ -14,7 +14,7 @@ class WebEngineContentClient : public content::ContentClient { ~WebEngineContentClient() override; // content::ContentClient implementation. - base::string16 GetLocalizedString(int message_id) override; + std::u16string GetLocalizedString(int message_id) override; base::StringPiece GetDataResource(int resource_id, ui::ScaleFactor scale_factor) override; base::RefCountedMemory* GetDataResourceBytes(int resource_id) override; diff --git a/chromium/fuchsia/engine/context_provider_impl.cc b/chromium/fuchsia/engine/context_provider_impl.cc index cacb6e9905b..5c8dc52312e 100644 --- a/chromium/fuchsia/engine/context_provider_impl.cc +++ b/chromium/fuchsia/engine/context_provider_impl.cc @@ -170,6 +170,7 @@ bool MaybeAddCommandLineArgsFromConfig(const base::Value& config, switches::kGoogleApiKey, switches::kMaxDecodedImageSizeMb, switches::kRendererProcessLimit, + switches::kUseCmdDecoder, switches::kVModule, switches::kVulkanHeapMemoryLimitMb, switches::kVulkanSyncCpuMemoryLimitMb, diff --git a/chromium/fuchsia/engine/renderer/DEPS b/chromium/fuchsia/engine/renderer/DEPS index f6d592b8e85..3a2d94b1d26 100644 --- a/chromium/fuchsia/engine/renderer/DEPS +++ b/chromium/fuchsia/engine/renderer/DEPS @@ -4,5 +4,5 @@ include_rules = [ "+media", "+mojo/public/cpp/bindings", "+services/service_manager/public/cpp", - "+third_party/blink/public/common/associated_interfaces", + "+third_party/blink/public", ] diff --git a/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.cc b/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.cc index ceee3e16c72..56f71f59732 100644 --- a/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.cc +++ b/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.cc @@ -13,6 +13,7 @@ #include "components/on_load_script_injector/renderer/on_load_script_injector.h" #include "content/public/common/content_switches.h" #include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_view.h" #include "fuchsia/engine/common/cast_streaming.h" #include "fuchsia/engine/features.h" #include "fuchsia/engine/renderer/cast_streaming_demuxer.h" @@ -23,6 +24,7 @@ #include "services/network/public/cpp/features.h" #include "services/service_manager/public/cpp/binder_registry.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" +#include "third_party/blink/public/web/web_view.h" #include "third_party/widevine/cdm/widevine_cdm_common.h" namespace { @@ -30,7 +32,7 @@ namespace { // Returns true if the specified video format can be decoded on hardware. bool IsSupportedHardwareVideoCodec(const media::VideoType& type) { // TODO(crbug.com/1013412): Replace these hardcoded checks with a query to the - // fuchsia.mediacodec FIDL service when fxb/36000 is resolved. + // fuchsia.mediacodec FIDL service. if (type.codec == media::kCodecH264 && type.level <= 41) return true; @@ -43,11 +45,9 @@ bool IsSupportedHardwareVideoCodec(const media::VideoType& type) { class PlayreadyKeySystemProperties : public ::media::KeySystemProperties { public: PlayreadyKeySystemProperties(const std::string& key_system_name, - media::SupportedCodecs supported_codecs, - bool persistent_usage_record_support) + media::SupportedCodecs supported_codecs) : key_system_name_(key_system_name), - supported_codecs_(supported_codecs), - persistent_usage_record_support_(persistent_usage_record_support) {} + supported_codecs_(supported_codecs) {} std::string GetKeySystemName() const override { return key_system_name_; } @@ -75,24 +75,9 @@ class PlayreadyKeySystemProperties : public ::media::KeySystemProperties { return media::EmeConfigRule::NOT_SUPPORTED; } - // For backward compatible, currently JS will create a persistent license - // session and inject a special init data as the persistent usage record - // session signal. In other words, the platform has to announce the support of - // persistent license session to allow JS use the persistent usage record - // session functions. - // TODO(internal b/142749428): Remove once the temporary solution is removed. media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport() const override { - return persistent_usage_record_support_ - ? media::EmeSessionTypeSupport::SUPPORTED - : media::EmeSessionTypeSupport::NOT_SUPPORTED; - } - - media::EmeSessionTypeSupport GetPersistentUsageRecordSessionSupport() - const override { - return persistent_usage_record_support_ - ? media::EmeSessionTypeSupport::SUPPORTED - : media::EmeSessionTypeSupport::NOT_SUPPORTED; + return media::EmeSessionTypeSupport::NOT_SUPPORTED; } media::EmeFeatureSupport GetPersistentStateSupport() const override { @@ -115,7 +100,6 @@ class PlayreadyKeySystemProperties : public ::media::KeySystemProperties { private: const std::string key_system_name_; const media::SupportedCodecs supported_codecs_; - const bool persistent_usage_record_support_; }; } // namespace @@ -156,6 +140,14 @@ void WebEngineContentRendererClient::RenderThreadStarted() { void WebEngineContentRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { + // If this is a top-level frame then it should have a transparent background. + // Both the RenderView and WebView should be guaranteed to be non-null, since + // the |render_frame| was only just created. + if (render_frame->IsMainFrame()) { + render_frame->GetRenderView()->GetWebView()->SetBaseBackgroundColor( + SK_AlphaTRANSPARENT); + } + // Add WebEngine services to the new RenderFrame. // The objects' lifetimes are bound to the RenderFrame's lifetime. new on_load_script_injector::OnLoadScriptInjector(render_frame); @@ -174,13 +166,13 @@ void WebEngineContentRendererClient::RenderFrameCreated( new media_control::MediaPlaybackOptions(render_frame); } -std::unique_ptr<content::URLLoaderThrottleProvider> +std::unique_ptr<blink::URLLoaderThrottleProvider> WebEngineContentRendererClient::CreateURLLoaderThrottleProvider( - content::URLLoaderThrottleProviderType type) { + blink::URLLoaderThrottleProviderType type) { DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); // TODO(crbug.com/976975): Add support for service workers. - if (type == content::URLLoaderThrottleProviderType::kWorker) + if (type == blink::URLLoaderThrottleProviderType::kWorker) return nullptr; return std::make_unique<WebEngineURLLoaderThrottleProvider>(this); @@ -221,6 +213,8 @@ void WebEngineContentRendererClient::AddSupportedKeySystems( // Fuchsia always decrypts audio into clear buffers and return them back to // Chromium. Hardware secured decoders are only available for supported // video codecs. + // TODO(crbug.com/1013412): Replace these hardcoded values with a query to + // the fuchsia.mediacodec FIDL service. key_systems->emplace_back(new cdm::WidevineKeySystemProperties( supported_codecs, // codecs encryption_schemes, // encryption schemes @@ -229,11 +223,10 @@ void WebEngineContentRendererClient::AddSupportedKeySystems( cdm::WidevineKeySystemProperties::Robustness:: HW_SECURE_CRYPTO, // max audio robustness cdm::WidevineKeySystemProperties::Robustness:: - HW_SECURE_ALL, // max video robustness - media::EmeSessionTypeSupport::SUPPORTED, // persistent license - media::EmeSessionTypeSupport::SUPPORTED, // persistent usage record - media::EmeFeatureSupport::ALWAYS_ENABLED, // persistent state - media::EmeFeatureSupport::ALWAYS_ENABLED)); // distinctive identifier + HW_SECURE_ALL, // max video robustness + media::EmeSessionTypeSupport::NOT_SUPPORTED, // persistent license + media::EmeFeatureSupport::ALWAYS_ENABLED, // persistent state + media::EmeFeatureSupport::ALWAYS_ENABLED)); // distinctive identifier } std::string playready_key_system = @@ -241,8 +234,7 @@ void WebEngineContentRendererClient::AddSupportedKeySystems( switches::kPlayreadyKeySystem); if (!playready_key_system.empty()) { key_systems->emplace_back(new PlayreadyKeySystemProperties( - playready_key_system, supported_codecs, - /*persistent_usage_record_support=*/true)); + playready_key_system, supported_codecs)); } } diff --git a/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.h b/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.h index 9d718570b01..b5a57304cd9 100644 --- a/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.h +++ b/chromium/fuchsia/engine/renderer/web_engine_content_renderer_client.h @@ -35,9 +35,9 @@ class WebEngineContentRendererClient : public content::ContentRendererClient { std::vector<std::unique_ptr<media::KeySystemProperties>>* key_systems) override; bool IsSupportedVideoType(const media::VideoType& type) override; - std::unique_ptr<content::URLLoaderThrottleProvider> + std::unique_ptr<blink::URLLoaderThrottleProvider> CreateURLLoaderThrottleProvider( - content::URLLoaderThrottleProviderType type) override; + blink::URLLoaderThrottleProviderType type) override; bool DeferMediaLoad(content::RenderFrame* render_frame, bool has_played_media_before, base::OnceClosure closure) override; diff --git a/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.cc b/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.cc index ea5b4b1d3fa..7fda178d227 100644 --- a/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.cc +++ b/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.cc @@ -18,14 +18,14 @@ WebEngineURLLoaderThrottleProvider::~WebEngineURLLoaderThrottleProvider() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -std::unique_ptr<content::URLLoaderThrottleProvider> +std::unique_ptr<blink::URLLoaderThrottleProvider> WebEngineURLLoaderThrottleProvider::Clone() { // This should only happen for service workers, which we do not support here. NOTREACHED(); return nullptr; } -std::vector<std::unique_ptr<blink::URLLoaderThrottle>> +blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> WebEngineURLLoaderThrottleProvider::CreateThrottles( int render_frame_id, const blink::WebURLRequest& request) { @@ -35,7 +35,7 @@ WebEngineURLLoaderThrottleProvider::CreateThrottles( // bug has been found. CHECK_NE(render_frame_id, MSG_ROUTING_NONE); - std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles; + blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> throttles; scoped_refptr<WebEngineURLLoaderThrottle::UrlRequestRewriteRules>& rules = content_renderer_client_ ->GetWebEngineRenderFrameObserverForRenderFrameId(render_frame_id) diff --git a/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.h b/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.h index 88d656a8600..b4924576367 100644 --- a/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.h +++ b/chromium/fuchsia/engine/renderer/web_engine_url_loader_throttle_provider.h @@ -7,7 +7,7 @@ #include "base/macros.h" #include "base/sequence_checker.h" -#include "content/public/renderer/url_loader_throttle_provider.h" +#include "third_party/blink/public/platform/url_loader_throttle_provider.h" class WebEngineContentRendererClient; @@ -15,15 +15,15 @@ class WebEngineContentRendererClient; // URLLoaderThrottles, implemented as WebEngineURLLoaderThrottles for network // requests. class WebEngineURLLoaderThrottleProvider - : public content::URLLoaderThrottleProvider { + : public blink::URLLoaderThrottleProvider { public: explicit WebEngineURLLoaderThrottleProvider( WebEngineContentRendererClient* content_renderer_client); ~WebEngineURLLoaderThrottleProvider() override; - // content::URLLoaderThrottleProvider implementation. - std::unique_ptr<content::URLLoaderThrottleProvider> Clone() override; - std::vector<std::unique_ptr<blink::URLLoaderThrottle>> CreateThrottles( + // blink::URLLoaderThrottleProvider implementation. + std::unique_ptr<blink::URLLoaderThrottleProvider> Clone() override; + blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> CreateThrottles( int render_frame_id, const blink::WebURLRequest& request) override; void SetOnline(bool is_online) override; diff --git a/chromium/fuchsia/engine/web_engine_integration_test.cc b/chromium/fuchsia/engine/web_engine_integration_test.cc index 8c3e51118c2..6d77bfb2383 100644 --- a/chromium/fuchsia/engine/web_engine_integration_test.cc +++ b/chromium/fuchsia/engine/web_engine_integration_test.cc @@ -17,9 +17,8 @@ #include "media/base/media_switches.h" #include "media/fuchsia/audio/fake_audio_consumer.h" #include "media/fuchsia/camera/fake_fuchsia_camera.h" -#include "net/base/test_completion_callback.h" #include "net/http/http_request_headers.h" -#include "net/socket/tcp_client_socket.h" +#include "testing/gmock/include/gmock/gmock.h" namespace { @@ -245,32 +244,8 @@ TEST_F(WebEngineIntegrationTest, RemoteDebuggingPort) { // handled the Frame tear down. controller_run_loop.Run(); - // Verify that devtools server is shut down properly. WebEngine may shutdown - // the socket after shutting down the Frame, so make several attempts to - // connect until it fails. Don't try to read or write from/to the socket to - // avoid fxb/49779. - bool failed_to_connect = false; - for (int i = 0; i < 10; ++i) { - net::TestCompletionCallback connect_callback; - net::TCPClientSocket connecting_socket( - net::AddressList(net::IPEndPoint(net::IPAddress::IPv4Localhost(), - remote_debugging_port)), - nullptr, nullptr, nullptr, net::NetLogSource()); - int connect_result = connecting_socket.Connect(connect_callback.callback()); - connect_result = connect_callback.GetResult(connect_result); - - if (connect_result == net::OK) { - // If Connect() succeeded then try again a bit later. - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); - continue; - } - - EXPECT_EQ(connect_result, net::ERR_CONNECTION_REFUSED); - failed_to_connect = true; - break; - } - - EXPECT_TRUE(failed_to_connect); + devtools_list = cr_fuchsia::GetDevToolsListFromPort(remote_debugging_port); + EXPECT_TRUE(devtools_list.is_none()); } // Check that remote debugging requests for Frames in non-debuggable Contexts @@ -404,7 +379,7 @@ TEST_F(WebEngineIntegrationMediaTest, MicrophoneAccess_WithoutPermission) { navigation_controller_.get(), fuchsia::web::LoadUrlParams(), embedded_test_server_.GetURL("/mic.html?NoPermission").spec())); - navigation_listener_->RunUntilTitleEquals("ended"); + navigation_listener_->RunUntilTitleEquals("ended-NotFoundError"); } TEST_F(WebEngineIntegrationMediaTest, SetBlockMediaLoading_Blocked) { diff --git a/chromium/fuchsia/engine/web_engine_integration_test_base.cc b/chromium/fuchsia/engine/web_engine_integration_test_base.cc index 513953a8a76..91345438687 100644 --- a/chromium/fuchsia/engine/web_engine_integration_test_base.cc +++ b/chromium/fuchsia/engine/web_engine_integration_test_base.cc @@ -81,13 +81,16 @@ WebEngineIntegrationTestBase::ContextParamsWithFilteredServiceDirectory() { std::make_unique<base::FilteredServiceDirectory>( base::ComponentContextForProcess()->svc().get()); fidl::InterfaceHandle<fuchsia::io::Directory> svc_dir; - filtered_service_directory_->ConnectClient(svc_dir.NewRequest()); + EXPECT_EQ(filtered_service_directory_->ConnectClient(svc_dir.NewRequest()), + ZX_OK); // Push all services from /svc to the service directory. base::FileEnumerator file_enum(base::FilePath("/svc"), false, base::FileEnumerator::FILES); for (auto file = file_enum.Next(); !file.empty(); file = file_enum.Next()) { - filtered_service_directory_->AddService(file.BaseName().value().c_str()); + EXPECT_EQ(filtered_service_directory_->AddService( + file.BaseName().value().c_str()), + ZX_OK); } fuchsia::web::CreateContextParams create_params; diff --git a/chromium/fuchsia/engine/web_engine_integration_tests.cmx b/chromium/fuchsia/engine/web_engine_integration_tests.cmx deleted file mode 100644 index a389cdc3417..00000000000 --- a/chromium/fuchsia/engine/web_engine_integration_tests.cmx +++ /dev/null @@ -1,28 +0,0 @@ -{ - "sandbox": { - "features": [ - "hub", - "isolated-persistent-storage", - "isolated-temp" - ], - "services": [ - "fuchsia.accessibility.semantics.SemanticsManager", - "fuchsia.device.NameProvider", - "fuchsia.fonts.Provider", - "fuchsia.intl.PropertyProvider", - "fuchsia.logger.Log", - "fuchsia.logger.LogSink", - "fuchsia.media.Audio", - "fuchsia.media.SessionAudioConsumerFactory", - "fuchsia.memorypressure.Provider", - "fuchsia.net.NameLookup", - "fuchsia.net.interfaces.State", - "fuchsia.posix.socket.Provider", - "fuchsia.process.Launcher", - "fuchsia.sys.Launcher", - "fuchsia.sysmem.Allocator", - "fuchsia.vulkan.loader.Loader", - "fuchsia.web.ContextProvider" - ] - } -} diff --git a/chromium/fuchsia/http/BUILD.gn b/chromium/fuchsia/http/BUILD.gn index 0144f0b4b8d..38afeabe2d2 100644 --- a/chromium/fuchsia/http/BUILD.gn +++ b/chromium/fuchsia/http/BUILD.gn @@ -54,6 +54,8 @@ test("http_service_tests") { "//testing/gtest", ] data = [ "testdata/" ] + additional_manifest_fragments = + [ "//build/config/fuchsia/test/network_capabilities.test-cmx" ] } if (is_official_build) { diff --git a/chromium/fuchsia/release/size_tests/fyi_sizes.json b/chromium/fuchsia/release/size_tests/fyi_sizes.json index 86ee4f8def5..c061514db12 100644 --- a/chromium/fuchsia/release/size_tests/fyi_sizes.json +++ b/chromium/fuchsia/release/size_tests/fyi_sizes.json @@ -3,11 +3,10 @@ "gen/fuchsia/engine/web_engine/web_engine.far", "gen/fuchsia/runners/cast_runner/cast_runner.far" ], - "zstd_args" : [ "-14" ], "far_total_name" : "chrome_fuchsia", "size_limits" : { - "chrome_fuchsia_compressed": 54e6, - "cast_runner_compressed": 7e6, - "web_engine_compressed": 53e6 + "chrome_fuchsia_compressed": 1e12, + "cast_runner_compressed": 1e12, + "web_engine_compressed": 1e12 } } diff --git a/chromium/fuchsia/release/size_tests/fyi_sizes_smoketest.json b/chromium/fuchsia/release/size_tests/fyi_sizes_smoketest.json new file mode 100644 index 00000000000..c061514db12 --- /dev/null +++ b/chromium/fuchsia/release/size_tests/fyi_sizes_smoketest.json @@ -0,0 +1,12 @@ +{ + "far_files" : [ + "gen/fuchsia/engine/web_engine/web_engine.far", + "gen/fuchsia/runners/cast_runner/cast_runner.far" + ], + "far_total_name" : "chrome_fuchsia", + "size_limits" : { + "chrome_fuchsia_compressed": 1e12, + "cast_runner_compressed": 1e12, + "web_engine_compressed": 1e12 + } +} diff --git a/chromium/fuchsia/release/size_tests/fyi_sizes_warning.json b/chromium/fuchsia/release/size_tests/fyi_sizes_warning.json new file mode 100644 index 00000000000..443a0574415 --- /dev/null +++ b/chromium/fuchsia/release/size_tests/fyi_sizes_warning.json @@ -0,0 +1,10 @@ +{ + "far_files" : [ + "gen/fuchsia/engine/web_engine/web_engine.far", + "gen/fuchsia/runners/cast_runner/cast_runner.far" + ], + "far_total_name" : "chrome_fuchsia", + "size_limits" : { + "chrome_fuchsia_compressed": 38.6e6 + } +} diff --git a/chromium/fuchsia/runners/BUILD.gn b/chromium/fuchsia/runners/BUILD.gn index e5e6bc445ad..ebcbe0edde4 100644 --- a/chromium/fuchsia/runners/BUILD.gn +++ b/chromium/fuchsia/runners/BUILD.gn @@ -198,7 +198,11 @@ test("cast_runner_integration_tests") { "web_engine", ], ] - manifest = "runners_integration_tests.test-cmx" + additional_manifest_fragments = [ + "//build/config/fuchsia/test/network_capabilities.test-cmx", + "//build/config/fuchsia/test/vulkan_capabilities.test-cmx", + "//build/config/fuchsia/test/web_engine_required_capabilities.test-cmx", + ] } test("cast_runner_browsertests") { @@ -223,6 +227,11 @@ test("cast_runner_browsertests") { "//testing/gtest", "//ui/ozone", ] + additional_manifest_fragments = [ + "//build/config/fuchsia/test/jit_capabilities.test-cmx", + "//build/config/fuchsia/test/network_capabilities.test-cmx", + "//build/config/fuchsia/test/vulkan_capabilities.test-cmx", + ] } executable("web_runner_exe") { @@ -274,7 +283,11 @@ test("web_runner_integration_tests") { "web_runner", ], ] - manifest = "runners_integration_tests.test-cmx" + additional_manifest_fragments = [ + "//build/config/fuchsia/test/network_capabilities.test-cmx", + "//build/config/fuchsia/test/vulkan_capabilities.test-cmx", + "//build/config/fuchsia/test/web_engine_required_capabilities.test-cmx", + ] } if (is_official_build) { diff --git a/chromium/fuchsia/runners/cast/cast_component.cc b/chromium/fuchsia/runners/cast/cast_component.cc index 886ad70d8c8..b22234ed50a 100644 --- a/chromium/fuchsia/runners/cast/cast_component.cc +++ b/chromium/fuchsia/runners/cast/cast_component.cc @@ -96,6 +96,38 @@ void CastComponent::SetOnDestroyedCallback(base::OnceClosure on_destroyed) { on_destroyed_ = std::move(on_destroyed); } +void CastComponent::ConnectMetricsRecorder( + fidl::InterfaceRequest<fuchsia::legacymetrics::MetricsRecorder> request) { + startup_context()->svc()->Connect(std::move(request)); +} + +void CastComponent::ConnectAudio( + fidl::InterfaceRequest<fuchsia::media::Audio> request) { + agent_manager_->ConnectToAgentService(application_config_.agent_url(), + std::move(request)); +} + +void CastComponent::ConnectDeviceWatcher( + fidl::InterfaceRequest<fuchsia::camera3::DeviceWatcher> request) { + agent_manager_->ConnectToAgentService(application_config_.agent_url(), + std::move(request)); +} + +bool CastComponent::HasWebPermission( + fuchsia::web::PermissionType permission_type) const { + if (!application_config_.has_permissions()) { + return false; + } + + for (auto& permission : application_config_.permissions()) { + if (permission.has_type() && permission.type() == permission_type) { + return true; + } + } + + return false; +} + void CastComponent::StartComponent() { if (application_config_.has_enable_remote_debugging() && application_config_.enable_remote_debugging()) { diff --git a/chromium/fuchsia/runners/cast/cast_component.h b/chromium/fuchsia/runners/cast/cast_component.h index e575e041870..5a66d852c3f 100644 --- a/chromium/fuchsia/runners/cast/cast_component.h +++ b/chromium/fuchsia/runners/cast/cast_component.h @@ -5,8 +5,13 @@ #ifndef FUCHSIA_RUNNERS_CAST_CAST_COMPONENT_H_ #define FUCHSIA_RUNNERS_CAST_CAST_COMPONENT_H_ +#include <fuchsia/camera3/cpp/fidl.h> +#include <fuchsia/legacymetrics/cpp/fidl.h> +#include <fuchsia/media/cpp/fidl.h> +#include <fuchsia/web/cpp/fidl.h> #include <lib/fidl/cpp/binding.h> #include <memory> +#include <string> #include <utility> #include <vector> @@ -72,17 +77,23 @@ class CastComponent : public WebComponent, void SetOnDestroyedCallback(base::OnceClosure on_destroyed); + void ConnectMetricsRecorder( + fidl::InterfaceRequest<fuchsia::legacymetrics::MetricsRecorder> request); + void ConnectAudio(fidl::InterfaceRequest<fuchsia::media::Audio> request); + void ConnectDeviceWatcher( + fidl::InterfaceRequest<fuchsia::camera3::DeviceWatcher> request); + + bool HasWebPermission(fuchsia::web::PermissionType permission_type) const; + + const std::string& agent_url() const { + return application_config_.agent_url(); + } + // WebComponent overrides. void StartComponent() final; void DestroyComponent(int64_t termination_exit_code, fuchsia::sys::TerminationReason reason) final; - const chromium::cast::ApplicationConfig& application_config() { - return application_config_; - } - - cr_fuchsia::AgentManager* agent_manager() { return agent_manager_.get(); } - private: void OnRewriteRulesReceived( std::vector<fuchsia::web::UrlRequestRewriteRule> url_rewrite_rules); diff --git a/chromium/fuchsia/runners/cast/cast_runner.cc b/chromium/fuchsia/runners/cast/cast_runner.cc index b40db1b4709..c9a72299009 100644 --- a/chromium/fuchsia/runners/cast/cast_runner.cc +++ b/chromium/fuchsia/runners/cast/cast_runner.cc @@ -39,6 +39,7 @@ static constexpr const char* kServices[] = { "fuchsia.accessibility.semantics.SemanticsManager", "fuchsia.device.NameProvider", "fuchsia.fonts.Provider", + "fuchsia.input.virtualkeyboard.ControllerCreator", "fuchsia.intl.PropertyProvider", "fuchsia.logger.LogSink", "fuchsia.media.SessionAudioConsumerFactory", @@ -52,8 +53,7 @@ static constexpr const char* kServices[] = { "fuchsia.process.Launcher", "fuchsia.settings.Display", "fuchsia.sysmem.Allocator", - "fuchsia.ui.input.ImeService", - "fuchsia.ui.input.ImeVisibilityService", + "fuchsia.ui.input3.Keyboard", "fuchsia.ui.scenic.Scenic", "fuchsia.vulkan.loader.Loader", @@ -63,18 +63,6 @@ static constexpr const char* kServices[] = { // * fuchsia.media.Audio }; -bool IsPermissionGrantedInAppConfig( - const chromium::cast::ApplicationConfig& application_config, - fuchsia::web::PermissionType permission_type) { - if (application_config.has_permissions()) { - for (auto& permission : application_config.permissions()) { - if (permission.has_type() && permission.type() == permission_type) - return true; - } - } - return false; -} - // Names used to partition the Runner's persistent storage for different uses. constexpr char kCdmDataSubdirectoryName[] = "cdm_data"; constexpr char kProfileSubdirectoryName[] = "web_profile"; @@ -294,23 +282,36 @@ CastRunner::CastRunner(bool is_headless) // Specify the services to connect via the Runner process' service directory. for (const char* name : kServices) { - main_services_->AddService(name); - isolated_services_->AddService(name); + zx_status_t status = main_services_->AddService(name); + ZX_CHECK(status == ZX_OK, status) + << "AddService(" << name << ") to main failed"; + status = isolated_services_->AddService(name); + ZX_CHECK(status == ZX_OK, status) + << "AddService(" << name << ") to isolated failed"; } // Add handlers to main context's service directory for redirected services. - main_services_->outgoing_directory()->AddPublicService<fuchsia::media::Audio>( - fit::bind_member(this, &CastRunner::OnAudioServiceRequest)); - main_services_->outgoing_directory() - ->AddPublicService<fuchsia::camera3::DeviceWatcher>( - fit::bind_member(this, &CastRunner::OnCameraServiceRequest)); - main_services_->outgoing_directory() - ->AddPublicService<fuchsia::legacymetrics::MetricsRecorder>( - fit::bind_member(this, &CastRunner::OnMetricsRecorderServiceRequest)); + zx_status_t status = + main_services_->outgoing_directory() + ->AddPublicService<fuchsia::media::Audio>( + fit::bind_member(this, &CastRunner::OnAudioServiceRequest)); + ZX_CHECK(status == ZX_OK, status) << "AddPublicService(Audio) to main failed"; + status = main_services_->outgoing_directory() + ->AddPublicService<fuchsia::camera3::DeviceWatcher>( + fit::bind_member(this, &CastRunner::OnCameraServiceRequest)); + ZX_CHECK(status == ZX_OK, status) + << "AddPublicService(DeviceWatcher) to main failed"; + status = main_services_->outgoing_directory() + ->AddPublicService<fuchsia::legacymetrics::MetricsRecorder>( + fit::bind_member( + this, &CastRunner::OnMetricsRecorderServiceRequest)); + ZX_CHECK(status == ZX_OK, status) + << "AddPublicService(MetricsRecorder) to main failed"; // Isolated contexts can use the normal Audio service, and don't record // metrics. - isolated_services_->AddService(fuchsia::media::Audio::Name_); + status = isolated_services_->AddService(fuchsia::media::Audio::Name_); + ZX_CHECK(status == ZX_OK, status) << "AddService(Audio) to isolated failed"; } CastRunner::~CastRunner() = default; @@ -456,34 +457,36 @@ void CastRunner::LaunchPendingComponent(PendingCastComponent* pending_component, std::vector<fuchsia::net::http::Header>()); if (component_owner == main_context_.get()) { - const auto& application_config = cast_component->application_config(); + // For components in the main Context the cache sentinel file should have + // been created as a side-effect of |CastComponent::StartComponent()|. + DCHECK(was_cache_sentinel_created_); // If this component has the microphone permission then use it to route // Audio service requests through. - if (IsPermissionGrantedInAppConfig( - application_config, fuchsia::web::PermissionType::MICROPHONE)) { + if (cast_component->HasWebPermission( + fuchsia::web::PermissionType::MICROPHONE)) { if (first_audio_capturer_agent_url_.empty()) { - first_audio_capturer_agent_url_ = application_config.agent_url(); + first_audio_capturer_agent_url_ = cast_component->agent_url(); } else { - LOG_IF(WARNING, first_audio_capturer_agent_url_ != - application_config.agent_url()) + LOG_IF(WARNING, + first_audio_capturer_agent_url_ != cast_component->agent_url()) << "Audio capturer already in use for different agent. " "Current agent: " - << application_config.agent_url(); + << cast_component->agent_url(); } audio_capturer_components_.emplace(cast_component.get()); } - if (IsPermissionGrantedInAppConfig(application_config, - fuchsia::web::PermissionType::CAMERA)) { + if (cast_component->HasWebPermission( + fuchsia::web::PermissionType::CAMERA)) { if (first_video_capturer_agent_url_.empty()) { - first_video_capturer_agent_url_ = application_config.agent_url(); + first_video_capturer_agent_url_ = cast_component->agent_url(); } else { - LOG_IF(WARNING, first_video_capturer_agent_url_ != - application_config.agent_url()) + LOG_IF(WARNING, + first_video_capturer_agent_url_ != cast_component->agent_url()) << "Video capturer already in use for different agent. " "Current agent: " - << application_config.agent_url(); + << cast_component->agent_url(); } video_capturer_components_.emplace(cast_component.get()); } @@ -555,8 +558,9 @@ fuchsia::web::CreateContextParams CastRunner::GetMainContextParams() { *params.mutable_features() |= fuchsia::web::ContextFeatureFlags::NETWORK | fuchsia::web::ContextFeatureFlags::LEGACYMETRICS; - main_services_->ConnectClient( + zx_status_t status = main_services_->ConnectClient( params.mutable_service_directory()->NewRequest()); + ZX_CHECK(status == ZX_OK, status) << "ConnectClient failed"; if (!disable_vulkan_for_test_) SetCdmParamsForMainContext(¶ms); @@ -581,8 +585,9 @@ CastRunner::GetIsolatedContextParamsWithFuchsiaDirs( fuchsia::web::CreateContextParams params = GetCommonContextParams(); params.set_remote_debugging_port(kEphemeralRemoteDebuggingPort); params.set_content_directories(std::move(content_directories)); - isolated_services_->ConnectClient( + zx_status_t status = isolated_services_->ConnectClient( params.mutable_service_directory()->NewRequest()); + ZX_CHECK(status == ZX_OK, status) << "ConnectClient failed"; return params; } @@ -593,8 +598,9 @@ CastRunner::GetIsolatedContextParamsForCastStreaming() { ApplyCastStreamingContextParams(¶ms); // TODO(crbug.com/1069746): Use a different FilteredServiceDirectory for Cast // Streaming Contexts. - main_services_->ConnectClient( + zx_status_t status = main_services_->ConnectClient( params.mutable_service_directory()->NewRequest()); + ZX_CHECK(status == ZX_OK, status) << "ConnectClient failed"; return params; } @@ -647,9 +653,7 @@ void CastRunner::OnAudioServiceRequest( // fuchsia.media.Audio requests to the corresponding agent. if (!audio_capturer_components_.empty()) { CastComponent* capturer_component = *audio_capturer_components_.begin(); - capturer_component->agent_manager()->ConnectToAgentService( - capturer_component->application_config().agent_url(), - std::move(request)); + capturer_component->ConnectAudio(std::move(request)); return; } @@ -665,9 +669,7 @@ void CastRunner::OnCameraServiceRequest( // fuchsia.camera3.DeviceWatcher requests to the corresponding agent. if (!video_capturer_components_.empty()) { CastComponent* capturer_component = *video_capturer_components_.begin(); - capturer_component->agent_manager()->ConnectToAgentService( - capturer_component->application_config().agent_url(), - std::move(request)); + capturer_component->ConnectDeviceWatcher(std::move(request)); return; } @@ -688,7 +690,7 @@ void CastRunner::OnMetricsRecorderServiceRequest( if (any_component) { VLOG(1) << "Connecting MetricsRecorder via CastComponent."; CastComponent* component = reinterpret_cast<CastComponent*>(any_component); - component->startup_context()->svc()->Connect(std::move(request)); + component->ConnectMetricsRecorder(std::move(request)); return; } diff --git a/chromium/fuchsia/runners/cast/cast_runner.cmx b/chromium/fuchsia/runners/cast/cast_runner.cmx index 4b64d375b6f..d6882df9542 100644 --- a/chromium/fuchsia/runners/cast/cast_runner.cmx +++ b/chromium/fuchsia/runners/cast/cast_runner.cmx @@ -11,6 +11,7 @@ "fuchsia.device.NameProvider", "fuchsia.feedback.CrashReportingProductRegister", "fuchsia.fonts.Provider", + "fuchsia.input.virtualkeyboard.ControllerCreator", "fuchsia.intl.PropertyProvider", "fuchsia.logger.LogSink", "fuchsia.media.Audio", @@ -25,8 +26,7 @@ "fuchsia.process.Launcher", "fuchsia.settings.Display", "fuchsia.sysmem.Allocator", - "fuchsia.ui.input.ImeService", - "fuchsia.ui.input.ImeVisibilityService", + "fuchsia.ui.input3.Keyboard", "fuchsia.ui.scenic.Scenic", "fuchsia.vulkan.loader.Loader", "fuchsia.web.ContextProvider" diff --git a/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc b/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc index 00cf835855a..af0949ebda8 100644 --- a/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc +++ b/chromium/fuchsia/runners/cast/cast_runner_integration_test.cc @@ -23,6 +23,7 @@ #include "base/fuchsia/process_context.h" #include "base/fuchsia/scoped_service_binding.h" #include "base/fuchsia/test_component_controller.h" +#include "base/macros.h" #include "base/path_service.h" #include "base/strings/strcat.h" #include "base/strings/string_piece.h" @@ -222,146 +223,6 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase { base::OnceClosure on_delete_; }; -enum CastRunnerFeatures { - kCastRunnerFeaturesNone = 0, - kCastRunnerFeaturesHeadless = 1, - kCastRunnerFeaturesVulkan = 1 << 1, - kCastRunnerFeaturesFrameHost = 1 << 2 -}; - -sys::ServiceDirectory StartCastRunner( - fidl::InterfaceHandle<fuchsia::io::Directory> web_engine_host_directory, - CastRunnerFeatures runner_features, - fidl::InterfaceRequest<fuchsia::sys::ComponentController> - component_controller_request) { - fuchsia::sys::LaunchInfo launch_info; - launch_info.url = - "fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx"; - - // Clone stderr from the current process to CastRunner and ask it to - // redirect all logs to stderr. - launch_info.err = fuchsia::sys::FileDescriptor::New(); - launch_info.err->type0 = PA_FD; - zx_status_t status = fdio_fd_clone( - STDERR_FILENO, launch_info.err->handle0.reset_and_get_address()); - ZX_CHECK(status == ZX_OK, status); - - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII("enable-logging", "stderr"); - - if (runner_features & kCastRunnerFeaturesHeadless) - command_line.AppendSwitch(kForceHeadlessForTestsSwitch); - if (!(runner_features & kCastRunnerFeaturesVulkan)) - command_line.AppendSwitch(kDisableVulkanForTestsSwitch); - if (runner_features & kCastRunnerFeaturesFrameHost) - command_line.AppendSwitch(kEnableFrameHostComponent); - - // Add all switches and arguments, skipping the program. - launch_info.arguments.emplace(std::vector<std::string>( - command_line.argv().begin() + 1, command_line.argv().end())); - - fidl::InterfaceHandle<fuchsia::io::Directory> cast_runner_services_dir; - launch_info.directory_request = - cast_runner_services_dir.NewRequest().TakeChannel(); - - // Redirect ContextProvider to |web_engine_host_directory|. - launch_info.additional_services = - std::make_unique<fuchsia::sys::ServiceList>(); - launch_info.additional_services->host_directory = - web_engine_host_directory.TakeChannel(); - launch_info.additional_services->names.push_back( - fuchsia::web::ContextProvider::Name_); - - fuchsia::sys::LauncherPtr launcher; - base::ComponentContextForProcess()->svc()->Connect(launcher.NewRequest()); - launcher->CreateComponent(std::move(launch_info), - std::move(component_controller_request)); - return sys::ServiceDirectory(std::move(cast_runner_services_dir)); -} - -} // namespace - -class CastRunnerIntegrationTest : public testing::Test { - public: - CastRunnerIntegrationTest() - : CastRunnerIntegrationTest(kCastRunnerFeaturesNone) {} - - CastRunnerIntegrationTest(const CastRunnerIntegrationTest&) = delete; - CastRunnerIntegrationTest& operator=(const CastRunnerIntegrationTest&) = - delete; - - void TearDown() override { - // Unbind the Runner channel, to prevent it from triggering an error when - // the CastRunner and WebEngine are torn down. - cast_runner_.Unbind(); - } - - protected: - explicit CastRunnerIntegrationTest(CastRunnerFeatures runner_features) { - StartAndPublishWebEngine(); - - // Start CastRunner. - fidl::InterfaceHandle<::fuchsia::io::Directory> incoming_services; - services_for_cast_runner_.GetOrCreateDirectory("svc")->Serve( - ::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE, - incoming_services.NewRequest().TakeChannel()); - sys::ServiceDirectory cast_runner_services = - StartCastRunner(std::move(incoming_services), runner_features, - cast_runner_controller_.ptr().NewRequest()); - - // Connect to the CastRunner's fuchsia.sys.Runner interface. - cast_runner_ = cast_runner_services.Connect<fuchsia::sys::Runner>(); - cast_runner_.set_error_handler([](zx_status_t status) { - ZX_LOG(ERROR, status) << "CastRunner closed channel."; - ADD_FAILURE(); - }); - - test_server_.ServeFilesFromSourceDirectory(kTestServerRoot); - net::test_server::RegisterDefaultHandlers(&test_server_); - EXPECT_TRUE(test_server_.Start()); - } - - void StartAndPublishWebEngine() { - fidl::InterfaceHandle<fuchsia::io::Directory> web_engine_outgoing_dir = - cr_fuchsia::StartWebEngineForTests( - web_engine_controller_.ptr().NewRequest()); - sys::ServiceDirectory web_engine_outgoing_services( - std::move(web_engine_outgoing_dir)); - - services_for_cast_runner_ - .RemovePublicService<fuchsia::web::ContextProvider>(); - services_for_cast_runner_.AddPublicService( - std::make_unique<vfs::Service>( - [web_engine_outgoing_services = - std::move(web_engine_outgoing_services)]( - zx::channel channel, async_dispatcher_t* dispatcher) { - web_engine_outgoing_services.Connect( - fuchsia::web::ContextProvider::Name_, std::move(channel)); - }), - fuchsia::web::ContextProvider::Name_); - } - - base::test::SingleThreadTaskEnvironment task_environment_{ - base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; - net::EmbeddedTestServer test_server_; - - // TODO(https://crbug.com/1168538): Override the RunLoop timeout set by - // |task_environment_| to allow for the very high variability in web.Context - // launch times. - const base::test::ScopedRunLoopTimeout scoped_timeout_{ - FROM_HERE, TestTimeouts::action_max_timeout()}; - - base::TestComponentController web_engine_controller_; - base::TestComponentController cast_runner_controller_; - - // Directory used to publish test ContextProvider to CastRunner. Some tests - // restart ContextProvider, so we can't pass the services directory from - // ContextProvider to CastRunner directly. - sys::OutgoingDirectory services_for_cast_runner_; - - fuchsia::sys::RunnerPtr cast_runner_; -}; - class TestCastComponent { public: TestCastComponent(fuchsia::sys::Runner* cast_runner) @@ -380,6 +241,8 @@ class TestCastComponent { void CreateComponentContextAndStartComponent( base::StringPiece app_id = kTestAppId) { + ASSERT_FALSE(component_context_) + << "ComponentContext may only be created once"; auto component_url = base::StrCat({"cast:", app_id}); InjectQueryApi(); CreateComponentContext(component_url); @@ -389,6 +252,8 @@ class TestCastComponent { } void CreateComponentContext(const base::StringPiece& component_url) { + ASSERT_FALSE(component_context_) + << "ComponentContext may only be created once"; url_request_rewrite_rules_provider_ = std::make_unique<FakeUrlRequestRewriteRulesProvider>(); component_context_ = std::make_unique<cr_fuchsia::FakeComponentContext>( @@ -400,6 +265,9 @@ class TestCastComponent { } void StartCastComponent(base::StringPiece component_url) { + ASSERT_FALSE(component_services_client_) + << "Component may only be started once"; + // Configure the Runner, including a service directory channel to publish // services to. fuchsia::sys::StartupInfo startup_info; @@ -423,9 +291,11 @@ class TestCastComponent { component_services_.GetOrCreateDirectory("svc")->Serve( fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE, directory.NewRequest().TakeChannel()); - component_services_.AddPublicService( - cors_exempt_header_provider_binding_.GetHandler( - &cors_exempt_header_provider_)); + + ASSERT_EQ(component_services_.AddPublicService( + cors_exempt_header_provider_binding_.GetHandler( + &cors_exempt_header_provider_)), + ZX_OK); // Provide the directory of services in the |flat_namespace|. startup_info.flat_namespace.paths.emplace_back(base::kServiceDirectoryPath); @@ -627,6 +497,147 @@ class TestCastComponent { fuchsia::sys::Runner* cast_runner_; }; +enum CastRunnerFeatures { + kCastRunnerFeaturesNone = 0, + kCastRunnerFeaturesHeadless = 1, + kCastRunnerFeaturesVulkan = 1 << 1, + kCastRunnerFeaturesFrameHost = 1 << 2 +}; + +sys::ServiceDirectory StartCastRunner( + fidl::InterfaceHandle<fuchsia::io::Directory> web_engine_host_directory, + CastRunnerFeatures runner_features, + fidl::InterfaceRequest<fuchsia::sys::ComponentController> + component_controller_request) { + fuchsia::sys::LaunchInfo launch_info; + launch_info.url = + "fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx"; + + // Clone stderr from the current process to CastRunner and ask it to + // redirect all logs to stderr. + launch_info.err = fuchsia::sys::FileDescriptor::New(); + launch_info.err->type0 = PA_FD; + zx_status_t status = fdio_fd_clone( + STDERR_FILENO, launch_info.err->handle0.reset_and_get_address()); + ZX_CHECK(status == ZX_OK, status); + + base::CommandLine command_line(base::CommandLine::NO_PROGRAM); + command_line.AppendSwitchASCII("enable-logging", "stderr"); + + if (runner_features & kCastRunnerFeaturesHeadless) + command_line.AppendSwitch(kForceHeadlessForTestsSwitch); + if (!(runner_features & kCastRunnerFeaturesVulkan)) + command_line.AppendSwitch(kDisableVulkanForTestsSwitch); + if (runner_features & kCastRunnerFeaturesFrameHost) + command_line.AppendSwitch(kEnableFrameHostComponent); + + // Add all switches and arguments, skipping the program. + launch_info.arguments.emplace(std::vector<std::string>( + command_line.argv().begin() + 1, command_line.argv().end())); + + fidl::InterfaceHandle<fuchsia::io::Directory> cast_runner_services_dir; + launch_info.directory_request = + cast_runner_services_dir.NewRequest().TakeChannel(); + + // Redirect ContextProvider to |web_engine_host_directory|. + launch_info.additional_services = + std::make_unique<fuchsia::sys::ServiceList>(); + launch_info.additional_services->host_directory = + web_engine_host_directory.TakeChannel(); + launch_info.additional_services->names.push_back( + fuchsia::web::ContextProvider::Name_); + + fuchsia::sys::LauncherPtr launcher; + base::ComponentContextForProcess()->svc()->Connect(launcher.NewRequest()); + launcher->CreateComponent(std::move(launch_info), + std::move(component_controller_request)); + return sys::ServiceDirectory(std::move(cast_runner_services_dir)); +} + +} // namespace + +class CastRunnerIntegrationTest : public testing::Test { + public: + CastRunnerIntegrationTest() + : CastRunnerIntegrationTest(kCastRunnerFeaturesNone) {} + + CastRunnerIntegrationTest(const CastRunnerIntegrationTest&) = delete; + CastRunnerIntegrationTest& operator=(const CastRunnerIntegrationTest&) = + delete; + + void TearDown() override { + // Unbind the Runner channel, to prevent it from triggering an error when + // the CastRunner and WebEngine are torn down. + cast_runner_.Unbind(); + } + + protected: + explicit CastRunnerIntegrationTest(CastRunnerFeatures runner_features) { + StartAndPublishWebEngine(); + + // Start CastRunner. + fidl::InterfaceHandle<::fuchsia::io::Directory> incoming_services; + services_for_cast_runner_.GetOrCreateDirectory("svc")->Serve( + ::fuchsia::io::OPEN_RIGHT_READABLE | ::fuchsia::io::OPEN_RIGHT_WRITABLE, + incoming_services.NewRequest().TakeChannel()); + sys::ServiceDirectory cast_runner_services = + StartCastRunner(std::move(incoming_services), runner_features, + cast_runner_controller_.ptr().NewRequest()); + + // Connect to the CastRunner's fuchsia.sys.Runner interface. + cast_runner_ = cast_runner_services.Connect<fuchsia::sys::Runner>(); + cast_runner_.set_error_handler([](zx_status_t status) { + ZX_LOG(ERROR, status) << "CastRunner closed channel."; + ADD_FAILURE(); + }); + + test_server_.ServeFilesFromSourceDirectory(kTestServerRoot); + net::test_server::RegisterDefaultHandlers(&test_server_); + EXPECT_TRUE(test_server_.Start()); + } + + void StartAndPublishWebEngine() { + fidl::InterfaceHandle<fuchsia::io::Directory> web_engine_outgoing_dir = + cr_fuchsia::StartWebEngineForTests( + web_engine_controller_.ptr().NewRequest()); + sys::ServiceDirectory web_engine_outgoing_services( + std::move(web_engine_outgoing_dir)); + ignore_result(services_for_cast_runner_ + .RemovePublicService<fuchsia::web::ContextProvider>()); + ASSERT_EQ(services_for_cast_runner_.AddPublicService( + std::make_unique<vfs::Service>( + [web_engine_outgoing_services = + std::move(web_engine_outgoing_services)]( + zx::channel channel, async_dispatcher_t* dispatcher) { + web_engine_outgoing_services.Connect( + fuchsia::web::ContextProvider::Name_, + std::move(channel)); + }), + fuchsia::web::ContextProvider::Name_), + ZX_OK); + } + + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; + net::EmbeddedTestServer test_server_; + + // TODO(https://crbug.com/1168538): Override the RunLoop timeout set by + // |task_environment_| to allow for the very high variability in web.Context + // launch times. + const base::test::ScopedRunLoopTimeout scoped_timeout_{ + FROM_HERE, TestTimeouts::action_max_timeout()}; + + base::TestComponentController web_engine_controller_; + base::TestComponentController cast_runner_controller_; + + // Directory used to publish test ContextProvider to CastRunner. Some tests + // restart ContextProvider, so we can't pass the services directory from + // ContextProvider to CastRunner directly. + sys::OutgoingDirectory services_for_cast_runner_; + + fuchsia::sys::RunnerPtr cast_runner_; +}; + // A basic integration test ensuring a basic cast request launches the right // URL in the Chromium service. TEST_F(CastRunnerIntegrationTest, BasicRequest) { @@ -648,7 +659,7 @@ TEST_F(CastRunnerIntegrationTest, CanRecreateContext) { component.app_config_manager()->AddApp(kTestAppId, app_url); // Create a Cast component and verify that it has loaded. - component.CreateComponentContextAndStartComponent(); + component.CreateComponentContextAndStartComponent(kTestAppId); component.CheckAppUrl(app_url); // Terminate the component that provides the ContextProvider service and @@ -663,9 +674,10 @@ TEST_F(CastRunnerIntegrationTest, CanRecreateContext) { // WebContentRunner::CreateFrameWithParams() will synchronously verify that // the web.Context is not-yet-closed, to work-around that. StartAndPublishWebEngine(); - component.app_config_manager()->AddApp(kTestAppId, app_url); - component.CreateComponentContextAndStartComponent(); - component.CheckAppUrl(app_url); + TestCastComponent second_component(cast_runner_.get()); + second_component.app_config_manager()->AddApp(kTestAppId, app_url); + second_component.CreateComponentContextAndStartComponent(kTestAppId); + second_component.CheckAppUrl(app_url); } TEST_F(CastRunnerIntegrationTest, ApiBindings) { @@ -911,13 +923,15 @@ TEST_F(CastRunnerIntegrationTest, MicrophoneRedirect) { // Expect fuchsia.media.Audio connection to be redirected to the agent. base::RunLoop run_loop; - component.component_state()->outgoing_directory()->AddPublicService( - std::make_unique<vfs::Service>( - [quit_closure = run_loop.QuitClosure()]( - zx::channel channel, async_dispatcher_t* dispatcher) mutable { - std::move(quit_closure).Run(); - }), - fuchsia::media::Audio::Name_); + ASSERT_EQ( + component.component_state()->outgoing_directory()->AddPublicService( + std::make_unique<vfs::Service>( + [quit_closure = run_loop.QuitClosure()]( + zx::channel channel, async_dispatcher_t* dispatcher) mutable { + std::move(quit_closure).Run(); + }), + fuchsia::media::Audio::Name_), + ZX_OK); component.ExecuteJavaScript("connectMicrophone();"); @@ -941,13 +955,15 @@ TEST_F(CastRunnerIntegrationTest, CameraRedirect) { // Expect fuchsia.camera3.DeviceWatcher connection to be redirected to the // agent. bool received_device_watcher_request = false; - component.component_state()->outgoing_directory()->AddPublicService( - std::make_unique<vfs::Service>( - [&received_device_watcher_request]( - zx::channel channel, async_dispatcher_t* dispatcher) mutable { - received_device_watcher_request = true; - }), - fuchsia::camera3::DeviceWatcher::Name_); + ASSERT_EQ( + component.component_state()->outgoing_directory()->AddPublicService( + std::make_unique<vfs::Service>( + [&received_device_watcher_request]( + zx::channel channel, async_dispatcher_t* dispatcher) mutable { + received_device_watcher_request = true; + }), + fuchsia::camera3::DeviceWatcher::Name_), + ZX_OK); component.ExecuteJavaScript("connectCamera();"); EXPECT_TRUE(received_device_watcher_request); @@ -1014,13 +1030,17 @@ TEST_F(CastRunnerIntegrationTest, MultipleComponentsUsingCamera) { // Expect fuchsia.camera3.DeviceWatcher connection to be redirected to the // agent. bool received_device_watcher_request = false; - second_component.component_state()->outgoing_directory()->AddPublicService( - std::make_unique<vfs::Service>( - [&received_device_watcher_request]( - zx::channel channel, async_dispatcher_t* dispatcher) mutable { - received_device_watcher_request = true; - }), - fuchsia::camera3::DeviceWatcher::Name_); + ASSERT_EQ( + second_component.component_state() + ->outgoing_directory() + ->AddPublicService(std::make_unique<vfs::Service>( + [&received_device_watcher_request]( + zx::channel channel, + async_dispatcher_t* dispatcher) mutable { + received_device_watcher_request = true; + }), + fuchsia::camera3::DeviceWatcher::Name_), + ZX_OK); second_component.ExecuteJavaScript("connectCamera();"); EXPECT_TRUE(received_device_watcher_request); @@ -1080,12 +1100,14 @@ TEST_F(CastRunnerIntegrationTest, LegacyMetricsRedirect) { base::RunLoop run_loop; // Add MetricsRecorder the the component's incoming_services. - component.component_services()->AddPublicService( - std::make_unique<vfs::Service>( - [&run_loop](zx::channel request, async_dispatcher_t* dispatcher) { - run_loop.Quit(); - }), - fuchsia::legacymetrics::MetricsRecorder::Name_); + ASSERT_EQ( + component.component_services()->AddPublicService( + std::make_unique<vfs::Service>( + [&run_loop](zx::channel request, async_dispatcher_t* dispatcher) { + run_loop.Quit(); + }), + fuchsia::legacymetrics::MetricsRecorder::Name_), + ZX_OK); component.StartCastComponent(component_url); diff --git a/chromium/fuchsia/runners/common/web_component.h b/chromium/fuchsia/runners/common/web_component.h index 31dbc7c936b..62471b3a906 100644 --- a/chromium/fuchsia/runners/common/web_component.h +++ b/chromium/fuchsia/runners/common/web_component.h @@ -65,13 +65,13 @@ class WebComponent : public fuchsia::sys::ComponentController, WebContentRunner* runner() const { return runner_; } + protected: // Returns the component's startup context (e.g. incoming services, public // service directory, etc). base::StartupContext* startup_context() const { return startup_context_.get(); } - protected: // fuchsia::sys::ComponentController implementation. void Kill() override; void Detach() override; diff --git a/chromium/fuchsia/runners/runners_integration_tests.test-cmx b/chromium/fuchsia/runners/runners_integration_tests.test-cmx deleted file mode 100644 index b9815624d0a..00000000000 --- a/chromium/fuchsia/runners/runners_integration_tests.test-cmx +++ /dev/null @@ -1,37 +0,0 @@ -{ - "facets": { - "fuchsia.test": { - "injected-services": { - "fuchsia.fonts.Provider": "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cmx", - "fuchsia.intl.PropertyProvider": "fuchsia-pkg://fuchsia.com/intl_property_manager#meta/intl_property_manager.cmx", - "fuchsia.memorypressure.Provider": "fuchsia-pkg://fuchsia.com/memory_monitor#meta/memory_monitor.cmx", - "fuchsia.net.interfaces.State": "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cmx", - "fuchsia.posix.socket.Provider": "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cmx", - "fuchsia.web.ContextProvider": "fuchsia-pkg://fuchsia.com/web_engine#meta/context_provider.cmx" - }, - "system-services": [ - "fuchsia.device.NameProvider", - "fuchsia.scheduler.ProfileProvider", - "fuchsia.sysmem.Allocator", - "fuchsia.vulkan.loader.Loader" - ] - } - }, - "sandbox": { - "features": [ - "isolated-persistent-storage", - "isolated-temp" - ], - "services": [ - "fuchsia.fonts.Provider", - "fuchsia.intl.PropertyProvider", - "fuchsia.logger.LogSink", - "fuchsia.memorypressure.Provider", - "fuchsia.net.interfaces.State", - "fuchsia.posix.socket.Provider", - "fuchsia.process.Launcher", - "fuchsia.sys.Launcher", - "fuchsia.web.ContextProvider" - ] - } -} diff --git a/chromium/fuchsia/runners/web/web_runner.cmx b/chromium/fuchsia/runners/web/web_runner.cmx index 36fc69d1d9b..f283078ea7f 100644 --- a/chromium/fuchsia/runners/web/web_runner.cmx +++ b/chromium/fuchsia/runners/web/web_runner.cmx @@ -8,6 +8,7 @@ "fuchsia.camera3.DeviceWatcher", "fuchsia.device.NameProvider", "fuchsia.fonts.Provider", + "fuchsia.input.virtualkeyboard.ControllerCreator", "fuchsia.intl.PropertyProvider", "fuchsia.logger.LogSink", "fuchsia.media.Audio", @@ -20,8 +21,7 @@ "fuchsia.posix.socket.Provider", "fuchsia.process.Launcher", "fuchsia.sysmem.Allocator", - "fuchsia.ui.input.ImeService", - "fuchsia.ui.input.ImeVisibilityService", + "fuchsia.ui.input3.Keyboard", "fuchsia.ui.scenic.Scenic", "fuchsia.vulkan.loader.Loader", "fuchsia.web.ContextProvider" |