// 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 #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"(
Main frame
)"; const char* kIFrame1 = R"(
IFrame 1
)"; const char* kIFrame2 = R"(
IFrame 1
)"; 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::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 match_all = headless::network::RequestPattern::Builder() .SetUrlPattern("*") .Build(); std::vector> 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::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 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 frame_ids_; std::set execution_context_frame_ids_; std::map url_to_frame_id_; TestInMemoryProtocolHandler* http_handler_; // NOT OWNED std::unique_ptr 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