blob: c97e4a5e0e30696c930cc9bc986b106084ea1b07 [file] [log] [blame]
// Copyright 2020 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/bind.h"
#include "base/callback.h"
#include "base/check_op.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "chromecast/base/chromecast_switches.h"
#include "chromecast/browser/cast_browser_process.h"
#include "chromecast/browser/extensions/cast_extension_system_factory.h"
#include "chromecast/browser/webview/webview_browser_context.h"
#include "chromecast/browser/webview/webview_controller.h"
#include "chromecast/graphics/cast_window_manager_aura.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_base.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/web_contents_tester.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window_tree_host.h"
using testing::_;
using testing::InSequence;
using testing::Truly;
namespace chromecast {
namespace {
constexpr base::TimeDelta kDefaultTimeout =
base::TimeDelta::FromMilliseconds(5000);
const std::string kKeyInputDataURL = R"HTML(
<!DOCTYPE html>
<html>
<input type="text" id="input" autofocus></input>
<script type="text/javascript">
var keyDownCode = 0;
var keyPressCode = 0;
var keyUpCode = 0;
var events = "";
var inputRect;
var touchX = 0;
var touchY = 0;
document.addEventListener("keydown", function(event) {
keyDownCode = event.keyCode;
events += "keydown ";
if (keyDownCode == 9) {
event.preventDefault();
}
});
document.addEventListener("keypress", function(event) {
keyPressCode = event.keyCode;
events += "keypress ";
});
document.addEventListener("keyup", function(event) {
keyUpCode = event.keyCode;
events += "keyup";
});
inputRect = document.getElementById("input").getBoundingClientRect();
touchX = (inputRect.left + inputRect.right) / 2;
touchY = (inputRect.top + inputRect.bottom) / 2;
document.title = "ready";
</script>
</html>
)HTML";
} // namespace
class MockClient : public WebviewController::Client {
public:
MOCK_METHOD(void,
EnqueueSend,
(std::unique_ptr<webview::WebviewResponse> response));
MOCK_METHOD(void, OnError, (const std::string& error_message));
};
class WebviewTest : public content::BrowserTestBase {
protected:
WebviewTest() = default;
~WebviewTest() override = default;
void PreRunTestOnMainThread() override {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::RunLoop().RunUntilIdle();
context_ = std::make_unique<content::TestBrowserContext>();
run_loop_.reset(new base::RunLoop());
}
void SetUp() final {
SetUpCommandLine(base::CommandLine::ForCurrentProcess());
net::test_server::RegisterDefaultHandlers(embedded_test_server());
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&WebviewTest::HandleRequest, base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
BrowserTestBase::SetUp();
}
void SetUpOnMainThread() final {
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->StartAcceptingConnections();
}
void TearDownOnMainThread() final {
BrowserContextDependencyManager::GetInstance()
->DestroyBrowserContextServices(context_.get());
context_.reset();
}
void SetUpCommandLine(base::CommandLine* command_line) final {
command_line->AppendSwitchASCII(switches::kTestType, "browser");
}
void RunTestOnMainThread() override {}
void PostRunTestOnMainThread() override {}
void RunMessageLoop() {
base::test::ScopedRunLoopTimeout timeout(
FROM_HERE, kDefaultTimeout,
base::BindRepeating(&WebviewTest::OnTimeout, base::Unretained(this)));
run_loop_->Run();
}
// Handle an inbound "request" (simulated gRPC call from client)
// asynchronously.
void SubmitWebviewRequest(WebviewController* controller,
const webview::WebviewRequest& request) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&WebviewController::ProcessRequest,
base::Unretained(controller), request));
}
// Asynchronously submit an attempt to navigate from one page to another.
void SubmitNavigation(content::WebContents* web_contents,
const std::string& path) {
GURL url = embedded_test_server()->GetURL("foo.com", path);
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
[](content::WebContents* web_contents, const GURL& url) {
ignore_result(content::NavigateToURL(web_contents, url));
},
web_contents, url));
}
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
GURL absolute_url = embedded_test_server()->GetURL(request.relative_url);
if (absolute_url.path() != "/test" && absolute_url.path() != "/test2" &&
absolute_url.path() != "/key_input")
return nullptr;
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
if (absolute_url.path() == "/key_input") {
http_response->set_content(kKeyInputDataURL);
http_response->set_content_type("text/html");
} else {
http_response->set_content("hello");
http_response->set_content_type("text/plain");
}
return http_response;
}
std::string OnTimeout() {
Quit();
return "Timeout in webview browsertest";
}
std::function<bool(const std::unique_ptr<webview::WebviewResponse>& response)>
CheckForPageLoadedEvent(const std::string& path) {
return [path](const std::unique_ptr<webview::WebviewResponse>& response) {
if (!response->has_page_event() ||
response->page_event().current_page_state() !=
webview::AsyncPageEvent_State_LOADED)
return false;
GURL url(response->page_event().url());
return url.path() == path;
};
}
void Quit() { run_loop_->QuitWhenIdle(); }
webview::WebviewRequest GenerateKeyInputRequest(
int event_type,
const std::string& key_string) {
webview::WebviewRequest request;
request.mutable_input()->set_event_type(event_type);
request.mutable_input()->set_timestamp(
base::TimeTicks::Now().since_origin().InMicroseconds());
request.mutable_input()->mutable_key()->set_key_string(key_string);
return request;
}
webview::WebviewRequest GenerateTouchInputRequest(int event_type,
float touch_x,
float touch_y) {
webview::WebviewRequest request;
webview::InputEvent* input_event = request.mutable_input();
input_event->set_event_type(event_type);
input_event->set_timestamp(
base::TimeTicks::Now().since_origin().InMicroseconds());
webview::TouchInput* touch_input = input_event->mutable_touch();
touch_input->set_x(touch_x);
touch_input->set_y(touch_y);
touch_input->set_root_x(touch_x);
touch_input->set_root_y(touch_y);
touch_input->set_pointer_type(
static_cast<int>(ui::EventPointerType::kTouch));
touch_input->set_pointer_id(1);
return request;
}
int ExecuteScriptAndExtractInt(content::WebContents* contents,
const std::string& script) {
int value = 0;
EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
contents, "domAutomationController.send(" + script + ")", &value));
return value;
}
std::string ExecuteScriptAndExtractString(content::WebContents* contents,
const std::string& script) {
std::string value = "";
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
contents, "domAutomationController.send(" + script + ")", &value));
return value;
}
double ExecuteScriptAndExtractDouble(content::WebContents* contents,
const std::string& script) {
double value = 0;
EXPECT_TRUE(content::ExecuteScriptAndExtractDouble(
contents, "domAutomationController.send(" + script + ")", &value));
return value;
}
bool URLLoaded(content::WebContents* contents) {
std::u16string ready_title(u"ready");
content::TitleWatcher watcher(contents, ready_title);
const std::u16string title = watcher.WaitAndGetTitle();
return title == ready_title;
}
std::unique_ptr<content::TestBrowserContext> context_;
std::unique_ptr<base::RunLoop> run_loop_;
MockClient client_;
};
IN_PROC_BROWSER_TEST_F(WebviewTest, Navigate) {
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_page_event() &&
response->page_event().current_page_state() ==
webview::AsyncPageEvent_State_LOADED;
};
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(testing::AtLeast(1))
.WillOnce([this](std::unique_ptr<webview::WebviewResponse> response) {
Quit();
});
WebviewController webview(context_.get(), &client_, true);
GURL test_url = embedded_test_server()->GetURL("foo.com", "/test");
webview::WebviewRequest nav;
nav.mutable_navigate()->set_url(test_url.spec());
SubmitWebviewRequest(&webview, nav);
RunMessageLoop();
}
// Verify the navigation request process
// Disabled due to flakiness. http://crbug.com/1192724
IN_PROC_BROWSER_TEST_F(WebviewTest, DISABLED_VerifyNavigationDelegation) {
WebviewController webview(context_.get(), &client_, true);
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
EXPECT_CALL(client_, EnqueueSend(Truly(
// Look for our initial page load event
CheckForPageLoadedEvent("/test"))))
.Times(testing::AtLeast(1))
.WillOnce(
[this, &webview](std::unique_ptr<webview::WebviewResponse> response) {
// Turn on navigation delegation in the webview.
webview::WebviewRequest test_settings_request;
test_settings_request.mutable_update_settings()
->set_has_navigation_delegate(true);
SubmitWebviewRequest(&webview, test_settings_request);
// And now ask to navigate to the second page.
SubmitNavigation(webview.GetWebContents(), "/test2");
});
EXPECT_CALL(
client_,
EnqueueSend(
// Look for navigation events.
Truly([](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_navigation_event();
})))
.Times(testing::AtLeast(1))
.WillOnce(
// Respond with our navigation decision (NAVIGATE)
[this, &webview](std::unique_ptr<webview::WebviewResponse> response) {
webview::WebviewRequest nav_decision_request;
nav_decision_request.set_navigation_decision(
webview::NavigationDecision::NAVIGATE);
// Submit the navigation decision back to the controller.
SubmitWebviewRequest(&webview, nav_decision_request);
});
// Checks for secondary page (/test2) loaded event.
EXPECT_CALL(client_, EnqueueSend(Truly(CheckForPageLoadedEvent("/test2"))))
.Times(testing::AtLeast(1))
.WillOnce([this](std::unique_ptr<webview::WebviewResponse> response) {
Quit();
});
GURL test_url = embedded_test_server()->GetURL("foo.com", "/test");
webview::WebviewRequest nav;
nav.mutable_navigate()->set_url(test_url.spec());
SubmitWebviewRequest(&webview, nav);
RunMessageLoop();
}
IN_PROC_BROWSER_TEST_F(WebviewTest, Incognito) {
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_page_event() &&
response->page_event().current_page_state() ==
webview::AsyncPageEvent_State_LOADED;
};
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(testing::AtLeast(1))
.WillOnce([this](std::unique_ptr<webview::WebviewResponse> response) {
Quit();
});
std::unique_ptr<content::BrowserContext> owned_context =
std::make_unique<WebviewBrowserContext>(context_.get());
WebviewController webview(std::move(owned_context), &client_, true);
GURL test_url = embedded_test_server()->GetURL("foo.com", "/test");
webview::WebviewRequest nav;
nav.mutable_navigate()->set_url(test_url.spec());
webview.ProcessRequest(nav);
RunMessageLoop();
}
IN_PROC_BROWSER_TEST_F(WebviewTest, SetInsets) {
// Webview creation sends messages to the client (eg: accessibility ID).
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
WebviewController webview(context_.get(), &client_, true);
GURL test_url = embedded_test_server()->GetURL("foo.com", "/test");
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_page_event() &&
response->page_event().current_page_state() ==
webview::AsyncPageEvent_State_LOADED;
};
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(testing::AtLeast(1))
.WillOnce(
[this, &webview](std::unique_ptr<webview::WebviewResponse> response) {
webview::WebviewRequest request;
request.mutable_set_insets()->set_top(0);
request.mutable_set_insets()->set_left(0);
request.mutable_set_insets()->set_bottom(200);
request.mutable_set_insets()->set_right(0);
// Note this one needs to be processed synchronously (not through
// SubmitWebviewRequest) because we check the results immediately
// after.
webview.ProcessRequest(request);
gfx::Size size_after = webview.GetWebContents()
->GetRenderWidgetHostView()
->GetVisibleViewportSize();
EXPECT_EQ(gfx::Size(800, 400), size_after);
Quit();
});
// Requests are executed serially. Resize first to make sure the Webview is
// properly sized by the time the page loads.
webview::WebviewRequest resize;
resize.mutable_resize()->set_width(800);
resize.mutable_resize()->set_height(600);
SubmitWebviewRequest(&webview, resize);
webview::WebviewRequest nav;
nav.mutable_navigate()->set_url(test_url.spec());
SubmitWebviewRequest(&webview, nav);
RunMessageLoop();
}
IN_PROC_BROWSER_TEST_F(WebviewTest, UserDataOverrideOnFirstRequest) {
// Webview creation sends messages to the client (eg: accessibility ID).
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
WebviewController webview(context_.get(), &client_, true);
const std::string kHeaderPath =
std::string("/echoheader?") + net::HttpRequestHeaders::kUserAgent;
GURL test_url = embedded_test_server()->GetURL(kHeaderPath);
const std::string kUserAgentOverride = "bar";
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_page_event() &&
response->page_event().current_page_state() ==
webview::AsyncPageEvent_State_LOADED;
};
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(2)
.WillOnce([&](std::unique_ptr<webview::WebviewResponse> response) {
std::string header_value;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
webview.GetWebContents(),
"window.domAutomationController.send(document.body.textContent);",
&header_value));
EXPECT_EQ(kUserAgentOverride, header_value);
// Send an update without the override so we can check that the caller
// can disable the override.
webview::WebviewRequest update_settings;
update_settings.mutable_update_settings()->set_javascript_enabled(true);
SubmitWebviewRequest(&webview, update_settings);
webview::WebviewRequest reload;
reload.mutable_reload();
SubmitWebviewRequest(&webview, reload);
})
.WillOnce([&](std::unique_ptr<webview::WebviewResponse> response) {
std::string header_value;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
webview.GetWebContents(),
"window.domAutomationController.send(document.body.textContent);",
&header_value));
EXPECT_NE(kUserAgentOverride, header_value);
Quit();
});
// Need to enable JS in order to extract the UserAgent string from the loaded
// web page.
webview::WebviewRequest update_settings;
update_settings.mutable_update_settings()->set_javascript_enabled(true);
update_settings.mutable_update_settings()->mutable_user_agent()->set_value(
kUserAgentOverride);
SubmitWebviewRequest(&webview, update_settings);
webview::WebviewRequest navigate;
navigate.mutable_navigate()->set_url(test_url.spec());
SubmitWebviewRequest(&webview, navigate);
RunMessageLoop();
}
IN_PROC_BROWSER_TEST_F(WebviewTest, UserDataOverride) {
// Webview creation sends messages to the client (eg: accessibility ID).
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
WebviewController webview(context_.get(), &client_, true);
const std::string kHeaderPath =
std::string("/echoheader?") + net::HttpRequestHeaders::kUserAgent;
GURL test_url = embedded_test_server()->GetURL(kHeaderPath);
const std::string kUserAgentOverride = "bar";
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_page_event() &&
response->page_event().current_page_state() ==
webview::AsyncPageEvent_State_LOADED;
};
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(2)
.WillOnce([&](std::unique_ptr<webview::WebviewResponse> response) {
std::string header_value;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
webview.GetWebContents(),
"window.domAutomationController.send(document.body.textContent);",
&header_value));
EXPECT_NE(kUserAgentOverride, header_value);
webview::WebviewRequest update_settings;
update_settings.mutable_update_settings()->set_javascript_enabled(true);
update_settings.mutable_update_settings()
->mutable_user_agent()
->set_value(kUserAgentOverride);
SubmitWebviewRequest(&webview, update_settings);
webview::WebviewRequest reload;
reload.mutable_reload();
SubmitWebviewRequest(&webview, reload);
})
.WillOnce([&](std::unique_ptr<webview::WebviewResponse> response) {
std::string header_value;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
webview.GetWebContents(),
"window.domAutomationController.send(document.body.textContent);",
&header_value));
EXPECT_EQ(kUserAgentOverride, header_value);
Quit();
});
// Need to enable JS in order to extract the UserAgent string from the loaded
// web page.
webview::WebviewRequest update_settings;
update_settings.mutable_update_settings()->set_javascript_enabled(true);
SubmitWebviewRequest(&webview, update_settings);
webview::WebviewRequest navigate;
navigate.mutable_navigate()->set_url(test_url.spec());
SubmitWebviewRequest(&webview, navigate);
RunMessageLoop();
}
IN_PROC_BROWSER_TEST_F(WebviewTest, Focus) {
// Webview creation sends messages to the client (eg: accessibility ID).
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
WebviewController webview(context_.get(), &client_, true);
GURL test_url = embedded_test_server()->GetURL("foo.com", "/test");
webview.GetWebContents()->GetNativeView()->Show();
CastWindowManagerAura window_manager(false);
window_manager.Setup();
window_manager.AddWindow(webview.GetWebContents()->GetNativeView());
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_page_event() &&
response->page_event().current_page_state() ==
webview::AsyncPageEvent_State_LOADED;
};
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(testing::AtLeast(1))
.WillOnce(
[this, &webview](std::unique_ptr<webview::WebviewResponse> response) {
webview::WebviewRequest request;
request.mutable_focus();
webview.ProcessRequest(request);
EXPECT_TRUE(webview.GetWebContents()->GetNativeView()->HasFocus());
Quit();
});
webview::WebviewRequest navigate;
navigate.mutable_navigate()->set_url(test_url.spec());
SubmitWebviewRequest(&webview, navigate);
RunMessageLoop();
}
IN_PROC_BROWSER_TEST_F(WebviewTest, GetUserAgent) {
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_get_user_agent();
};
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(testing::AtLeast(1))
.WillOnce([this](std::unique_ptr<webview::WebviewResponse> response) {
EXPECT_NE(response->get_user_agent().user_agent(), "");
Quit();
});
WebviewController webview(context_.get(), &client_, true);
webview::WebviewRequest request;
request.mutable_get_user_agent();
SubmitWebviewRequest(&webview, request);
RunMessageLoop();
}
IN_PROC_BROWSER_TEST_F(WebviewTest, KeyInput) {
// Webview creation sends messages to the client (eg: accessibility ID).
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
CastWindowManagerAura window_manager(false);
window_manager.Setup();
WebviewController webview(context_.get(), &client_, true);
window_manager.AddWindow(webview.GetWebContents()->GetNativeView());
webview.GetWebContents()->GetNativeView()->Show();
webview.GetWebContents()->GetNativeView()->Focus();
GURL test_url = embedded_test_server()->GetURL("foo.com", "/key_input");
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_page_event() &&
response->page_event().current_page_state() ==
webview::AsyncPageEvent_State_LOADED;
};
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(testing::AtLeast(1))
.WillOnce([this,
&webview](std::unique_ptr<webview::WebviewResponse> response) {
DCHECK(URLLoaded(webview.GetWebContents()));
content::RenderFrameSubmissionObserver frame_observer(
webview.GetWebContents());
webview::WebviewRequest input =
GenerateKeyInputRequest(ui::ET_KEY_PRESSED, "a");
webview.ProcessRequest(input);
input = GenerateKeyInputRequest(ui::ET_KEY_RELEASED, "a");
webview.ProcessRequest(input);
while (ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyDownCode") == 0 ||
ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyPressCode") == 0 ||
ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyUpCode") == 0)
frame_observer.WaitForAnyFrameSubmission();
EXPECT_EQ(
ExecuteScriptAndExtractString(webview.GetWebContents(), "events"),
"keydown keypress keyup");
EXPECT_EQ(
ExecuteScriptAndExtractInt(webview.GetWebContents(), "keyDownCode"),
65);
EXPECT_EQ(ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyPressCode"),
97);
EXPECT_EQ(
ExecuteScriptAndExtractInt(webview.GetWebContents(), "keyUpCode"),
65);
input = GenerateKeyInputRequest(ui::ET_KEY_PRESSED, u8"\u00b6");
webview.ProcessRequest(input);
input = GenerateKeyInputRequest(ui::ET_KEY_RELEASED, u8"\u00b6");
webview.ProcessRequest(input);
while (ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyPressCode") == 97)
frame_observer.WaitForAnyFrameSubmission();
// Non-US layout keys should only generate a keypress event.
EXPECT_EQ(
ExecuteScriptAndExtractInt(webview.GetWebContents(), "keyDownCode"),
65);
EXPECT_EQ(ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyPressCode"),
182);
EXPECT_EQ(
ExecuteScriptAndExtractInt(webview.GetWebContents(), "keyUpCode"),
65);
input = GenerateKeyInputRequest(ui::ET_KEY_PRESSED, "Backspace");
webview.ProcessRequest(input);
input = GenerateKeyInputRequest(ui::ET_KEY_RELEASED, "Backspace");
webview.ProcessRequest(input);
while (ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyDownCode") == 0 ||
ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyUpCode") == 0)
frame_observer.WaitForAnyFrameSubmission();
EXPECT_EQ(
ExecuteScriptAndExtractInt(webview.GetWebContents(), "keyDownCode"),
8);
// Backspace does not generate a keypress event.
EXPECT_EQ(ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyPressCode"),
182);
EXPECT_EQ(
ExecuteScriptAndExtractInt(webview.GetWebContents(), "keyUpCode"),
8);
input = GenerateKeyInputRequest(ui::ET_KEY_PRESSED, "Tab");
webview.ProcessRequest(input);
input = GenerateKeyInputRequest(ui::ET_KEY_RELEASED, "Tab");
webview.ProcessRequest(input);
while (ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyDownCode") == 8 ||
ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyUpCode") == 8)
frame_observer.WaitForAnyFrameSubmission();
EXPECT_EQ(
ExecuteScriptAndExtractInt(webview.GetWebContents(), "keyDownCode"),
9);
// Tab does not generate a keypress event.
EXPECT_EQ(ExecuteScriptAndExtractInt(webview.GetWebContents(),
"keyPressCode"),
182);
EXPECT_EQ(
ExecuteScriptAndExtractInt(webview.GetWebContents(), "keyUpCode"),
9);
Quit();
})
.WillRepeatedly(
[](std::unique_ptr<webview::WebviewResponse> response) {});
// Need to enable JS in order to extract the key string from the loaded
// web page.
webview::WebviewRequest update_settings;
update_settings.mutable_update_settings()->set_javascript_enabled(true);
SubmitWebviewRequest(&webview, update_settings);
// Requests are executed serially. Resize first to make sure the Webview is
// properly sized by the time the page loads.
webview::WebviewRequest resize;
resize.mutable_resize()->set_width(800);
resize.mutable_resize()->set_height(600);
SubmitWebviewRequest(&webview, resize);
webview::WebviewRequest navigate;
navigate.mutable_navigate()->set_url(test_url.spec());
SubmitWebviewRequest(&webview, navigate);
RunMessageLoop();
}
IN_PROC_BROWSER_TEST_F(WebviewTest, SendFocusEventWhenVKShouldBeShown) {
// Webview creation sends messages to the client (eg: accessibility ID).
EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber());
CastWindowManagerAura window_manager(false);
window_manager.Setup();
WebviewController webview(context_.get(), &client_, true);
window_manager.AddWindow(webview.GetWebContents()->GetNativeView());
webview.GetWebContents()->GetNativeView()->Show();
webview.GetWebContents()->GetNativeView()->Focus();
webview.OnVisible(webview.GetWebContents()->GetNativeView());
GURL test_url = embedded_test_server()->GetURL("foo.com", "/key_input");
InSequence seq;
auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_page_event() &&
response->page_event().current_page_state() ==
webview::AsyncPageEvent_State_LOADED;
};
EXPECT_CALL(client_, EnqueueSend(Truly(check)))
.Times(testing::AtLeast(1))
.WillOnce([&](std::unique_ptr<webview::WebviewResponse> response) {
DCHECK(URLLoaded(webview.GetWebContents()));
float touch_x =
ExecuteScriptAndExtractDouble(webview.GetWebContents(), "touchX");
float touch_y =
ExecuteScriptAndExtractDouble(webview.GetWebContents(), "touchY");
SubmitWebviewRequest(
&webview,
GenerateTouchInputRequest(ui::ET_TOUCH_PRESSED, touch_x, touch_y));
SubmitWebviewRequest(
&webview,
GenerateTouchInputRequest(ui::ET_TOUCH_RELEASED, touch_x, touch_y));
})
.WillRepeatedly(
[](std::unique_ptr<webview::WebviewResponse> response) {});
auto input_focus_text_check =
[](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_input_focus_event() &&
response->input_focus_event().type() ==
webview::TEXT_INPUT_TYPE_TEXT;
};
EXPECT_CALL(client_, EnqueueSend(Truly(input_focus_text_check)))
.WillOnce([](std::unique_ptr<webview::WebviewResponse> response) {})
.WillOnce([&](std::unique_ptr<webview::WebviewResponse> response) {
// Tap outside the input field to verify focus loss.
SubmitWebviewRequest(&webview, GenerateTouchInputRequest(
ui::ET_TOUCH_PRESSED, 300, 300));
SubmitWebviewRequest(&webview, GenerateTouchInputRequest(
ui::ET_TOUCH_RELEASED, 300, 300));
});
auto input_focus_none_check =
[](const std::unique_ptr<webview::WebviewResponse>& response) {
return response->has_input_focus_event() &&
response->input_focus_event().type() ==
webview::TEXT_INPUT_TYPE_NONE;
};
EXPECT_CALL(client_, EnqueueSend(Truly(input_focus_none_check)))
.WillOnce(
[&](std::unique_ptr<webview::WebviewResponse> response) { Quit(); });
// Need to enable JS in order to extract the text input field touch
// coordinates from the loaded web page.
webview::WebviewRequest update_settings;
update_settings.mutable_update_settings()->set_javascript_enabled(true);
SubmitWebviewRequest(&webview, update_settings);
// Requests are executed serially. Resize first to make sure the Webview is
// properly sized by the time the page loads.
webview::WebviewRequest resize;
resize.mutable_resize()->set_width(500);
resize.mutable_resize()->set_height(500);
SubmitWebviewRequest(&webview, resize);
webview::WebviewRequest navigate;
navigate.mutable_navigate()->set_url(test_url.spec());
SubmitWebviewRequest(&webview, navigate);
RunMessageLoop();
}
} // namespace chromecast