Let headless embedders disable the ResourceScheduler
A C++ headless embedder may wish to implement their own resource scheduling. Bug: 761364 Change-Id: I82faf8505611e606dcf1df70f221cd87bc582b10 Reviewed-on: https://chromium-review.googlesource.com/645426 Reviewed-by: Jochen Eisinger <jochen@chromium.org> Reviewed-by: Randy Smith <rdsmith@chromium.org> Reviewed-by: Sami Kyöstilä <skyostil@chromium.org> Commit-Queue: Alex Clarke <alexclarke@chromium.org> Cr-Commit-Position: refs/heads/master@{#502221}
This commit is contained in:
content
browser
loader
public
headless
@ -737,7 +737,11 @@ std::unique_ptr<net::ClientCertStore>
|
||||
}
|
||||
|
||||
void ResourceDispatcherHostImpl::OnInit() {
|
||||
scheduler_.reset(new ResourceScheduler);
|
||||
// In some tests |delegate_| does not get set, when that happens assume the
|
||||
// scheduler is enabled.
|
||||
bool enable_resource_scheduler =
|
||||
delegate_ ? delegate_->ShouldUseResourceScheduler() : true;
|
||||
scheduler_.reset(new ResourceScheduler(enable_resource_scheduler));
|
||||
}
|
||||
|
||||
void ResourceDispatcherHostImpl::OnShutdown() {
|
||||
|
@ -780,6 +780,9 @@ class ResourceScheduler::Client {
|
||||
|
||||
ShouldStartReqResult ShouldStartRequest(
|
||||
ScheduledResourceRequest* request) const {
|
||||
if (!resource_scheduler_->enabled())
|
||||
return START_REQUEST;
|
||||
|
||||
const net::URLRequest& url_request = *request->url_request();
|
||||
// Syncronous requests could block the entire render, which could impact
|
||||
// user-observable Clients.
|
||||
@ -993,8 +996,9 @@ class ResourceScheduler::Client {
|
||||
base::WeakPtrFactory<ResourceScheduler::Client> weak_ptr_factory_;
|
||||
};
|
||||
|
||||
ResourceScheduler::ResourceScheduler()
|
||||
: priority_requests_delayable_(
|
||||
ResourceScheduler::ResourceScheduler(bool enabled)
|
||||
: enabled_(enabled),
|
||||
priority_requests_delayable_(
|
||||
base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)),
|
||||
head_priority_requests_delayable_(base::FeatureList::IsEnabled(
|
||||
kHeadPrioritySupportedRequestsDelayable)),
|
||||
|
@ -75,7 +75,7 @@ class CONTENT_EXPORT ResourceScheduler {
|
||||
};
|
||||
typedef std::vector<MaxRequestsForBDPRange> MaxRequestsForBDPRanges;
|
||||
|
||||
ResourceScheduler();
|
||||
explicit ResourceScheduler(bool enabled);
|
||||
~ResourceScheduler();
|
||||
|
||||
// Requests that this ResourceScheduler schedule, and eventually loads, the
|
||||
@ -167,6 +167,8 @@ class CONTENT_EXPORT ResourceScheduler {
|
||||
task_runner_ = std::move(sequenced_task_runner);
|
||||
}
|
||||
|
||||
bool enabled() const { return enabled_; }
|
||||
|
||||
private:
|
||||
class Client;
|
||||
class RequestQueue;
|
||||
@ -253,6 +255,11 @@ class CONTENT_EXPORT ResourceScheduler {
|
||||
ClientMap client_map_;
|
||||
RequestSet unowned_requests_;
|
||||
|
||||
// Whether or not to enable ResourceScheduling. This will almost always be
|
||||
// enabled, except for some C++ headless embedders who may implement their own
|
||||
// resource scheduling via protocol handlers.
|
||||
const bool enabled_;
|
||||
|
||||
// True if requests to servers that support priorities (e.g., H2/QUIC) can
|
||||
// be delayed.
|
||||
bool priority_requests_delayable_;
|
||||
|
@ -181,12 +181,12 @@ class ResourceSchedulerTest : public testing::Test {
|
||||
|
||||
// Done separately from construction to allow for modification of command
|
||||
// line flags in tests.
|
||||
void InitializeScheduler() {
|
||||
void InitializeScheduler(bool enabled = true) {
|
||||
CleanupScheduler();
|
||||
|
||||
// Destroys previous scheduler, also destroys any previously created
|
||||
// mock_timer_.
|
||||
scheduler_.reset(new ResourceScheduler());
|
||||
scheduler_.reset(new ResourceScheduler(enabled));
|
||||
|
||||
scheduler_->OnClientCreated(kChildId, kRouteId,
|
||||
&network_quality_estimator_);
|
||||
@ -1966,6 +1966,32 @@ TEST_F(ResourceSchedulerTest, NumDelayableAtStartOfNonDelayableUMA) {
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_F(ResourceSchedulerTest, SchedulerEnabled) {
|
||||
std::unique_ptr<TestRequest> high(
|
||||
NewRequest("http://host/high", net::HIGHEST));
|
||||
std::unique_ptr<TestRequest> low(NewRequest("http://host/req", net::LOWEST));
|
||||
|
||||
std::unique_ptr<TestRequest> request(
|
||||
NewRequest("http://host/req", net::LOWEST));
|
||||
|
||||
EXPECT_FALSE(request->started());
|
||||
}
|
||||
|
||||
TEST_F(ResourceSchedulerTest, SchedulerDisabled) {
|
||||
InitializeScheduler(false);
|
||||
|
||||
std::unique_ptr<TestRequest> high(
|
||||
NewRequest("http://host/high", net::HIGHEST));
|
||||
std::unique_ptr<TestRequest> low(NewRequest("http://host/req", net::LOWEST));
|
||||
|
||||
std::unique_ptr<TestRequest> request(
|
||||
NewRequest("http://host/req", net::LOWEST));
|
||||
|
||||
// Normally |request| wouldn't start immediately due to the |high| priority
|
||||
// request, but when the scheduler is disabled it starts immediately.
|
||||
EXPECT_TRUE(request->started());
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
} // namespace content
|
||||
|
@ -104,6 +104,10 @@ ResourceDispatcherHostDelegate::CreateClientCertStore(
|
||||
return std::unique_ptr<net::ClientCertStore>();
|
||||
}
|
||||
|
||||
bool ResourceDispatcherHostDelegate::ShouldUseResourceScheduler() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
ResourceDispatcherHostDelegate::~ResourceDispatcherHostDelegate() {
|
||||
}
|
||||
|
||||
|
@ -142,6 +142,11 @@ class CONTENT_EXPORT ResourceDispatcherHostDelegate {
|
||||
virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
|
||||
ResourceContext* resource_context);
|
||||
|
||||
// Whether or not to enable ResourceScheduling. This will almost always be
|
||||
// enabled, except for some C++ headless embedders who may implement their own
|
||||
// resource scheduling via protocol handlers.
|
||||
virtual bool ShouldUseResourceScheduler() const;
|
||||
|
||||
protected:
|
||||
virtual ~ResourceDispatcherHostDelegate();
|
||||
};
|
||||
|
@ -542,6 +542,10 @@ test("headless_unittests") {
|
||||
"public/util/error_reporter_unittest.cc",
|
||||
"public/util/expedited_dispatcher_test.cc",
|
||||
"public/util/generic_url_request_job_test.cc",
|
||||
"public/util/testing/generic_url_request_mocks.cc",
|
||||
"public/util/testing/generic_url_request_mocks.h",
|
||||
"public/util/testing/test_in_memory_protocol_handler.cc",
|
||||
"public/util/testing/test_in_memory_protocol_handler.h",
|
||||
"public/util/throttled_dispatcher_test.cc",
|
||||
"public/util/virtual_time_controller_test.cc",
|
||||
]
|
||||
@ -671,6 +675,12 @@ test("headless_browsertests") {
|
||||
"lib/headless_devtools_client_browsertest.cc",
|
||||
"lib/headless_web_contents_browsertest.cc",
|
||||
"lib/virtual_time_browsertest.cc",
|
||||
"public/util/testing/fake_managed_dispatch_url_request_job.cc",
|
||||
"public/util/testing/fake_managed_dispatch_url_request_job.h",
|
||||
"public/util/testing/generic_url_request_mocks.cc",
|
||||
"public/util/testing/generic_url_request_mocks.h",
|
||||
"public/util/testing/test_in_memory_protocol_handler.cc",
|
||||
"public/util/testing/test_in_memory_protocol_handler.h",
|
||||
"test/headless_browser_test.cc",
|
||||
"test/headless_browser_test.h",
|
||||
"test/headless_test_launcher.cc",
|
||||
|
@ -292,7 +292,8 @@ void HeadlessContentBrowserClient::AllowCertificateError(
|
||||
|
||||
void HeadlessContentBrowserClient::ResourceDispatcherHostCreated() {
|
||||
resource_dispatcher_host_delegate_.reset(
|
||||
new HeadlessResourceDispatcherHostDelegate);
|
||||
new HeadlessResourceDispatcherHostDelegate(
|
||||
browser_->options()->enable_resource_scheduler));
|
||||
content::ResourceDispatcherHost::Get()->SetDelegate(
|
||||
resource_dispatcher_host_delegate_.get());
|
||||
}
|
||||
|
@ -6,10 +6,16 @@
|
||||
|
||||
namespace headless {
|
||||
|
||||
HeadlessResourceDispatcherHostDelegate::
|
||||
HeadlessResourceDispatcherHostDelegate() {}
|
||||
HeadlessResourceDispatcherHostDelegate::HeadlessResourceDispatcherHostDelegate(
|
||||
bool enable_resource_scheduler)
|
||||
: enable_resource_scheduler_(enable_resource_scheduler) {}
|
||||
|
||||
HeadlessResourceDispatcherHostDelegate::
|
||||
~HeadlessResourceDispatcherHostDelegate() {}
|
||||
|
||||
bool HeadlessResourceDispatcherHostDelegate::ShouldUseResourceScheduler()
|
||||
const {
|
||||
return enable_resource_scheduler_;
|
||||
}
|
||||
|
||||
} // namespace headless
|
||||
|
@ -12,10 +12,15 @@ namespace headless {
|
||||
class HeadlessResourceDispatcherHostDelegate
|
||||
: public content::ResourceDispatcherHostDelegate {
|
||||
public:
|
||||
HeadlessResourceDispatcherHostDelegate();
|
||||
explicit HeadlessResourceDispatcherHostDelegate(
|
||||
bool enable_resource_scheduler);
|
||||
~HeadlessResourceDispatcherHostDelegate() override;
|
||||
|
||||
bool ShouldUseResourceScheduler() const override;
|
||||
|
||||
private:
|
||||
const bool enable_resource_scheduler_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(HeadlessResourceDispatcherHostDelegate);
|
||||
};
|
||||
|
||||
|
@ -12,9 +12,7 @@
|
||||
#include "headless/public/devtools/domains/network.h"
|
||||
#include "headless/public/devtools/domains/page.h"
|
||||
#include "headless/public/headless_devtools_client.h"
|
||||
#include "headless/public/util/expedited_dispatcher.h"
|
||||
#include "headless/public/util/generic_url_request_job.h"
|
||||
#include "headless/public/util/url_fetcher.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"
|
||||
@ -27,118 +25,6 @@ using testing::ContainerEq;
|
||||
namespace headless {
|
||||
|
||||
namespace {
|
||||
class TestProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
|
||||
public:
|
||||
explicit TestProtocolHandler(
|
||||
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
|
||||
: test_delegate_(new TestDelegate()),
|
||||
dispatcher_(new ExpeditedDispatcher(io_thread_task_runner)),
|
||||
headless_browser_context_(nullptr) {}
|
||||
|
||||
~TestProtocolHandler() override {}
|
||||
|
||||
void SetHeadlessBrowserContext(
|
||||
HeadlessBrowserContext* headless_browser_context) {
|
||||
headless_browser_context_ = headless_browser_context;
|
||||
}
|
||||
|
||||
struct Response {
|
||||
Response() {}
|
||||
Response(const std::string& body, const std::string& mime_type)
|
||||
: data("HTTP/1.1 200 OK\r\nContent-Type: " + mime_type + "\r\n\r\n" +
|
||||
body) {}
|
||||
|
||||
std::string data;
|
||||
};
|
||||
|
||||
void InsertResponse(const std::string& url, const Response& response) {
|
||||
response_map_[url] = response;
|
||||
}
|
||||
|
||||
const Response* GetResponse(const std::string& url) const {
|
||||
std::map<std::string, Response>::const_iterator find_it =
|
||||
response_map_.find(url);
|
||||
if (find_it == response_map_.end())
|
||||
return nullptr;
|
||||
return &find_it->second;
|
||||
}
|
||||
|
||||
class MockURLFetcher : public URLFetcher {
|
||||
public:
|
||||
explicit MockURLFetcher(TestProtocolHandler* protocol_handler)
|
||||
: protocol_handler_(protocol_handler) {}
|
||||
~MockURLFetcher() override {}
|
||||
|
||||
// URLFetcher implementation:
|
||||
void StartFetch(const Request* request,
|
||||
ResultListener* result_listener) override {
|
||||
GURL url = request->GetURLRequest()->url();
|
||||
EXPECT_EQ("GET", request->GetURLRequest()->method());
|
||||
|
||||
const Response* response = protocol_handler_->GetResponse(url.spec());
|
||||
if (!response)
|
||||
result_listener->OnFetchStartError(net::ERR_FILE_NOT_FOUND);
|
||||
|
||||
net::LoadTimingInfo load_timing_info;
|
||||
result_listener->OnFetchCompleteExtractHeaders(
|
||||
url, response->data.c_str(), response->data.size(), load_timing_info);
|
||||
|
||||
int frame_tree_node_id = request->GetFrameTreeNodeId();
|
||||
DCHECK_NE(frame_tree_node_id, -1) << " For url " << url;
|
||||
protocol_handler_->url_to_frame_tree_node_id_[url.spec()] =
|
||||
frame_tree_node_id;
|
||||
}
|
||||
|
||||
private:
|
||||
TestProtocolHandler* protocol_handler_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MockURLFetcher);
|
||||
};
|
||||
|
||||
class TestDelegate : public GenericURLRequestJob::Delegate {
|
||||
public:
|
||||
TestDelegate() {}
|
||||
~TestDelegate() override {}
|
||||
|
||||
// GenericURLRequestJob::Delegate implementation:
|
||||
void OnResourceLoadFailed(const Request* request,
|
||||
net::Error error) override {}
|
||||
|
||||
void OnResourceLoadComplete(
|
||||
const Request* request,
|
||||
const GURL& final_url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
const char* body,
|
||||
size_t body_size) override {}
|
||||
|
||||
private:
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
|
||||
};
|
||||
|
||||
// net::URLRequestJobFactory::ProtocolHandler implementation::
|
||||
net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const override {
|
||||
return new GenericURLRequestJob(
|
||||
request, network_delegate, dispatcher_.get(),
|
||||
base::MakeUnique<MockURLFetcher>(
|
||||
const_cast<TestProtocolHandler*>(this)),
|
||||
test_delegate_.get(), headless_browser_context_);
|
||||
}
|
||||
|
||||
std::map<std::string, int> url_to_frame_tree_node_id_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<TestDelegate> test_delegate_;
|
||||
std::unique_ptr<ExpeditedDispatcher> dispatcher_;
|
||||
std::map<std::string, Response> response_map_;
|
||||
HeadlessBrowserContext* headless_browser_context_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestProtocolHandler);
|
||||
};
|
||||
|
||||
const char* kIndexHtml = R"(
|
||||
<html>
|
||||
<head><link rel="stylesheet" type="text/css" href="style1.css"></head>
|
||||
@ -215,8 +101,9 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
|
||||
|
||||
ProtocolHandlerMap GetProtocolHandlers() override {
|
||||
ProtocolHandlerMap protocol_handlers;
|
||||
std::unique_ptr<TestProtocolHandler> http_handler(
|
||||
new TestProtocolHandler(browser()->BrowserIOThread()));
|
||||
std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
|
||||
new TestInMemoryProtocolHandler(browser()->BrowserIOThread(),
|
||||
/* simulate_slow_fetch */ false));
|
||||
http_handler_ = http_handler.get();
|
||||
http_handler_->InsertResponse("http://foo.com/index.html",
|
||||
{kIndexHtml, "text/html"});
|
||||
@ -243,7 +130,7 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
|
||||
// page::Observer implementation:
|
||||
void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
|
||||
std::map<std::string, std::string> protocol_handler_url_to_frame_id_;
|
||||
for (const auto& pair : http_handler_->url_to_frame_tree_node_id_) {
|
||||
for (const auto& pair : http_handler_->url_to_frame_tree_node_id()) {
|
||||
// TODO(alexclarke): This will probably break with OOPIF, fix this.
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=715924
|
||||
protocol_handler_url_to_frame_id_[pair.first] =
|
||||
@ -264,7 +151,7 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> url_to_frame_id_;
|
||||
TestProtocolHandler* http_handler_; // NOT OWNED
|
||||
TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
|
||||
};
|
||||
|
||||
HEADLESS_ASYNC_DEVTOOLED_TEST_F(FrameIdTest);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "headless/public/headless_browser.h"
|
||||
#include "headless/public/headless_devtools_client.h"
|
||||
#include "headless/public/headless_web_contents.h"
|
||||
#include "headless/public/util/testing/test_in_memory_protocol_handler.h"
|
||||
#include "headless/test/headless_browser_test.h"
|
||||
#include "headless/test/tab_socket_test.h"
|
||||
#include "printing/features/features.h"
|
||||
@ -42,7 +43,10 @@
|
||||
#endif
|
||||
|
||||
using testing::ElementsAre;
|
||||
using testing::ElementsAreArray;
|
||||
using testing::Not;
|
||||
using testing::UnorderedElementsAre;
|
||||
using testing::UnorderedElementsAreArray;
|
||||
|
||||
namespace headless {
|
||||
|
||||
@ -913,4 +917,113 @@ IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserOpenInTab) {
|
||||
EXPECT_EQ(2u, browser_context->GetAllWebContents().size());
|
||||
browser_context->RemoveObserver(&observer);
|
||||
}
|
||||
|
||||
namespace {
|
||||
const char* kRequestOrderTestPage = R"(
|
||||
<html>
|
||||
<body>
|
||||
<img src="img0">
|
||||
<img src="img1">
|
||||
<img src="img2">
|
||||
<img src="img3">
|
||||
<img src="img4">
|
||||
<img src="img5">
|
||||
<img src="img6">
|
||||
<script src='script7' async></script>
|
||||
<script>
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'xhr8');
|
||||
xhr.send();
|
||||
</script>
|
||||
<iframe src=frame9></iframe>
|
||||
<img src="img10">
|
||||
<img src="img11">
|
||||
</body>
|
||||
</html> )";
|
||||
|
||||
const char* kRequestOrderTestPageUrls[] = {
|
||||
"http://foo.com/index.html", "http://foo.com/img0",
|
||||
"http://foo.com/img1", "http://foo.com/img2",
|
||||
"http://foo.com/img3", "http://foo.com/img4",
|
||||
"http://foo.com/img5", "http://foo.com/img6",
|
||||
"http://foo.com/script7", "http://foo.com/img10",
|
||||
"http://foo.com/img11", "http://foo.com/xhr8",
|
||||
"http://foo.com/frame9"};
|
||||
} // namespace
|
||||
|
||||
class ResourceSchedulerTest : public HeadlessAsyncDevTooledBrowserTest,
|
||||
public page::Observer {
|
||||
public:
|
||||
void SetUp() override {
|
||||
options()->enable_resource_scheduler = GetEnableResourceScheduler();
|
||||
HeadlessBrowserTest::SetUp();
|
||||
}
|
||||
|
||||
void RunDevTooledTest() override {
|
||||
http_handler_->SetHeadlessBrowserContext(browser_context_);
|
||||
devtools_client_->GetPage()->AddObserver(this);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
|
||||
base::MessageLoop::ScopedNestableTaskAllower nest_loop(
|
||||
base::MessageLoop::current());
|
||||
run_loop.Run();
|
||||
|
||||
devtools_client_->GetPage()->Navigate("http://foo.com/index.html");
|
||||
}
|
||||
|
||||
virtual bool GetEnableResourceScheduler() = 0;
|
||||
|
||||
ProtocolHandlerMap GetProtocolHandlers() override {
|
||||
ProtocolHandlerMap protocol_handlers;
|
||||
std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
|
||||
new TestInMemoryProtocolHandler(browser()->BrowserIOThread(),
|
||||
/* simulate_slow_fetch */ true));
|
||||
http_handler_ = http_handler.get();
|
||||
http_handler_->InsertResponse("http://foo.com/index.html",
|
||||
{kRequestOrderTestPage, "text/html"});
|
||||
protocol_handlers[url::kHttpScheme] = std::move(http_handler);
|
||||
return protocol_handlers;
|
||||
}
|
||||
|
||||
const TestInMemoryProtocolHandler* http_handler() const {
|
||||
return http_handler_;
|
||||
}
|
||||
|
||||
private:
|
||||
TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
|
||||
};
|
||||
|
||||
class DisableResourceSchedulerTest : public ResourceSchedulerTest {
|
||||
public:
|
||||
bool GetEnableResourceScheduler() override { return false; }
|
||||
|
||||
void OnLoadEventFired(const page::LoadEventFiredParams&) override {
|
||||
EXPECT_THAT(http_handler()->urls_requested(),
|
||||
ElementsAreArray(kRequestOrderTestPageUrls));
|
||||
FinishAsynchronousTest();
|
||||
}
|
||||
};
|
||||
|
||||
HEADLESS_ASYNC_DEVTOOLED_TEST_F(DisableResourceSchedulerTest);
|
||||
|
||||
class EnableResourceSchedulerTest : public ResourceSchedulerTest {
|
||||
public:
|
||||
bool GetEnableResourceScheduler() override {
|
||||
return true; // The default value.
|
||||
}
|
||||
|
||||
void OnLoadEventFired(const page::LoadEventFiredParams&) override {
|
||||
// We expect a different resource order when the ResourceScheduler is used.
|
||||
EXPECT_THAT(http_handler()->urls_requested(),
|
||||
Not(ElementsAreArray(kRequestOrderTestPageUrls)));
|
||||
// However all the same urls should still be requested.
|
||||
EXPECT_THAT(http_handler()->urls_requested(),
|
||||
UnorderedElementsAreArray(kRequestOrderTestPageUrls));
|
||||
FinishAsynchronousTest();
|
||||
}
|
||||
};
|
||||
|
||||
HEADLESS_ASYNC_DEVTOOLED_TEST_F(EnableResourceSchedulerTest);
|
||||
|
||||
} // namespace headless
|
||||
|
@ -39,6 +39,7 @@ Options::Options(int argc, const char** argv)
|
||||
message_pump(nullptr),
|
||||
single_process_mode(false),
|
||||
disable_sandbox(false),
|
||||
enable_resource_scheduler(true),
|
||||
#if defined(USE_OZONE)
|
||||
// TODO(skyostil): Implement SwiftShader backend for headless ozone.
|
||||
gl_implementation("osmesa"),
|
||||
@ -125,6 +126,11 @@ Builder& Builder::SetDisableSandbox(bool disable_sandbox) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Builder& Builder::SetEnableResourceScheduler(bool enable_resource_scheduler) {
|
||||
options_.enable_resource_scheduler = enable_resource_scheduler;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Builder& Builder::SetGLImplementation(const std::string& gl_implementation) {
|
||||
options_.gl_implementation = gl_implementation;
|
||||
return *this;
|
||||
|
@ -137,6 +137,9 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options {
|
||||
// a security risk and should be used with caution.
|
||||
bool disable_sandbox;
|
||||
|
||||
// Whether or not to enable content::ResourceScheduler. Enabled by default.
|
||||
bool enable_resource_scheduler;
|
||||
|
||||
// Choose the GL implementation to use for rendering. A suitable
|
||||
// implementantion is selected by default. Setting this to an empty
|
||||
// string can be used to disable GL rendering (e.g., WebGL support).
|
||||
@ -205,6 +208,7 @@ class HEADLESS_EXPORT HeadlessBrowser::Options::Builder {
|
||||
Builder& SetMessagePump(base::MessagePump* message_pump);
|
||||
Builder& SetSingleProcessMode(bool single_process_mode);
|
||||
Builder& SetDisableSandbox(bool disable_sandbox);
|
||||
Builder& SetEnableResourceScheduler(bool enable_resource_scheduler);
|
||||
Builder& SetGLImplementation(const std::string& gl_implementation);
|
||||
Builder& AddMojoServiceName(const std::string& mojo_service_name);
|
||||
#if defined(OS_WIN)
|
||||
|
@ -18,11 +18,11 @@
|
||||
#include "headless/public/util/managed_dispatch_url_request_job.h"
|
||||
#include "headless/public/util/url_fetcher.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace net {
|
||||
class HttpResponseHeaders;
|
||||
class IOBuffer;
|
||||
} // namespace net
|
||||
|
||||
|
127
headless/public/util/testing/test_in_memory_protocol_handler.cc
Normal file
127
headless/public/util/testing/test_in_memory_protocol_handler.cc
Normal file
@ -0,0 +1,127 @@
|
||||
// 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 "headless/public/util/testing/test_in_memory_protocol_handler.h"
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "headless/public/util/expedited_dispatcher.h"
|
||||
#include "headless/public/util/generic_url_request_job.h"
|
||||
#include "headless/public/util/url_fetcher.h"
|
||||
|
||||
namespace headless {
|
||||
|
||||
class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher {
|
||||
public:
|
||||
MockURLFetcher(
|
||||
TestInMemoryProtocolHandler* protocol_handler,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
|
||||
: protocol_handler_(protocol_handler),
|
||||
io_thread_task_runner_(io_thread_task_runner) {}
|
||||
~MockURLFetcher() override {}
|
||||
|
||||
// URLFetcher implementation:
|
||||
void StartFetch(const Request* request,
|
||||
ResultListener* result_listener) override {
|
||||
GURL url = request->GetURLRequest()->url();
|
||||
DCHECK_EQ("GET", request->GetURLRequest()->method());
|
||||
|
||||
int frame_tree_node_id = request->GetFrameTreeNodeId();
|
||||
DCHECK_NE(frame_tree_node_id, -1) << " For url " << url;
|
||||
protocol_handler_->RegisterUrl(url.spec(), frame_tree_node_id);
|
||||
|
||||
if (protocol_handler_->simulate_slow_fetch()) {
|
||||
io_thread_task_runner_->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&MockURLFetcher::FinishFetch, base::Unretained(this),
|
||||
result_listener, url),
|
||||
base::TimeDelta::FromMilliseconds(100));
|
||||
} else {
|
||||
FinishFetch(result_listener, url);
|
||||
}
|
||||
}
|
||||
|
||||
void FinishFetch(ResultListener* result_listener, GURL url) {
|
||||
const TestInMemoryProtocolHandler::Response* response =
|
||||
protocol_handler_->GetResponse(url.spec());
|
||||
if (response) {
|
||||
net::LoadTimingInfo load_timing_info;
|
||||
result_listener->OnFetchCompleteExtractHeaders(
|
||||
url, response->data.c_str(), response->data.size(), load_timing_info);
|
||||
} else {
|
||||
result_listener->OnFetchStartError(net::ERR_FILE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TestInMemoryProtocolHandler* protocol_handler_; // NOT OWNED.
|
||||
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MockURLFetcher);
|
||||
};
|
||||
|
||||
class TestInMemoryProtocolHandler::TestDelegate
|
||||
: public GenericURLRequestJob::Delegate {
|
||||
public:
|
||||
TestDelegate() {}
|
||||
~TestDelegate() override {}
|
||||
|
||||
// GenericURLRequestJob::Delegate implementation:
|
||||
void OnResourceLoadFailed(const Request* request, net::Error error) override {
|
||||
}
|
||||
|
||||
void OnResourceLoadComplete(
|
||||
const Request* request,
|
||||
const GURL& final_url,
|
||||
scoped_refptr<net::HttpResponseHeaders> response_headers,
|
||||
const char* body,
|
||||
size_t body_size) override {}
|
||||
|
||||
private:
|
||||
scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
|
||||
};
|
||||
|
||||
TestInMemoryProtocolHandler::TestInMemoryProtocolHandler(
|
||||
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
|
||||
bool simulate_slow_fetch)
|
||||
: test_delegate_(new TestDelegate()),
|
||||
dispatcher_(new ExpeditedDispatcher(io_thread_task_runner)),
|
||||
headless_browser_context_(nullptr),
|
||||
simulate_slow_fetch_(simulate_slow_fetch),
|
||||
io_thread_task_runner_(io_thread_task_runner) {}
|
||||
|
||||
TestInMemoryProtocolHandler::~TestInMemoryProtocolHandler() {}
|
||||
|
||||
void TestInMemoryProtocolHandler::SetHeadlessBrowserContext(
|
||||
HeadlessBrowserContext* headless_browser_context) {
|
||||
headless_browser_context_ = headless_browser_context;
|
||||
}
|
||||
|
||||
void TestInMemoryProtocolHandler::InsertResponse(const std::string& url,
|
||||
const Response& response) {
|
||||
response_map_[url] = response;
|
||||
}
|
||||
|
||||
const TestInMemoryProtocolHandler::Response*
|
||||
TestInMemoryProtocolHandler::GetResponse(const std::string& url) const {
|
||||
std::map<std::string, Response>::const_iterator find_it =
|
||||
response_map_.find(url);
|
||||
if (find_it == response_map_.end())
|
||||
return nullptr;
|
||||
return &find_it->second;
|
||||
}
|
||||
|
||||
net::URLRequestJob* TestInMemoryProtocolHandler::MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
return new GenericURLRequestJob(
|
||||
request, network_delegate, dispatcher_.get(),
|
||||
base::MakeUnique<MockURLFetcher>(
|
||||
const_cast<TestInMemoryProtocolHandler*>(this),
|
||||
io_thread_task_runner_),
|
||||
test_delegate_.get(), headless_browser_context_);
|
||||
}
|
||||
|
||||
} // namespace headless
|
@ -0,0 +1,84 @@
|
||||
// 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.
|
||||
|
||||
#ifndef HEADLESS_PUBLIC_UTIL_TESTING_TEST_IN_MEMORY_PROTOCOL_HANDLER_H_
|
||||
#define HEADLESS_PUBLIC_UTIL_TESTING_TEST_IN_MEMORY_PROTOCOL_HANDLER_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace headless {
|
||||
class ExpeditedDispatcher;
|
||||
class HeadlessBrowserContext;
|
||||
|
||||
class TestInMemoryProtocolHandler
|
||||
: public net::URLRequestJobFactory::ProtocolHandler {
|
||||
public:
|
||||
TestInMemoryProtocolHandler(
|
||||
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
|
||||
bool simulate_slow_fetch);
|
||||
|
||||
~TestInMemoryProtocolHandler() override;
|
||||
|
||||
void SetHeadlessBrowserContext(
|
||||
HeadlessBrowserContext* headless_browser_context);
|
||||
|
||||
struct Response {
|
||||
Response() {}
|
||||
Response(const std::string& body, const std::string& mime_type)
|
||||
: data("HTTP/1.1 200 OK\r\nContent-Type: " + mime_type + "\r\n\r\n" +
|
||||
body) {}
|
||||
|
||||
std::string data;
|
||||
};
|
||||
|
||||
void InsertResponse(const std::string& url, const Response& response);
|
||||
|
||||
// net::URLRequestJobFactory::ProtocolHandler implementation::
|
||||
net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const override;
|
||||
|
||||
const std::map<std::string, int>& url_to_frame_tree_node_id() const {
|
||||
return url_to_frame_tree_node_id_;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& urls_requested() const {
|
||||
return urls_requested_;
|
||||
}
|
||||
|
||||
private:
|
||||
const Response* GetResponse(const std::string& url) const;
|
||||
|
||||
void RegisterUrl(const std::string& url, int frame_tree_node_id) {
|
||||
urls_requested_.push_back(url);
|
||||
url_to_frame_tree_node_id_[url] = frame_tree_node_id;
|
||||
}
|
||||
|
||||
bool simulate_slow_fetch() const { return simulate_slow_fetch_; }
|
||||
|
||||
class TestDelegate;
|
||||
class MockURLFetcher;
|
||||
friend class TestDelegate;
|
||||
friend class MockURLFetcher;
|
||||
|
||||
std::unique_ptr<TestDelegate> test_delegate_;
|
||||
std::unique_ptr<ExpeditedDispatcher> dispatcher_;
|
||||
std::map<std::string, Response> response_map_;
|
||||
HeadlessBrowserContext* headless_browser_context_;
|
||||
std::map<std::string, int> url_to_frame_tree_node_id_;
|
||||
std::vector<std::string> urls_requested_;
|
||||
bool simulate_slow_fetch_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestInMemoryProtocolHandler);
|
||||
};
|
||||
|
||||
} // namespace headless
|
||||
|
||||
#endif // HEADLESS_PUBLIC_UTIL_TESTING_TEST_IN_MEMORY_PROTOCOL_HANDLER_H_
|
Reference in New Issue
Block a user