Commit a0f813c9 authored by Wez's avatar Wez Committed by Commit Bot

[Fuchsia] Support embedder-provided UserAgent product & version.

Bug: 969252
Change-Id: Ia54129af9dbcb727aab1d3289be997a31143e004
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1649186
Commit-Queue: Wez <wez@chromium.org>
Reviewed-by: default avatarKevin Marshall <kmarshall@chromium.org>
Reviewed-by: default avatarThiemo Nagel <tnagel@chromium.org>
Reviewed-by: default avatarFabrice de Gans-Riberi <fdegans@chromium.org>
Auto-Submit: Wez <wez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672700}
parent 0a89851b
......@@ -4,8 +4,10 @@
#include "fuchsia/base/frame_test_util.h"
#include "base/json/json_reader.h"
#include "base/run_loop.h"
#include "fuchsia/base/fit_adapter.h"
#include "fuchsia/base/mem_buffer_util.h"
#include "fuchsia/base/result_receiver.h"
#include "fuchsia/base/test_navigation_listener.h"
......@@ -14,16 +16,36 @@ namespace cr_fuchsia {
bool LoadUrlAndExpectResponse(
fuchsia::web::NavigationController* navigation_controller,
fuchsia::web::LoadUrlParams load_url_params,
std::string url) {
base::StringPiece url) {
DCHECK(navigation_controller);
base::RunLoop run_loop;
ResultReceiver<fuchsia::web::NavigationController_LoadUrl_Result> result(
run_loop.QuitClosure());
navigation_controller->LoadUrl(
url, std::move(load_url_params),
url.as_string(), std::move(load_url_params),
CallbackToFitFunction(result.GetReceiveCallback()));
run_loop.Run();
return result->is_response();
}
base::Optional<base::Value> ExecuteJavaScript(fuchsia::web::Frame* frame,
base::StringPiece script) {
base::RunLoop run_loop;
ResultReceiver<fuchsia::web::Frame_ExecuteJavaScript_Result> result(
run_loop.QuitClosure());
frame->ExecuteJavaScript({"*"}, MemBufferFromString(script),
CallbackToFitFunction(result.GetReceiveCallback()));
run_loop.Run();
if (!result.has_value() || !result->is_response())
return {};
std::string result_json;
if (!StringFromMemBuffer(result->response().result, &result_json)) {
return {};
}
return base::JSONReader::Read(result_json);
}
} // namespace cr_fuchsia
......@@ -7,6 +7,10 @@
#include <fuchsia/web/cpp/fidl.h>
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
namespace cr_fuchsia {
// Uses |navigation_controller| to load |url| with |load_url_params|. Returns
......@@ -15,7 +19,12 @@ namespace cr_fuchsia {
bool LoadUrlAndExpectResponse(
fuchsia::web::NavigationController* navigation_controller,
fuchsia::web::LoadUrlParams load_url_params,
std::string url);
base::StringPiece url);
// Executes |script| in the context of |frame|'s top-level document.
// Returns an un-set |base::Optional<>| on failure.
base::Optional<base::Value> ExecuteJavaScript(fuchsia::web::Frame* frame,
base::StringPiece script);
} // namespace cr_fuchsia
......
......@@ -246,6 +246,7 @@ test("web_engine_integration_tests") {
"test_debug_listener.cc",
"test_debug_listener.h",
"web_engine_debug_integration_test.cc",
"web_engine_integration_test.cc",
]
data = [
"test/data",
......
......@@ -12,6 +12,7 @@
#include "fuchsia/engine/browser/web_engine_browser_context.h"
#include "fuchsia/engine/browser/web_engine_browser_main_parts.h"
#include "fuchsia/engine/browser/web_engine_devtools_manager_delegate.h"
#include "fuchsia/engine/common.h"
WebEngineContentBrowserClient::WebEngineContentBrowserClient(
fidl::InterfaceRequest<fuchsia::web::Context> request)
......@@ -43,8 +44,14 @@ std::string WebEngineContentBrowserClient::GetProduct() {
}
std::string WebEngineContentBrowserClient::GetUserAgent() {
return content::BuildUserAgentFromProduct(
version_info::GetProductNameAndVersionForUserAgent());
std::string user_agent = content::BuildUserAgentFromProduct(GetProduct());
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
kUserAgentProductAndVersion)) {
user_agent +=
" " + base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
kUserAgentProductAndVersion);
}
return user_agent;
}
void WebEngineContentBrowserClient::OverrideWebkitPrefs(
......
......@@ -6,3 +6,4 @@
constexpr char kIncognitoSwitch[] = "incognito";
constexpr char kRemoteDebuggerHandles[] = "remote-debugger-handles";
constexpr char kUserAgentProductAndVersion[] = "user-agent-product";
......@@ -20,6 +20,9 @@ WEB_ENGINE_EXPORT extern const char kIncognitoSwitch[];
// a comma-separated list of remote debugger handle IDs as an argument.
WEB_ENGINE_EXPORT extern const char kRemoteDebuggerHandles[];
// Switch passed to Context process to customize the UserAgent string.
WEB_ENGINE_EXPORT extern const char kUserAgentProductAndVersion[];
// Handle ID for the Context interface request passed from ContextProvider to
// Context process.
constexpr uint32_t kContextRequestHandleId = PA_HND(PA_USER0, 0);
......
......@@ -31,6 +31,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "fuchsia/engine/common.h"
#include "net/http/http_util.h"
#include "services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h"
namespace {
......@@ -183,6 +184,31 @@ void ContextProviderImpl::Create(
launch_command.AppendSwitchASCII("--use-gl", "stub");
}
// Validate embedder-supplied product, and optional version, and pass it to
// the Context to include in the UserAgent.
if (params.has_user_agent_product()) {
if (!net::HttpUtil::IsToken(params.user_agent_product())) {
DLOG(ERROR) << "Invalid embedder product.";
context_request.Close(ZX_ERR_INVALID_ARGS);
return;
}
std::string product_tag(params.user_agent_product());
if (params.has_user_agent_version()) {
if (!net::HttpUtil::IsToken(params.user_agent_version())) {
DLOG(ERROR) << "Invalid embedder version.";
context_request.Close(ZX_ERR_INVALID_ARGS);
return;
}
product_tag += "/" + params.user_agent_version();
}
launch_command.AppendSwitchNative(kUserAgentProductAndVersion,
std::move(product_tag));
} else if (params.has_user_agent_version()) {
DLOG(ERROR) << "Embedder version without product.";
context_request.Close(ZX_ERR_INVALID_ARGS);
return;
}
if (launch_for_test_)
launch_for_test_.Run(launch_command, launch_options);
else
......
......@@ -5,6 +5,7 @@
"system-temp" ],
"services": [
"fuchsia.device.NameProvider",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.net.SocketProvider",
"fuchsia.netstack.Netstack",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/web/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include "base/fuchsia/file_utils.h"
#include "base/fuchsia/service_directory_client.h"
#include "base/macros.h"
#include "base/test/scoped_task_environment.h"
#include "fuchsia/base/frame_test_util.h"
#include "fuchsia/base/test_navigation_listener.h"
#include "net/http/http_request_headers.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr char kValidUserAgentProduct[] = "TestProduct";
constexpr char kValidUserAgentVersion[] = "dev.12345";
constexpr char kValidUserAgentProductAndVersion[] = "TestProduct/dev.12345";
constexpr char kInvalidUserAgentProduct[] = "Test/Product";
constexpr char kInvalidUserAgentVersion[] = "dev/12345";
} // namespace
class WebEngineIntegrationTest : public testing::Test {
public:
WebEngineIntegrationTest()
: task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
~WebEngineIntegrationTest() override = default;
void SetUp() override {
web_context_provider_ =
base::fuchsia::ServiceDirectoryClient::ForCurrentProcess()
->ConnectToService<fuchsia::web::ContextProvider>();
web_context_provider_.set_error_handler(
[](zx_status_t status) { ADD_FAILURE(); });
net::test_server::RegisterDefaultHandlers(&embedded_test_server_);
ASSERT_TRUE(embedded_test_server_.Start());
}
fuchsia::web::CreateContextParams DefaultContextParams() const {
fuchsia::web::CreateContextParams create_params;
auto directory = base::fuchsia::OpenDirectory(
base::FilePath(base::fuchsia::kServiceDirectoryPath));
EXPECT_TRUE(directory.is_valid());
create_params.set_service_directory(std::move(directory));
return create_params;
}
void CreateContextAndFrame(fuchsia::web::CreateContextParams params) {
web_context_provider_->Create(std::move(params), context_.NewRequest());
context_.set_error_handler([](zx_status_t status) { ADD_FAILURE(); });
context_->CreateFrame(frame_.NewRequest());
frame_.set_error_handler([](zx_status_t status) { ADD_FAILURE(); });
frame_->GetNavigationController(navigation_controller_.NewRequest());
navigation_controller_.set_error_handler(
[](zx_status_t status) { ADD_FAILURE(); });
}
void CreateContextAndExpectError(fuchsia::web::CreateContextParams params,
zx_status_t expected_error) {
web_context_provider_->Create(std::move(params), context_.NewRequest());
base::RunLoop run_loop;
context_.set_error_handler([&run_loop, expected_error](zx_status_t status) {
EXPECT_EQ(status, expected_error);
run_loop.Quit();
});
run_loop.Run();
}
void CreateContextAndFrameAndLoadUrl(fuchsia::web::CreateContextParams params,
const GURL& url) {
CreateContextAndFrame(std::move(params));
// Attach a navigation listener, to monitor the state of the Frame.
cr_fuchsia::TestNavigationListener listener;
fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
&listener);
frame_->SetNavigationEventListener(listener_binding.NewBinding());
// Navigate the Frame to |url| and wait for it to complete loading.
fuchsia::web::LoadUrlParams load_url_params;
ASSERT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
navigation_controller_.get(), std::move(load_url_params), url.spec()));
// Wait for the URL to finish loading.
listener.RunUntilUrlEquals(url);
}
std::string ExecuteJavaScriptWithStringResult(base::StringPiece script) {
base::Optional<base::Value> value =
cr_fuchsia::ExecuteJavaScript(frame_.get(), script);
return value ? value->GetString() : std::string();
}
protected:
const base::test::ScopedTaskEnvironment task_environment_;
fuchsia::web::ContextProviderPtr web_context_provider_;
net::EmbeddedTestServer embedded_test_server_;
fuchsia::web::ContextPtr context_;
fuchsia::web::FramePtr frame_;
fuchsia::web::NavigationControllerPtr navigation_controller_;
DISALLOW_COPY_AND_ASSIGN(WebEngineIntegrationTest);
};
TEST_F(WebEngineIntegrationTest, ValidUserAgent) {
const std::string kEchoHeaderPath =
std::string("/echoheader?") + net::HttpRequestHeaders::kUserAgent;
const GURL kEchoUserAgentUrl(embedded_test_server_.GetURL(kEchoHeaderPath));
{
// Create a Context with just an embedder product specified.
fuchsia::web::CreateContextParams create_params = DefaultContextParams();
create_params.set_user_agent_product(kValidUserAgentProduct);
CreateContextAndFrameAndLoadUrl(std::move(create_params),
kEchoUserAgentUrl);
// Query & verify that the header echoed into the document body contains
// the product tag.
std::string result =
ExecuteJavaScriptWithStringResult("document.body.innerText;");
EXPECT_TRUE(result.find(kValidUserAgentProduct) != std::string::npos);
// Query & verify that the navigator.userAgent contains the product tag.
result = ExecuteJavaScriptWithStringResult("navigator.userAgent;");
EXPECT_TRUE(result.find(kValidUserAgentProduct) != std::string::npos);
}
{
// Create a Context with both product and version specified.
fuchsia::web::CreateContextParams create_params = DefaultContextParams();
create_params.set_user_agent_product(kValidUserAgentProduct);
create_params.set_user_agent_version(kValidUserAgentVersion);
CreateContextAndFrameAndLoadUrl(std::move(create_params),
kEchoUserAgentUrl);
// Query & verify that the header echoed into the document body contains
// both product & version.
std::string result =
ExecuteJavaScriptWithStringResult("document.body.innerText;");
EXPECT_TRUE(result.find(kValidUserAgentProductAndVersion) !=
std::string::npos);
// Query & verify that the navigator.userAgent contains product & version.
result = ExecuteJavaScriptWithStringResult("navigator.userAgent;");
EXPECT_TRUE(result.find(kValidUserAgentProductAndVersion) !=
std::string::npos);
}
}
TEST_F(WebEngineIntegrationTest, InvalidUserAgent) {
const std::string kEchoHeaderPath =
std::string("/echoheader?") + net::HttpRequestHeaders::kUserAgent;
const GURL kEchoUserAgentUrl(embedded_test_server_.GetURL(kEchoHeaderPath));
{
// Try to create a Context with an invalid embedder product tag.
fuchsia::web::CreateContextParams create_params = DefaultContextParams();
create_params.set_user_agent_product(kInvalidUserAgentProduct);
CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS);
}
{
// Try to create a Context with an embedder version but no product.
fuchsia::web::CreateContextParams create_params = DefaultContextParams();
create_params.set_user_agent_version(kValidUserAgentVersion);
CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS);
}
{
// Try to create a Context with valid product tag, but invalid version.
fuchsia::web::CreateContextParams create_params = DefaultContextParams();
create_params.set_user_agent_product(kValidUserAgentProduct);
create_params.set_user_agent_version(kInvalidUserAgentVersion);
CreateContextAndExpectError(std::move(create_params), ZX_ERR_INVALID_ARGS);
}
}
......@@ -18,6 +18,7 @@
#include "fuchsia/base/agent_impl.h"
#include "fuchsia/base/fake_component_context.h"
#include "fuchsia/base/fit_adapter.h"
#include "fuchsia/base/frame_test_util.h"
#include "fuchsia/base/mem_buffer_util.h"
#include "fuchsia/base/result_receiver.h"
#include "fuchsia/base/test_navigation_listener.h"
......@@ -366,27 +367,11 @@ TEST_F(CastRunnerIntegrationTest, AdditionalHeadersProvider) {
navigation_listener.RunUntilUrlEquals(echo_app_url);
// Check the header was properly set.
{
base::RunLoop run_loop;
web_component->frame()->ExecuteJavaScript(
{echo_app_url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("document.body.innerText"),
[&](fuchsia::web::Frame_ExecuteJavaScript_Result result) {
ASSERT_TRUE(result.is_response());
std::string result_json;
ASSERT_TRUE(cr_fuchsia::StringFromMemBuffer(result.response().result,
&result_json));
EXPECT_EQ(result_json, "\"Value\"");
run_loop.Quit();
});
run_loop.Run();
}
// Shutdown the component and wait for the teardown of its state.
base::RunLoop run_loop;
component_state_->set_on_delete(run_loop.QuitClosure());
component_controller.Unbind();
run_loop.Run();
base::Optional<base::Value> result = cr_fuchsia::ExecuteJavaScript(
web_component->frame(), "document.body.innerText");
ASSERT_TRUE(result);
ASSERT_TRUE(result->is_string());
EXPECT_EQ(result->GetString(), "Value");
}
} // namespace castrunner
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment