blob: 41139ae238990b41d8eb309b5fcbd6fb057d0f13 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/fidl/cpp/binding.h>
#include <lib/ui/scenic/cpp/view_token_pair.h>
#include "base/containers/span.h"
#include "base/bind.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/macros.h"
#include "base/test/bind_test_util.h"
#include "base/test/test_timeouts.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.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"
#include "fuchsia/engine/browser/frame_impl.h"
#include "fuchsia/engine/common.h"
#include "fuchsia/engine/test/web_engine_browser_test.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/url_request/url_request_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/url_constants.h"
using testing::_;
using testing::AllOf;
using testing::Field;
using testing::InvokeWithoutArgs;
using testing::Mock;
// Use a shorter name for NavigationState, because it is referenced frequently
// in this file.
using NavigationDetails = fuchsia::web::NavigationState;
using OnNavigationStateChangedCallback =
fuchsia::web::NavigationEventListener::OnNavigationStateChangedCallback;
namespace {
const char kPage1Path[] = "/title1.html";
const char kPage2Path[] = "/title2.html";
const char kPage3Path[] = "/websql.html";
const char kDynamicTitlePath[] = "/dynamic_title.html";
const char kPage1Title[] = "title 1";
const char kPage2Title[] = "title 2";
const char kPage3Title[] = "websql not available";
const char kDataUrl[] =
"data:text/html;base64,PGI+SGVsbG8sIHdvcmxkLi4uPC9iPg==";
const char kTestServerRoot[] = FILE_PATH_LITERAL("fuchsia/engine/test/data");
const int64_t kOnLoadScriptId = 0;
MATCHER_P(NavigationHandleUrlEquals,
url,
"Checks equality with a NavigationHandle's URL.") {
return arg->GetURL() == url;
}
class MockWebContentsObserver : public content::WebContentsObserver {
public:
explicit MockWebContentsObserver(content::WebContents* web_contents) {
Observe(web_contents);
}
~MockWebContentsObserver() override = default;
MOCK_METHOD1(DidStartNavigation, void(content::NavigationHandle*));
MOCK_METHOD1(RenderViewDeleted,
void(content::RenderViewHost* render_view_host));
};
std::vector<uint8_t> StringToUnsignedVector(base::StringPiece str) {
const uint8_t* raw_data = reinterpret_cast<const uint8_t*>(str.data());
return std::vector<uint8_t>(raw_data, raw_data + str.length());
}
std::string StringFromMemBufferOrDie(const fuchsia::mem::Buffer& buffer) {
std::string output;
CHECK(cr_fuchsia::StringFromMemBuffer(buffer, &output));
return output;
}
} // namespace
// Defines a suite of tests that exercise Frame-level functionality, such as
// navigation commands and page events.
class FrameImplTest : public cr_fuchsia::WebEngineBrowserTest {
public:
FrameImplTest()
: run_timeout_(TestTimeouts::action_timeout(),
base::MakeExpectedNotRunClosure(FROM_HERE)) {
set_test_server_root(base::FilePath(kTestServerRoot));
}
~FrameImplTest() = default;
MOCK_METHOD1(OnServeHttpRequest,
void(const net::test_server::HttpRequest& request));
protected:
// Creates a Frame with |navigation_listener_| attached.
fuchsia::web::FramePtr CreateFrame() {
return WebEngineBrowserTest::CreateFrame(&navigation_listener_);
}
cr_fuchsia::TestNavigationListener navigation_listener_;
private:
const base::RunLoop::ScopedRunTimeoutForTest run_timeout_;
DISALLOW_COPY_AND_ASSIGN(FrameImplTest);
};
// Verifies that the browser will navigate and generate a navigation listener
// event when LoadUrl() is called.
IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigateFrame) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url::kAboutBlankURL));
navigation_listener_.RunUntilUrlAndTitleEquals(GURL(url::kAboutBlankURL),
url::kAboutBlankURL);
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigateDataFrame) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), kDataUrl));
navigation_listener_.RunUntilUrlAndTitleEquals(GURL(kDataUrl), kDataUrl);
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, FrameDeletedBeforeContext) {
fuchsia::web::FramePtr frame = CreateFrame();
// Process the frame creation message.
base::RunLoop().RunUntilIdle();
FrameImpl* frame_impl = context_impl()->GetFrameImplForTest(&frame);
MockWebContentsObserver deletion_observer(
frame_impl->web_contents_for_test());
base::RunLoop run_loop;
EXPECT_CALL(deletion_observer, RenderViewDeleted(_))
.WillOnce(InvokeWithoutArgs([&run_loop] { run_loop.Quit(); }));
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url::kAboutBlankURL));
frame.Unbind();
run_loop.Run();
// Check that |context| remains bound after the frame is closed.
EXPECT_TRUE(context());
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ContextDeletedBeforeFrame) {
fuchsia::web::FramePtr frame = CreateFrame();
EXPECT_TRUE(frame);
base::RunLoop run_loop;
frame.set_error_handler([&run_loop](zx_status_t status) {
EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
run_loop.Quit();
});
context().Unbind();
run_loop.Run();
EXPECT_FALSE(frame);
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ContextDeletedBeforeFrameWithView) {
fuchsia::web::FramePtr frame = CreateFrame();
EXPECT_TRUE(frame);
auto view_tokens = scenic::NewViewTokenPair();
frame->CreateView(std::move(view_tokens.first));
base::RunLoop().RunUntilIdle();
base::RunLoop run_loop;
frame.set_error_handler([&run_loop](zx_status_t status) {
EXPECT_EQ(status, ZX_ERR_PEER_CLOSED);
run_loop.Quit();
});
context().Unbind();
run_loop.Run();
EXPECT_FALSE(frame);
}
// TODO(https://crbug.com/695592): Remove this test when WebSQL is removed from
// Chrome.
IN_PROC_BROWSER_TEST_F(FrameImplTest, EnsureWebSqlDisabled) {
fuchsia::web::FramePtr frame = CreateFrame();
EXPECT_TRUE(frame);
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
ASSERT_TRUE(embedded_test_server()->Start());
GURL title3(embedded_test_server()->GetURL(kPage3Path));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title3.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title3, kPage3Title);
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, GoBackAndForward) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
ASSERT_TRUE(embedded_test_server()->Start());
GURL title1(embedded_test_server()->GetURL(kPage1Path));
GURL title2(embedded_test_server()->GetURL(kPage2Path));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title1.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title2.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title);
controller->GoBack();
navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
// At the top of the navigation entry list; this should be a no-op.
controller->GoBack();
// Process the navigation request message.
base::RunLoop().RunUntilIdle();
controller->GoForward();
navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title);
// At the end of the navigation entry list; this should be a no-op.
controller->GoForward();
// Process the navigation request message.
base::RunLoop().RunUntilIdle();
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ReloadFrame) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
&FrameImplTest::OnServeHttpRequest, base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kPage1Path));
EXPECT_CALL(*this, OnServeHttpRequest(_)).Times(testing::AtLeast(1));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, kPage1Title);
// Reload with NO_CACHE.
{
MockWebContentsObserver web_contents_observer(
context_impl()->GetFrameImplForTest(&frame)->web_contents_.get());
base::RunLoop run_loop;
EXPECT_CALL(web_contents_observer,
DidStartNavigation(NavigationHandleUrlEquals(url)))
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
controller->Reload(fuchsia::web::ReloadType::NO_CACHE);
run_loop.Run();
}
// Reload with PARTIAL_CACHE.
{
MockWebContentsObserver web_contents_observer(
context_impl()->GetFrameImplForTest(&frame)->web_contents_.get());
base::RunLoop run_loop;
EXPECT_CALL(web_contents_observer,
DidStartNavigation(NavigationHandleUrlEquals(url)))
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
controller->Reload(fuchsia::web::ReloadType::PARTIAL_CACHE);
run_loop.Run();
}
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, GetVisibleEntry) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
// Verify that a Frame returns an empty NavigationState prior to receiving any
// LoadUrl() calls.
{
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> result(
run_loop.QuitClosure());
controller->GetVisibleEntry(
cr_fuchsia::CallbackToFitFunction(result.GetReceiveCallback()));
run_loop.Run();
EXPECT_FALSE(result->has_title());
EXPECT_FALSE(result->has_url());
EXPECT_FALSE(result->has_page_type());
}
ASSERT_TRUE(embedded_test_server()->Start());
GURL title1(embedded_test_server()->GetURL(kPage1Path));
GURL title2(embedded_test_server()->GetURL(kPage2Path));
// Navigate to a page.
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title1.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
// Verify that GetVisibleEntry() reflects the new Frame navigation state.
{
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> result(
run_loop.QuitClosure());
controller->GetVisibleEntry(
cr_fuchsia::CallbackToFitFunction(result.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(result->has_url());
EXPECT_EQ(result->url(), title1.spec());
ASSERT_TRUE(result->has_title());
EXPECT_EQ(result->title(), kPage1Title);
ASSERT_TRUE(result->has_page_type());
EXPECT_EQ(result->page_type(), fuchsia::web::PageType::NORMAL);
}
// Navigate to another page.
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title2.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title);
// Verify the navigation with GetVisibleEntry().
{
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> result(
run_loop.QuitClosure());
controller->GetVisibleEntry(
cr_fuchsia::CallbackToFitFunction(result.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(result->has_url());
EXPECT_EQ(result->url(), title2.spec());
ASSERT_TRUE(result->has_title());
EXPECT_EQ(result->title(), kPage2Title);
ASSERT_TRUE(result->has_page_type());
EXPECT_EQ(result->page_type(), fuchsia::web::PageType::NORMAL);
}
// Navigate back to the first page.
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title1.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
// Verify the navigation with GetVisibleEntry().
{
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::NavigationState> result(
run_loop.QuitClosure());
controller->GetVisibleEntry(
cr_fuchsia::CallbackToFitFunction(result.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(result->has_url());
EXPECT_EQ(result->url(), title1.spec());
ASSERT_TRUE(result->has_title());
EXPECT_EQ(result->title(), kPage1Title);
ASSERT_TRUE(result->has_page_type());
EXPECT_EQ(result->page_type(), fuchsia::web::PageType::NORMAL);
}
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, NoNavigationObserverAttached) {
fuchsia::web::FramePtr frame = WebEngineBrowserTest::CreateFrame(nullptr);
base::RunLoop().RunUntilIdle();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
ASSERT_TRUE(embedded_test_server()->Start());
GURL title1(embedded_test_server()->GetURL(kPage1Path));
GURL title2(embedded_test_server()->GetURL(kPage2Path));
MockWebContentsObserver observer(
context_impl()->GetFrameImplForTest(&frame)->web_contents_.get());
{
base::RunLoop run_loop;
EXPECT_CALL(observer, DidStartNavigation(NavigationHandleUrlEquals(title1)))
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title1.spec()));
run_loop.Run();
}
{
base::RunLoop run_loop;
EXPECT_CALL(observer, DidStartNavigation(NavigationHandleUrlEquals(title2)))
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title2.spec()));
run_loop.Run();
}
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoad) {
constexpr int64_t kBindingsId = 1234;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kBindingsId, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlEquals(url);
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptUpdatedOnLoad) {
constexpr int64_t kBindingsId = 1234;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kBindingsId, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
// Verify that this script clobbers the previous script, as opposed to being
// injected alongside it. (The latter would result in the title being
// "helloclobber").
frame->AddBeforeLoadJavaScript(
kBindingsId, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString(
"stashed_title = document.title + 'clobber';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, "clobber");
}
// Verifies that bindings are injected in order by producing a cumulative,
// non-commutative result.
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoadOrdered) {
constexpr int64_t kBindingsId1 = 1234;
constexpr int64_t kBindingsId2 = 5678;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kBindingsId1, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
frame->AddBeforeLoadJavaScript(
kBindingsId2, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title += ' there';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello there");
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoadRemoved) {
constexpr int64_t kBindingsId1 = 1234;
constexpr int64_t kBindingsId2 = 5678;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kBindingsId1, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title = 'foo';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
// Add a script which clobbers "foo".
frame->AddBeforeLoadJavaScript(
kBindingsId2, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title = 'bar';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
// Deletes the clobbering script.
frame->RemoveBeforeLoadJavaScript(kBindingsId2);
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, "foo");
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptRemoveInvalidId) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kPage1Path));
fuchsia::web::FramePtr frame = CreateFrame();
frame->RemoveBeforeLoadJavaScript(kOnLoadScriptId);
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, kPage1Title);
}
// Test JS injection by using Javascript to trigger document navigation.
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptImmediate) {
fuchsia::web::FramePtr frame = CreateFrame();
ASSERT_TRUE(embedded_test_server()->Start());
GURL title1(embedded_test_server()->GetURL(kPage1Path));
GURL title2(embedded_test_server()->GetURL(kPage2Path));
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title1.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
frame->ExecuteJavaScriptNoResult(
{title1.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("window.location.href = \"" +
title2.spec() + "\";"),
[](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) {
EXPECT_TRUE(result.is_response());
});
navigation_listener_.RunUntilUrlAndTitleEquals(title2, kPage2Title);
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoadVmoDestroyed) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kOnLoadScriptId, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello");
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavascriptOnLoadWrongOrigin) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kOnLoadScriptId, {"http://example.com"},
cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
// Expect that the original HTML title is used, because we didn't inject a
// script with a replacement title.
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(
url, "Welcome to Stan the Offline Dino's Homepage");
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptOnLoadWildcardOrigin) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kOnLoadScriptId, {"*"},
cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
// Test script injection for the origin 127.0.0.1.
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello");
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url::kAboutBlankURL));
navigation_listener_.RunUntilUrlEquals(GURL(url::kAboutBlankURL));
// Test script injection using a different origin ("localhost"), which should
// still be picked up by the wildcard.
GURL alt_url = embedded_test_server()->GetURL("localhost", kDynamicTitlePath);
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), alt_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(alt_url, "hello");
}
// Test that consecutive scripts are executed in order by computing a cumulative
// result.
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteMultipleJavaScriptsOnLoad) {
constexpr int64_t kOnLoadScriptId2 = kOnLoadScriptId + 1;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kOnLoadScriptId, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
frame->AddBeforeLoadJavaScript(
kOnLoadScriptId2, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title += ' there';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello there");
}
// Test that we can inject scripts before and after RenderFrame creation.
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteOnLoadEarlyAndLateRegistrations) {
constexpr int64_t kOnLoadScriptId2 = kOnLoadScriptId + 1;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kDynamicTitlePath));
fuchsia::web::FramePtr frame = CreateFrame();
frame->AddBeforeLoadJavaScript(
kOnLoadScriptId, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title = 'hello';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello");
frame->AddBeforeLoadJavaScript(
kOnLoadScriptId2, {url.GetOrigin().spec()},
cr_fuchsia::MemBufferFromString("stashed_title += ' there';"),
[](fuchsia::web::Frame_AddBeforeLoadJavaScript_Result result) {
EXPECT_TRUE(result.is_response());
});
// Navigate away to clean the slate.
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url::kAboutBlankURL));
navigation_listener_.RunUntilUrlEquals(GURL(url::kAboutBlankURL));
// Navigate back and see if both scripts are working.
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, "hello there");
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, ExecuteJavaScriptBadEncoding) {
fuchsia::web::FramePtr frame = CreateFrame();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL(kPage1Path));
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(url, kPage1Title);
base::RunLoop run_loop;
// 0xFE is an illegal UTF-8 byte; it should cause UTF-8 conversion to fail.
frame->ExecuteJavaScriptNoResult(
{url.GetOrigin().spec()}, cr_fuchsia::MemBufferFromString("true;\xfe"),
[&run_loop](fuchsia::web::Frame_ExecuteJavaScriptNoResult_Result result) {
EXPECT_TRUE(result.is_err());
EXPECT_EQ(result.err(), fuchsia::web::FrameError::BUFFER_NOT_UTF8);
run_loop.Quit();
});
run_loop.Run();
}
// Verifies that a Frame will handle navigation listener disconnection events
// gracefully.
IN_PROC_BROWSER_TEST_F(FrameImplTest, NavigationObserverDisconnected) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
ASSERT_TRUE(embedded_test_server()->Start());
GURL title1(embedded_test_server()->GetURL(kPage1Path));
GURL title2(embedded_test_server()->GetURL(kPage2Path));
MockWebContentsObserver web_contents_observer(
context_impl()->GetFrameImplForTest(&frame)->web_contents_.get());
EXPECT_CALL(web_contents_observer,
DidStartNavigation(NavigationHandleUrlEquals(title1)));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title1.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
// Disconnect the listener & spin the runloop to propagate the disconnection
// event over IPC.
navigation_listener_bindings().CloseAll();
base::RunLoop().RunUntilIdle();
base::RunLoop run_loop;
EXPECT_CALL(web_contents_observer,
DidStartNavigation(NavigationHandleUrlEquals(title2)))
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title2.spec()));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, DelayedNavigationEventAck) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
ASSERT_TRUE(embedded_test_server()->Start());
GURL title1(embedded_test_server()->GetURL(kPage1Path));
GURL title2(embedded_test_server()->GetURL(kPage2Path));
// Expect an navigation event here, but deliberately postpone acknowledgement
// until the end of the test.
base::RunLoop captured_ack_run_loop;
OnNavigationStateChangedCallback captured_ack_cb;
navigation_listener_.SetBeforeAckHook(base::BindRepeating(
[](OnNavigationStateChangedCallback* dest_cb,
const fuchsia::web::NavigationState&,
OnNavigationStateChangedCallback cb) { *dest_cb = std::move(cb); },
base::Unretained(&captured_ack_cb)));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title1.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
EXPECT_TRUE(captured_ack_cb);
// Navigate to a second page.
{
// Since we have blocked NavigationEventObserver's flow, we must observe the
// lower level browser navigation events directly from the WebContents.
MockWebContentsObserver web_contents_observer(
context_impl()->GetFrameImplForTest(&frame)->web_contents_.get());
base::RunLoop run_loop;
EXPECT_CALL(web_contents_observer,
DidStartNavigation(NavigationHandleUrlEquals(title2)))
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title2.spec()));
run_loop.Run();
}
// Navigate to the first page.
{
MockWebContentsObserver web_contents_observer(
context_impl()->GetFrameImplForTest(&frame)->web_contents_.get());
base::RunLoop run_loop;
EXPECT_CALL(web_contents_observer,
DidStartNavigation(NavigationHandleUrlEquals(title1)))
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), title1.spec()));
run_loop.Run();
}
// Since there was no observable change in navigation state since the last
// ack, there should be no more NavigationEvents generated.
captured_ack_cb();
navigation_listener_.RunUntilUrlAndTitleEquals(title1, kPage1Title);
}
// Observes events specific to the Stop() test case.
struct WebContentsObserverForStop : public content::WebContentsObserver {
using content::WebContentsObserver::Observe;
MOCK_METHOD1(DidStartNavigation, void(content::NavigationHandle*));
MOCK_METHOD0(NavigationStopped, void());
};
IN_PROC_BROWSER_TEST_F(FrameImplTest, Stop) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
ASSERT_TRUE(embedded_test_server()->Start());
// Use a request handler that will accept the connection and stall
// indefinitely.
GURL hung_url(embedded_test_server()->GetURL("/hung"));
{
base::RunLoop run_loop;
WebContentsObserverForStop observer;
observer.Observe(
context_impl()->GetFrameImplForTest(&frame)->web_contents_.get());
EXPECT_CALL(observer, DidStartNavigation(_))
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
controller->LoadUrl(
hung_url.spec(), fuchsia::web::LoadUrlParams(),
[](fuchsia::web::NavigationController_LoadUrl_Result) {});
run_loop.Run();
}
EXPECT_TRUE(
context_impl()->GetFrameImplForTest(&frame)->web_contents_->IsLoading());
{
base::RunLoop run_loop;
WebContentsObserverForStop observer;
observer.Observe(
context_impl()->GetFrameImplForTest(&frame)->web_contents_.get());
EXPECT_CALL(observer, NavigationStopped())
.WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
controller->Stop();
run_loop.Run();
}
EXPECT_FALSE(
context_impl()->GetFrameImplForTest(&frame)->web_contents_->IsLoading());
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessage) {
fuchsia::web::FramePtr frame = CreateFrame();
ASSERT_TRUE(embedded_test_server()->Start());
GURL post_message_url(
embedded_test_server()->GetURL("/window_post_message.html"));
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), post_message_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(post_message_url,
"postmessage");
fuchsia::web::WebMessage message;
message.set_data(cr_fuchsia::MemBufferFromString(kPage1Path));
cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result>
post_result;
frame->PostMessage(
post_message_url.GetOrigin().spec(), std::move(message),
cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
navigation_listener_.RunUntilUrlAndTitleEquals(
embedded_test_server()->GetURL(kPage1Path), kPage1Title);
EXPECT_TRUE(post_result->is_response());
}
// Send a MessagePort to the content, then perform bidirectional messaging
// through the port.
IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessagePassMessagePort) {
fuchsia::web::FramePtr frame = CreateFrame();
ASSERT_TRUE(embedded_test_server()->Start());
GURL post_message_url(embedded_test_server()->GetURL("/message_port.html"));
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), post_message_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(post_message_url,
"messageport");
fuchsia::web::MessagePortPtr message_port;
fuchsia::web::WebMessage msg;
{
fuchsia::web::OutgoingTransferable outgoing;
outgoing.set_message_port(message_port.NewRequest());
std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector;
outgoing_vector.push_back(std::move(outgoing));
msg.set_outgoing_transfer(std::move(outgoing_vector));
msg.set_data(cr_fuchsia::MemBufferFromString("hi"));
cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result>
post_result;
frame->PostMessage(
post_message_url.GetOrigin().spec(), std::move(msg),
cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver(
run_loop.QuitClosure());
message_port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(receiver.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(receiver->has_data());
EXPECT_EQ("got_port", StringFromMemBufferOrDie(receiver->data()));
}
{
msg.set_data(cr_fuchsia::MemBufferFromString("ping"));
cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result>
post_result;
message_port->PostMessage(
std::move(msg),
cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver(
run_loop.QuitClosure());
message_port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(receiver.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(receiver->has_data());
EXPECT_EQ("ack ping", StringFromMemBufferOrDie(receiver->data()));
EXPECT_TRUE(post_result->is_response());
}
}
// Send a MessagePort to the content, then perform bidirectional messaging
// over its channel.
IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageMessagePortDisconnected) {
fuchsia::web::FramePtr frame = CreateFrame();
ASSERT_TRUE(embedded_test_server()->Start());
GURL post_message_url(embedded_test_server()->GetURL("/message_port.html"));
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), post_message_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(post_message_url,
"messageport");
fuchsia::web::MessagePortPtr message_port;
fuchsia::web::WebMessage msg;
{
fuchsia::web::OutgoingTransferable outgoing;
outgoing.set_message_port(message_port.NewRequest());
std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector;
outgoing_vector.push_back(std::move(outgoing));
msg.set_outgoing_transfer(std::move(outgoing_vector));
msg.set_data(cr_fuchsia::MemBufferFromString("hi"));
cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result>
post_result;
frame->PostMessage(
post_message_url.GetOrigin().spec(), std::move(msg),
cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver(
run_loop.QuitClosure());
message_port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(receiver.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(receiver->has_data());
EXPECT_EQ("got_port", StringFromMemBufferOrDie(receiver->data()));
EXPECT_TRUE(post_result->is_response());
}
// Navigating off-page should tear down the Mojo channel, thereby causing the
// MessagePortImpl to self-destruct and tear down its FIDL channel.
{
base::RunLoop run_loop;
message_port.set_error_handler(
[&run_loop](zx_status_t) { run_loop.Quit(); });
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), url::kAboutBlankURL));
run_loop.Run();
}
}
// Send a MessagePort to the content, and through that channel, receive a
// different MessagePort that was created by the content. Verify the second
// channel's liveness by sending a ping to it.
IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageUseContentProvidedPort) {
fuchsia::web::FramePtr frame = CreateFrame();
ASSERT_TRUE(embedded_test_server()->Start());
GURL post_message_url(embedded_test_server()->GetURL("/message_port.html"));
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), post_message_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(post_message_url,
"messageport");
fuchsia::web::MessagePortPtr incoming_message_port;
fuchsia::web::WebMessage msg;
{
fuchsia::web::MessagePortPtr message_port;
fuchsia::web::OutgoingTransferable outgoing;
outgoing.set_message_port(message_port.NewRequest());
std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector;
outgoing_vector.push_back(std::move(outgoing));
msg.set_outgoing_transfer(std::move(outgoing_vector));
msg.set_data(cr_fuchsia::MemBufferFromString("hi"));
cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result>
post_result;
frame->PostMessage(
"*", std::move(msg),
cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver(
run_loop.QuitClosure());
message_port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(receiver.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(receiver->has_data());
EXPECT_EQ("got_port", StringFromMemBufferOrDie(receiver->data()));
ASSERT_TRUE(receiver->has_incoming_transfer());
ASSERT_EQ(receiver->incoming_transfer().size(), 1u);
incoming_message_port =
receiver->mutable_incoming_transfer()->at(0).message_port().Bind();
EXPECT_TRUE(post_result->is_response());
}
// Get the content to send three 'ack ping' messages, which will accumulate in
// the MessagePortImpl buffer.
for (int i = 0; i < 3; ++i) {
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::MessagePort_PostMessage_Result>
post_result(run_loop.QuitClosure());
msg.set_data(cr_fuchsia::MemBufferFromString("ping"));
incoming_message_port->PostMessage(
std::move(msg),
cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
run_loop.Run();
EXPECT_TRUE(post_result->is_response());
}
// Receive another acknowledgement from content on a side channel to ensure
// that all the "ack pings" are ready to be consumed.
{
fuchsia::web::MessagePortPtr ack_message_port;
fuchsia::web::WebMessage msg;
fuchsia::web::OutgoingTransferable outgoing;
outgoing.set_message_port(ack_message_port.NewRequest());
std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector;
outgoing_vector.push_back(std::move(outgoing));
msg.set_outgoing_transfer(std::move(outgoing_vector));
msg.set_data(cr_fuchsia::MemBufferFromString("hi"));
// Quit the runloop only after we've received a WebMessage AND a PostMessage
// result.
cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result>
post_result;
frame->PostMessage(
"*", std::move(msg),
cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver(
run_loop.QuitClosure());
ack_message_port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(receiver.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(receiver->has_data());
EXPECT_EQ("got_port", StringFromMemBufferOrDie(receiver->data()));
EXPECT_TRUE(post_result->is_response());
}
// Pull the three 'ack ping's from the buffer.
for (int i = 0; i < 3; ++i) {
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver(
run_loop.QuitClosure());
incoming_message_port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(receiver.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(receiver->has_data());
EXPECT_EQ("ack ping", StringFromMemBufferOrDie(receiver->data()));
}
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, PostMessageBadOriginDropped) {
fuchsia::web::FramePtr frame = CreateFrame();
ASSERT_TRUE(embedded_test_server()->Start());
GURL post_message_url(embedded_test_server()->GetURL("/message_port.html"));
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), post_message_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(post_message_url,
"messageport");
fuchsia::web::MessagePortPtr bad_origin_incoming_message_port;
fuchsia::web::WebMessage msg;
// PostMessage() to invalid origins should be ignored. We pass in a
// MessagePort but nothing should happen to it.
fuchsia::web::MessagePortPtr unused_message_port;
fuchsia::web::OutgoingTransferable unused_outgoing;
unused_outgoing.set_message_port(unused_message_port.NewRequest());
std::vector<fuchsia::web::OutgoingTransferable> unused_outgoing_vector;
unused_outgoing_vector.push_back(std::move(unused_outgoing));
msg.set_outgoing_transfer(std::move(unused_outgoing_vector));
msg.set_data(cr_fuchsia::MemBufferFromString("bad origin, bad!"));
cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result>
unused_post_result;
frame->PostMessage("https://example.com", std::move(msg),
cr_fuchsia::CallbackToFitFunction(
unused_post_result.GetReceiveCallback()));
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> unused_message_read;
bad_origin_incoming_message_port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(
unused_message_read.GetReceiveCallback()));
// PostMessage() with a valid origin should succeed.
// Verify it by looking for an ack message on the MessagePort we passed in.
// Since message events are handled in order, observing the result of this
// operation will verify whether the previous PostMessage() was received but
// discarded.
fuchsia::web::MessagePortPtr incoming_message_port;
fuchsia::web::MessagePortPtr message_port;
fuchsia::web::OutgoingTransferable outgoing;
outgoing.set_message_port(message_port.NewRequest());
std::vector<fuchsia::web::OutgoingTransferable> outgoing_vector;
outgoing_vector.push_back(std::move(outgoing));
msg.set_outgoing_transfer(std::move(outgoing_vector));
msg.set_data(cr_fuchsia::MemBufferFromString("good origin"));
cr_fuchsia::ResultReceiver<fuchsia::web::Frame_PostMessage_Result>
post_result;
frame->PostMessage(
"*", std::move(msg),
cr_fuchsia::CallbackToFitFunction(post_result.GetReceiveCallback()));
base::RunLoop run_loop;
cr_fuchsia::ResultReceiver<fuchsia::web::WebMessage> receiver(
run_loop.QuitClosure());
message_port->ReceiveMessage(
cr_fuchsia::CallbackToFitFunction(receiver.GetReceiveCallback()));
run_loop.Run();
ASSERT_TRUE(receiver->has_data());
EXPECT_EQ("got_port", StringFromMemBufferOrDie(receiver->data()));
ASSERT_TRUE(receiver->has_incoming_transfer());
ASSERT_EQ(receiver->incoming_transfer().size(), 1u);
incoming_message_port =
receiver->mutable_incoming_transfer()->at(0).message_port().Bind();
EXPECT_TRUE(post_result->is_response());
// Verify that the first PostMessage() call wasn't handled.
EXPECT_FALSE(unused_message_read.has_value());
}
IN_PROC_BROWSER_TEST_F(FrameImplTest, RecreateView) {
fuchsia::web::FramePtr frame = CreateFrame();
ASSERT_TRUE(embedded_test_server()->Start());
// Process the Frame creation request, and verify we can get the FrameImpl.
base::RunLoop().RunUntilIdle();
FrameImpl* frame_impl = context_impl()->GetFrameImplForTest(&frame);
ASSERT_TRUE(frame_impl);
EXPECT_FALSE(frame_impl->has_view_for_test());
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
// Verify that the Frame can navigate, prior to the View being created.
const GURL page1_url(embedded_test_server()->GetURL(kPage1Path));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), page1_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(page1_url, kPage1Title);
// Request a View from the Frame, and pump the loop to process the request.
zx::eventpair owner_token, frame_token;
ASSERT_EQ(zx::eventpair::create(0, &owner_token, &frame_token), ZX_OK);
fuchsia::ui::views::ViewToken view_token;
view_token.value = std::move(frame_token);
frame->CreateView(std::move(view_token));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(frame_impl->has_view_for_test());
// Verify that the Frame still works, by navigating to Page #2.
const GURL page2_url(embedded_test_server()->GetURL(kPage2Path));
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), page2_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(page2_url, kPage2Title);
// Create new View tokens and request a new view.
zx::eventpair owner_token2, frame_token2;
ASSERT_EQ(zx::eventpair::create(0, &owner_token2, &frame_token2), ZX_OK);
fuchsia::ui::views::ViewToken view_token2;
view_token2.value = std::move(frame_token2);
frame->CreateView(std::move(view_token2));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(frame_impl->has_view_for_test());
// Verify that the Frame still works, by navigating back to Page #1.
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, fuchsia::web::LoadUrlParams(), page1_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(page1_url, kPage1Title);
}
class RequestMonitoringFrameImplBrowserTest : public FrameImplTest {
public:
RequestMonitoringFrameImplBrowserTest() = default;
protected:
void SetUpOnMainThread() override {
// Accumulate all http requests made to |embedded_test_server| into
// |accumulated_requests_| container.
embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
&RequestMonitoringFrameImplBrowserTest::MonitorRequestOnIoThread,
base::Unretained(this), base::SequencedTaskRunnerHandle::Get()));
ASSERT_TRUE(embedded_test_server()->Start());
}
void TearDown() override {
EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
}
std::map<GURL, net::test_server::HttpRequest> accumulated_requests_;
private:
void MonitorRequestOnIoThread(
const scoped_refptr<base::SequencedTaskRunner>& main_thread_task_runner,
const net::test_server::HttpRequest& request) {
main_thread_task_runner->PostTask(
FROM_HERE,
base::BindOnce(
&RequestMonitoringFrameImplBrowserTest::MonitorRequestOnMainThread,
base::Unretained(this), request));
}
void MonitorRequestOnMainThread(
const net::test_server::HttpRequest& request) {
accumulated_requests_[request.GetURL()] = request;
}
DISALLOW_COPY_AND_ASSIGN(RequestMonitoringFrameImplBrowserTest);
};
IN_PROC_BROWSER_TEST_F(RequestMonitoringFrameImplBrowserTest, ExtraHeaders) {
fuchsia::web::FramePtr frame = CreateFrame();
fuchsia::web::NavigationControllerPtr controller;
frame->GetNavigationController(controller.NewRequest());
const GURL page_url(embedded_test_server()->GetURL(kPage1Path));
fuchsia::web::LoadUrlParams load_url_params;
fuchsia::net::http::Header header1;
header1.name = StringToUnsignedVector("X-ExtraHeaders");
header1.value = StringToUnsignedVector("1");
fuchsia::net::http::Header header2;
header2.name = StringToUnsignedVector("X-2ExtraHeaders");
header2.value = StringToUnsignedVector("2");
load_url_params.set_headers({header1, header2});
EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
&controller, std::move(load_url_params), page_url.spec()));
navigation_listener_.RunUntilUrlAndTitleEquals(page_url, kPage1Title);
// At this point, the page should be loaded, the server should have received
// the request and the request should be in the map.
const auto iter = accumulated_requests_.find(page_url);
ASSERT_NE(iter, accumulated_requests_.end());
EXPECT_THAT(iter->second.headers,
testing::Contains(testing::Key("X-ExtraHeaders")));
EXPECT_THAT(iter->second.headers,
testing::Contains(testing::Key("X-2ExtraHeaders")));
}