blob: 4a491f01d3d2c569cb105e6a94cd826971e2ed10 [file] [log] [blame]
// Copyright 2017 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 <memory>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "content/public/test/browser_test.h"
#include "headless/lib/browser/headless_browser_context_impl.h"
#include "headless/lib/browser/headless_web_contents_impl.h"
#include "headless/public/devtools/domains/network.h"
#include "headless/public/devtools/domains/page.h"
#include "headless/public/devtools/domains/runtime.h"
#include "headless/public/headless_devtools_client.h"
#include "headless/public/util/testing/test_in_memory_protocol_handler.h"
#include "headless/test/headless_browser_test.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_job_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using testing::ContainerEq;
namespace headless {
namespace {
const char* kIndexHtml = R"(
<html>
<head><link rel="stylesheet" type="text/css" href="style1.css"></head>
<body>
<div class="red">Main frame</div>
<iframe src="iframe1.html"></iframe>
<iframe src="iframe2.html"></iframe>
</body>
</html>)";
const char* kIFrame1 = R"(
<html>
<head><link rel="stylesheet" type="text/css" href="style2.css"></head>
<body>
<div class="green">IFrame 1</div>
</body>
</html>)";
const char* kIFrame2 = R"(
<html>
<head><link rel="stylesheet" type="text/css" href="style3.css"></head>
<body>
<div class="blue">IFrame 1</div>
</body>
</html>)";
const char* kStyle1Css = R"(
.red {
color: #f00
} )";
const char* kStyle2Css = R"(
.green {
color: #0f0
} )";
const char* kStyle3Css = R"(
.blue {
color: #00f
} )";
} // namespace
class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
public network::ExperimentalObserver,
public page::Observer,
public runtime::Observer {
public:
void RunDevTooledTest() override {
http_handler_->SetHeadlessBrowserContext(browser_context_);
EXPECT_TRUE(embedded_test_server()->Start());
devtools_client_->GetNetwork()->GetExperimental()->AddObserver(this);
devtools_client_->GetNetwork()->Enable();
devtools_client_->GetRuntime()->GetExperimental()->AddObserver(this);
devtools_client_->GetRuntime()->Enable();
// Enabling the runtime domain will send us the current context.
run_loop_ = std::make_unique<base::RunLoop>(
base::RunLoop::Type::kNestableTasksAllowed);
run_loop_->Run();
run_loop_ = nullptr;
EXPECT_EQ(1u, execution_context_frame_ids_.size());
execution_context_frame_ids_.clear();
if (EnableInterception()) {
std::unique_ptr<headless::network::RequestPattern> match_all =
headless::network::RequestPattern::Builder()
.SetUrlPattern("*")
.Build();
std::vector<std::unique_ptr<headless::network::RequestPattern>> patterns;
patterns.push_back(std::move(match_all));
devtools_client_->GetNetwork()->GetExperimental()->SetRequestInterception(
network::SetRequestInterceptionParams::Builder()
.SetPatterns(std::move(patterns))
.Build());
}
devtools_client_->GetPage()->AddObserver(this);
run_loop_ = std::make_unique<base::RunLoop>(
base::RunLoop::Type::kNestableTasksAllowed);
devtools_client_->GetPage()->Enable(run_loop_->QuitClosure());
run_loop_->Run();
run_loop_ = nullptr;
devtools_client_->GetPage()->Navigate("http://foo.com/index.html");
}
ProtocolHandlerMap GetProtocolHandlers() override {
ProtocolHandlerMap protocol_handlers;
std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
new TestInMemoryProtocolHandler(browser()->BrowserIOThread(),
/* request_deferrer */ nullptr));
http_handler_ = http_handler.get();
http_handler_->InsertResponse("http://foo.com/index.html",
{kIndexHtml, "text/html"});
http_handler_->InsertResponse("http://foo.com/iframe1.html",
{kIFrame1, "text/html"});
http_handler_->InsertResponse("http://foo.com/iframe2.html",
{kIFrame2, "text/html"});
http_handler_->InsertResponse("http://foo.com/style1.css",
{kStyle1Css, "text/css"});
http_handler_->InsertResponse("http://foo.com/style2.css",
{kStyle2Css, "text/css"});
http_handler_->InsertResponse("http://foo.com/style3.css",
{kStyle3Css, "text/css"});
protocol_handlers[url::kHttpScheme] = std::move(http_handler);
return protocol_handlers;
}
// network::Observer implementation:
void OnRequestWillBeSent(
const network::RequestWillBeSentParams& params) override {
url_to_frame_id_[params.GetRequest()->GetUrl()] = params.GetFrameId();
frame_ids_.insert(params.GetFrameId());
}
// page::Observer implementation:
void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
EXPECT_THAT(url_to_frame_id_,
ContainerEq(http_handler_->url_to_devtools_frame_id()));
EXPECT_THAT(execution_context_frame_ids_, ContainerEq(frame_ids_));
FinishAsynchronousTest();
}
virtual bool EnableInterception() const { return false; }
void OnExecutionContextCreated(
const runtime::ExecutionContextCreatedParams& params) override {
const base::Value* frameId =
params.GetContext()->GetAuxData()->FindKey("frameId");
if (frameId && frameId->is_string())
execution_context_frame_ids_.insert(frameId->GetString());
// If we're nested then exit.
if (run_loop_)
run_loop_->Quit();
}
private:
std::set<std::string> frame_ids_;
std::set<std::string> execution_context_frame_ids_;
std::map<std::string, std::string> url_to_frame_id_;
TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
std::unique_ptr<base::RunLoop> run_loop_;
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(FrameIdTest);
// Frame IDs should still be available with network request interception enabled
class FrameIdWithDevtoolsRequestInterceptionTest : public FrameIdTest {
public:
void OnRequestIntercepted(
const network::RequestInterceptedParams& params) override {
// Allow the request to continue.
devtools_client_->GetNetwork()
->GetExperimental()
->ContinueInterceptedRequest(
network::ContinueInterceptedRequestParams::Builder()
.SetInterceptionId(params.GetInterceptionId())
.Build());
}
bool EnableInterception() const override { return true; }
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(FrameIdWithDevtoolsRequestInterceptionTest);
} // namespace headless