diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-01-25 11:39:07 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2016-01-25 15:20:42 +0000 |
commit | 6c91641271e536ffaa88a1dff5127e42ee99a91e (patch) | |
tree | 703d9dd49602377ddc90cbf886aad37913f2496b /chromium/content/browser/loader | |
parent | b145b7fafd36f0c260d6a768c81fc14e32578099 (diff) | |
download | qtwebengine-chromium-6c91641271e536ffaa88a1dff5127e42ee99a91e.tar.gz |
BASELINE: Update Chromium to 49.0.2623.23
Also adds missing printing sources.
Change-Id: I3726b8f0c7d6751c9fc846096c571fadca7108cd
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/content/browser/loader')
61 files changed, 3434 insertions, 684 deletions
diff --git a/chromium/content/browser/loader/async_resource_handler.cc b/chromium/content/browser/loader/async_resource_handler.cc index 2eace36852e..df08aa79537 100644 --- a/chromium/content/browser/loader/async_resource_handler.cc +++ b/chromium/content/browser/loader/async_resource_handler.cc @@ -11,6 +11,7 @@ #include "base/containers/hash_tables.h" #include "base/debug/alias.h" #include "base/logging.h" +#include "base/macros.h" #include "base/memory/shared_memory.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" @@ -144,10 +145,10 @@ void AsyncResourceHandler::ReportUploadProgress() { if (progress.position() == last_upload_position_) return; // No progress made since last time. - const uint64 kHalfPercentIncrements = 200; + const uint64_t kHalfPercentIncrements = 200; const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000); - uint64 amt_since_last = progress.position() - last_upload_position_; + uint64_t amt_since_last = progress.position() - last_upload_position_; TimeDelta time_since_last = TimeTicks::Now() - last_upload_ticks_; bool is_finished = (progress.size() == progress.position()); @@ -318,6 +319,9 @@ bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { int size; if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size)) return false; + + // TODO(erikchen): Temporary debugging. http://crbug.com/527588. + CHECK_LE(size, kBufferSize); filter->Send(new ResourceMsg_SetDataBuffer( GetRequestID(), handle, size, filter->peer_pid())); sent_first_data_msg_ = true; @@ -329,6 +333,10 @@ bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { int encoded_data_length = current_transfer_size - reported_transfer_size_; reported_transfer_size_ = current_transfer_size; + // TODO(erikchen): Temporary debugging. http://crbug.com/527588. + CHECK_LE(data_offset, kBufferSize); + + filter->Send(new ResourceMsg_DataReceivedDebug(GetRequestID(), data_offset)); filter->Send(new ResourceMsg_DataReceived( GetRequestID(), data_offset, bytes_read, encoded_data_length)); ++pending_data_count_; diff --git a/chromium/content/browser/loader/async_resource_handler.h b/chromium/content/browser/loader/async_resource_handler.h index 9d460963d52..d7f70c2b504 100644 --- a/chromium/content/browser/loader/async_resource_handler.h +++ b/chromium/content/browser/loader/async_resource_handler.h @@ -5,8 +5,11 @@ #ifndef CONTENT_BROWSER_LOADER_ASYNC_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_ASYNC_RESOURCE_HANDLER_H_ +#include <stdint.h> + #include <string> +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/timer/timer.h" #include "content/browser/loader/resource_handler.h" @@ -78,7 +81,7 @@ class AsyncResourceHandler : public ResourceHandler, bool sent_received_response_msg_; bool sent_first_data_msg_; - uint64 last_upload_position_; + uint64_t last_upload_position_; bool waiting_for_upload_progress_ack_; base::TimeTicks last_upload_ticks_; base::RepeatingTimer progress_timer_; diff --git a/chromium/content/browser/loader/async_resource_handler_browsertest.cc b/chromium/content/browser/loader/async_resource_handler_browsertest.cc index cb48a231115..456c31a544f 100644 --- a/chromium/content/browser/loader/async_resource_handler_browsertest.cc +++ b/chromium/content/browser/loader/async_resource_handler_browsertest.cc @@ -4,11 +4,14 @@ #include "content/browser/loader/async_resource_handler.h" +#include <stddef.h> #include <string> +#include <utility> #include "base/format_macros.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "build/build_config.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" @@ -28,8 +31,10 @@ const char kRedirectPostPath[] = "/redirect"; // ThreadSanitizer is too slow to perform the full upload, so tests // using that build get an easier test which might not show two distinct -// progress events. See crbug.com/526985. -#if defined(THREAD_SANITIZER) +// progress events. See crbug.com/526985. In addition, OSX buildbots have +// experienced slowdowns on this test (crbug.com/548819), give them the easier +// test too. +#if defined(THREAD_SANITIZER) || defined(OS_MACOSX) const size_t kPayloadSize = 1062882; // 2*3^12 #else const size_t kPayloadSize = 28697814; // 2*3^15 @@ -44,14 +49,14 @@ scoped_ptr<net::test_server::HttpResponse> HandlePostAndRedirectURLs( base::CompareCase::SENSITIVE)) { http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); http_response->AddCustomHeader("Location", kPostPath); - EXPECT_EQ(request.content.length(), kPayloadSize);; - return http_response.Pass(); - } else if(base::StartsWith(request.relative_url, kPostPath, - base::CompareCase::SENSITIVE)) { + EXPECT_EQ(request.content.length(), kPayloadSize); + return std::move(http_response); + } else if (base::StartsWith(request.relative_url, kPostPath, + base::CompareCase::SENSITIVE)) { http_response->set_content("hello"); http_response->set_content_type("text/plain"); EXPECT_EQ(request.content.length(), kPayloadSize); - return http_response.Pass(); + return std::move(http_response); } else { return scoped_ptr<net::test_server::HttpResponse>(); } @@ -63,8 +68,8 @@ class AsyncResourceHandlerBrowserTest : public ContentBrowserTest { }; IN_PROC_BROWSER_TEST_F(AsyncResourceHandlerBrowserTest, UploadProgress) { - net::test_server::EmbeddedTestServer* test_server = embedded_test_server(); - ASSERT_TRUE(test_server->InitializeAndWaitUntilReady()); + net::EmbeddedTestServer* test_server = embedded_test_server(); + ASSERT_TRUE(test_server->Start()); test_server->RegisterRequestHandler( base::Bind(&HandlePostAndRedirectURLs, kPostPath)); @@ -83,8 +88,8 @@ IN_PROC_BROWSER_TEST_F(AsyncResourceHandlerBrowserTest, UploadProgress) { IN_PROC_BROWSER_TEST_F(AsyncResourceHandlerBrowserTest, UploadProgressRedirect) { - net::test_server::EmbeddedTestServer* test_server = embedded_test_server(); - ASSERT_TRUE(test_server->InitializeAndWaitUntilReady()); + net::EmbeddedTestServer* test_server = embedded_test_server(); + ASSERT_TRUE(test_server->Start()); test_server->RegisterRequestHandler( base::Bind(&HandlePostAndRedirectURLs, kRedirectPostPath)); diff --git a/chromium/content/browser/loader/async_revalidation_driver.cc b/chromium/content/browser/loader/async_revalidation_driver.cc new file mode 100644 index 00000000000..3c5d45bc13b --- /dev/null +++ b/chromium/content/browser/loader/async_revalidation_driver.cc @@ -0,0 +1,274 @@ +// Copyright 2015 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 "content/browser/loader/async_revalidation_driver.h" + +#include <utility> + +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/metrics/histogram_macros.h" +#include "base/metrics/sparse_histogram.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request_status.h" + +namespace content { + +namespace { +// This matches the maximum allocation size of AsyncResourceHandler. +const int kReadBufSize = 32 * 1024; + +// The time to wait for a response. Since this includes the time taken to +// connect, this has been set to match the connect timeout +// kTransportConnectJobTimeoutInSeconds. +const int kResponseTimeoutInSeconds = 240; // 4 minutes. + +// This value should not be too large, as this request may be tying up a socket +// that could be used for something better. However, if it is too small, the +// cache entry will be truncated for no good reason. +// TODO(ricea): Find a more scientific way to set this timeout. +const int kReadTimeoutInSeconds = 30; +} // namespace + +AsyncRevalidationDriver::AsyncRevalidationDriver( + scoped_ptr<net::URLRequest> request, + scoped_ptr<ResourceThrottle> throttle, + const base::Closure& completion_callback) + : request_(std::move(request)), + throttle_(std::move(throttle)), + completion_callback_(completion_callback), + weak_ptr_factory_(this) { + request_->set_delegate(this); + throttle_->set_controller(this); +} + +AsyncRevalidationDriver::~AsyncRevalidationDriver() {} + +void AsyncRevalidationDriver::StartRequest() { + // Give the handler a chance to delay the URLRequest from being started. + bool defer_start = false; + throttle_->WillStartRequest(&defer_start); + + if (defer_start) { + RecordDefer(); + } else { + StartRequestInternal(); + } +} + +void AsyncRevalidationDriver::OnReceivedRedirect( + net::URLRequest* request, + const net::RedirectInfo& redirect_info, + bool* defer) { + DCHECK_EQ(request_.get(), request); + + // The async revalidation should not follow redirects, because caching is + // a property of an individual HTTP resource. + DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec(); + CancelRequestInternal(net::ERR_ABORTED, RESULT_GOT_REDIRECT); +} + +void AsyncRevalidationDriver::OnAuthRequired( + net::URLRequest* request, + net::AuthChallengeInfo* auth_info) { + DCHECK_EQ(request_.get(), request); + // This error code doesn't have exactly the right semantics, but it should + // be sufficient to narrow down the problem in net logs. + CancelRequestInternal(net::ERR_ACCESS_DENIED, RESULT_AUTH_FAILED); +} + +void AsyncRevalidationDriver::OnBeforeNetworkStart(net::URLRequest* request, + bool* defer) { + DCHECK_EQ(request_.get(), request); + + // Verify that the ResourceScheduler does not defer here. + throttle_->WillStartUsingNetwork(defer); + DCHECK(!*defer); + + // Start the response timer. This use of base::Unretained() is guaranteed safe + // by the semantics of base::OneShotTimer. + timer_.Start(FROM_HERE, + base::TimeDelta::FromSeconds(kResponseTimeoutInSeconds), + base::Bind(&AsyncRevalidationDriver::OnTimeout, + base::Unretained(this), RESULT_RESPONSE_TIMEOUT)); +} + +void AsyncRevalidationDriver::OnResponseStarted(net::URLRequest* request) { + DCHECK_EQ(request_.get(), request); + + DVLOG(1) << "OnResponseStarted: " << request_->url().spec(); + + // We have the response. No need to wait any longer. + timer_.Stop(); + + if (!request_->status().is_success()) { + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.AsyncRevalidation.ResponseError", + -request_->status().ToNetError()); + ResponseCompleted(RESULT_NET_ERROR); + // |this| may be deleted after this point. + return; + } + + const net::HttpResponseInfo& response_info = request_->response_info(); + if (!response_info.response_time.is_null() && response_info.was_cached) { + // The cached entry was revalidated. No need to read it in. + ResponseCompleted(RESULT_REVALIDATED); + // |this| may be deleted after this point. + return; + } + + bool defer = false; + throttle_->WillProcessResponse(&defer); + DCHECK(!defer); + + // Set up the timer for reading the body. This use of base::Unretained() is + // guaranteed safe by the semantics of base::OneShotTimer. + timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kReadTimeoutInSeconds), + base::Bind(&AsyncRevalidationDriver::OnTimeout, + base::Unretained(this), RESULT_BODY_TIMEOUT)); + StartReading(false); // Read the first chunk. +} + +void AsyncRevalidationDriver::OnReadCompleted(net::URLRequest* request, + int bytes_read) { + // request_ could be NULL if a timeout happened while OnReadCompleted() was + // queued to run asynchronously. + if (!request_) + return; + DCHECK_EQ(request_.get(), request); + DCHECK(!is_deferred_); + DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\"" + << " bytes_read = " << bytes_read; + + // bytes_read == 0 is EOF. + if (bytes_read == 0) { + ResponseCompleted(RESULT_LOADED); + return; + } + // bytes_read == -1 is an error. + if (bytes_read == -1 || !request_->status().is_success()) { + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.AsyncRevalidation.ReadError", + -request_->status().ToNetError()); + ResponseCompleted(RESULT_READ_ERROR); + // |this| may be deleted after this point. + return; + } + + DCHECK_GT(bytes_read, 0); + StartReading(true); // Read the next chunk. +} + +void AsyncRevalidationDriver::Resume() { + DCHECK(is_deferred_); + DCHECK(request_); + is_deferred_ = false; + request_->LogUnblocked(); + StartRequestInternal(); +} + +void AsyncRevalidationDriver::Cancel() { + NOTREACHED(); +} + +void AsyncRevalidationDriver::CancelAndIgnore() { + NOTREACHED(); +} + +void AsyncRevalidationDriver::CancelWithError(int error_code) { + NOTREACHED(); +} + +void AsyncRevalidationDriver::StartRequestInternal() { + DCHECK(request_); + DCHECK(!request_->is_pending()); + + request_->Start(); +} + +void AsyncRevalidationDriver::CancelRequestInternal( + int error, + AsyncRevalidationResult result) { + DVLOG(1) << "CancelRequestInternal: " << request_->url().spec(); + + // Set the error code since this will be reported to the NetworkDelegate and + // recorded in the NetLog. + request_->CancelWithError(error); + + // The ResourceScheduler needs to be able to examine the request when the + // ResourceThrottle is destroyed, so delete it first. + throttle_.reset(); + + // Destroy the request so that it doesn't try to send an asynchronous + // notification of completion. + request_.reset(); + + // Cancel timer to prevent OnTimeout() being called. + timer_.Stop(); + + ResponseCompleted(result); + // |this| may deleted after this point. +} + +void AsyncRevalidationDriver::StartReading(bool is_continuation) { + int bytes_read = 0; + ReadMore(&bytes_read); + + // If IO is pending, wait for the URLRequest to call OnReadCompleted. + if (request_->status().is_io_pending()) + return; + + if (!is_continuation || bytes_read <= 0) { + OnReadCompleted(request_.get(), bytes_read); + } else { + // Else, trigger OnReadCompleted asynchronously to avoid starving the IO + // thread in case the URLRequest can provide data synchronously. + scoped_refptr<base::SingleThreadTaskRunner> single_thread_task_runner = + base::ThreadTaskRunnerHandle::Get(); + single_thread_task_runner->PostTask( + FROM_HERE, + base::Bind(&AsyncRevalidationDriver::OnReadCompleted, + weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read)); + } +} + +void AsyncRevalidationDriver::ReadMore(int* bytes_read) { + DCHECK(!is_deferred_); + + if (!read_buffer_) + read_buffer_ = new net::IOBuffer(kReadBufSize); + + timer_.Reset(); + request_->Read(read_buffer_.get(), kReadBufSize, bytes_read); + + // No need to check the return value here as we'll detect errors by + // inspecting the URLRequest's status. +} + +void AsyncRevalidationDriver::ResponseCompleted( + AsyncRevalidationResult result) { + DVLOG(1) << "ResponseCompleted: " + << (request_ ? request_->url().spec() : "(request deleted)") + << "result = " << result; + UMA_HISTOGRAM_ENUMERATION("Net.AsyncRevalidation.Result", result, RESULT_MAX); + DCHECK(!completion_callback_.is_null()); + base::ResetAndReturn(&completion_callback_).Run(); + // |this| may be deleted after this point. +} + +void AsyncRevalidationDriver::OnTimeout(AsyncRevalidationResult result) { + CancelRequestInternal(net::ERR_TIMED_OUT, result); +} + +void AsyncRevalidationDriver::RecordDefer() { + request_->LogBlockedBy(throttle_->GetNameForLogging()); + DCHECK(!is_deferred_); + is_deferred_ = true; +} + +} // namespace content diff --git a/chromium/content/browser/loader/async_revalidation_driver.h b/chromium/content/browser/loader/async_revalidation_driver.h new file mode 100644 index 00000000000..b96e06ebd5c --- /dev/null +++ b/chromium/content/browser/loader/async_revalidation_driver.h @@ -0,0 +1,104 @@ +// Copyright 2015 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 CONTENT_BROWSER_LOADER_ASYNC_REVALIDATION_DRIVER_H_ +#define CONTENT_BROWSER_LOADER_ASYNC_REVALIDATION_DRIVER_H_ + +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/timer/timer.h" +#include "content/common/content_export.h" +#include "content/public/browser/resource_controller.h" +#include "content/public/browser/resource_throttle.h" +#include "net/base/io_buffer.h" +#include "net/url_request/url_request.h" + +namespace net { +class HttpCache; +} + +namespace content { + +// This class is responsible for driving the URLRequest for an async +// revalidation. It is passed an instance of ResourceThrottle created by +// content::ResourceScheduler to perform throttling on the request. +class CONTENT_EXPORT AsyncRevalidationDriver : public net::URLRequest::Delegate, + public ResourceController { + public: + // |completion_callback| is guaranteed to be called on completion, + // regardless of success or failure. + AsyncRevalidationDriver(scoped_ptr<net::URLRequest> request, + scoped_ptr<ResourceThrottle> throttle, + const base::Closure& completion_callback); + ~AsyncRevalidationDriver() override; + + void StartRequest(); + + private: + // This enum is logged as histogram "Net.AsyncRevalidation.Result". Only add + // new entries at the end and update histograms.xml to match. + enum AsyncRevalidationResult { + RESULT_LOADED, + RESULT_REVALIDATED, + RESULT_NET_ERROR, + RESULT_READ_ERROR, + RESULT_GOT_REDIRECT, + RESULT_AUTH_FAILED, + RESULT_RESPONSE_TIMEOUT, + RESULT_BODY_TIMEOUT, + RESULT_MAX + }; + + // net::URLRequest::Delegate implementation: + void OnReceivedRedirect(net::URLRequest* request, + const net::RedirectInfo& redirect_info, + bool* defer) override; + void OnAuthRequired(net::URLRequest* request, + net::AuthChallengeInfo* info) override; + void OnBeforeNetworkStart(net::URLRequest* request, bool* defer) override; + void OnResponseStarted(net::URLRequest* request) override; + void OnReadCompleted(net::URLRequest* request, int bytes_read) override; + + // ResourceController implementation: + void Resume() override; + + // For simplicity, this class assumes that ResourceScheduler never cancels + // requests, and so these three methods are never called. + void Cancel() override; + void CancelAndIgnore() override; + void CancelWithError(int error_code) override; + + // Internal methods. + void StartRequestInternal(); + void CancelRequestInternal(int error, AsyncRevalidationResult result); + void StartReading(bool is_continuation); + void ReadMore(int* bytes_read); + // Logs the result histogram, then calls and clears |completion_callback_|. + void ResponseCompleted(AsyncRevalidationResult result); + void OnTimeout(AsyncRevalidationResult result); + void RecordDefer(); + + bool is_deferred_ = false; + + scoped_refptr<net::IOBuffer> read_buffer_; + base::OneShotTimer timer_; + + scoped_ptr<net::URLRequest> request_; + scoped_ptr<ResourceThrottle> throttle_; + + base::Closure completion_callback_; + + base::WeakPtrFactory<AsyncRevalidationDriver> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(AsyncRevalidationDriver); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_LOADER_ASYNC_REVALIDATION_DRIVER_H_ diff --git a/chromium/content/browser/loader/async_revalidation_driver_unittest.cc b/chromium/content/browser/loader/async_revalidation_driver_unittest.cc new file mode 100644 index 00000000000..1f5a7e4b1f0 --- /dev/null +++ b/chromium/content/browser/loader/async_revalidation_driver_unittest.cc @@ -0,0 +1,397 @@ +// Copyright 2015 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 "content/browser/loader/async_revalidation_driver.h" + +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "content/public/browser/client_certificate_delegate.h" +#include "content/public/common/content_client.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/test_content_browser_client.h" +#include "ipc/ipc_message.h" +#include "net/base/net_errors.h" +#include "net/base/network_delegate_impl.h" +#include "net/base/request_priority.h" +#include "net/ssl/ssl_cert_request_info.h" +#include "net/url_request/url_request_job_factory.h" +#include "net/url_request/url_request_job_factory_impl.h" +#include "net/url_request/url_request_status.h" +#include "net/url_request/url_request_test_job.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { +namespace { + +// Dummy implementation of ResourceThrottle, an instance of which is needed to +// initialize AsyncRevalidationDriver. +class ResourceThrottleStub : public ResourceThrottle { + public: + ResourceThrottleStub() {} + + // If true, defers the request in WillStartRequest. + void set_defer_request_on_will_start_request( + bool defer_request_on_will_start_request) { + defer_request_on_will_start_request_ = defer_request_on_will_start_request; + } + + // ResourceThrottler implementation: + void WillStartRequest(bool* defer) override { + *defer = defer_request_on_will_start_request_; + } + + const char* GetNameForLogging() const override { + return "ResourceThrottleStub"; + } + + private: + bool defer_request_on_will_start_request_ = false; + + DISALLOW_COPY_AND_ASSIGN(ResourceThrottleStub); +}; + +// There are multiple layers of boilerplate needed to use a URLRequestTestJob +// subclass. Subclasses of AsyncRevalidationDriverTest can use +// BindCreateProtocolHandlerCallback() to bypass most of that boilerplate. +using CreateProtocolHandlerCallback = + base::Callback<scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>()>; + +template <typename T> +CreateProtocolHandlerCallback BindCreateProtocolHandlerCallback() { + static_assert(std::is_base_of<net::URLRequestJob, T>::value, + "Template argument to BindCreateProtocolHandlerCallback() must " + "be a subclass of URLRequestJob."); + + class TemplatedProtocolHandler + : public net::URLRequestJobFactory::ProtocolHandler { + public: + static scoped_ptr<net::URLRequestJobFactory::ProtocolHandler> Create() { + return make_scoped_ptr(new TemplatedProtocolHandler()); + } + + // URLRequestJobFactory::ProtocolHandler implementation: + net::URLRequestJob* MaybeCreateJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override { + return new T(request, network_delegate); + } + }; + + return base::Bind(&TemplatedProtocolHandler::Create); +} + +// An implementation of NetworkDelegate that captures the status of the last +// URLRequest to be destroyed. +class StatusCapturingNetworkDelegate : public net::NetworkDelegateImpl { + public: + const net::URLRequestStatus& last_status() { return last_status_; } + + private: + // net::NetworkDelegate implementation. + void OnURLRequestDestroyed(net::URLRequest* request) override { + last_status_ = request->status(); + } + + net::URLRequestStatus last_status_; +}; + +class AsyncRevalidationDriverTest : public testing::Test { + protected: + // Constructor for test fixtures that subclass this one. + AsyncRevalidationDriverTest( + const CreateProtocolHandlerCallback& create_protocol_handler_callback) + : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), + create_protocol_handler_callback_(create_protocol_handler_callback), + raw_ptr_resource_throttle_(nullptr), + raw_ptr_request_(nullptr) { + test_url_request_context_.set_job_factory(&job_factory_); + test_url_request_context_.set_network_delegate(&network_delegate_); + } + + // Constructor for tests that use this fixture directly. + AsyncRevalidationDriverTest() + : AsyncRevalidationDriverTest( + base::Bind(net::URLRequestTestJob::CreateProtocolHandler)) {} + + bool async_revalidation_complete_called() const { + return async_revalidation_complete_called_; + } + + const net::URLRequestStatus& last_status() { + return network_delegate_.last_status(); + } + + void SetUpAsyncRevalidationDriverWithRequestToUrl(const GURL& url) { + scoped_ptr<net::URLRequest> request(test_url_request_context_.CreateRequest( + url, net::DEFAULT_PRIORITY, nullptr /* delegate */)); + raw_ptr_request_ = request.get(); + raw_ptr_resource_throttle_ = new ResourceThrottleStub(); + // This use of base::Unretained() is safe because |driver_|, and the closure + // passed to it, will be destroyed before this object is. + driver_.reset(new AsyncRevalidationDriver( + std::move(request), make_scoped_ptr(raw_ptr_resource_throttle_), + base::Bind(&AsyncRevalidationDriverTest::OnAsyncRevalidationComplete, + base::Unretained(this)))); + } + + void SetUpAsyncRevalidationDriverWithDefaultRequest() { + SetUpAsyncRevalidationDriverWithRequestToUrl( + net::URLRequestTestJob::test_url_1()); + } + + void SetUp() override { + job_factory_.SetProtocolHandler("test", + create_protocol_handler_callback_.Run()); + SetUpAsyncRevalidationDriverWithDefaultRequest(); + } + + void OnAsyncRevalidationComplete() { + EXPECT_FALSE(async_revalidation_complete_called_); + async_revalidation_complete_called_ = true; + driver_.reset(); + } + + TestBrowserThreadBundle thread_bundle_; + net::URLRequestJobFactoryImpl job_factory_; + net::TestURLRequestContext test_url_request_context_; + StatusCapturingNetworkDelegate network_delegate_; + CreateProtocolHandlerCallback create_protocol_handler_callback_; + + // The AsyncRevalidationDriver owns the URLRequest and the ResourceThrottle. + ResourceThrottleStub* raw_ptr_resource_throttle_; + net::URLRequest* raw_ptr_request_; + scoped_ptr<AsyncRevalidationDriver> driver_; + bool async_revalidation_complete_called_ = false; +}; + +TEST_F(AsyncRevalidationDriverTest, NormalRequestCompletes) { + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// Verifies that request that should be deferred at start is deferred. +TEST_F(AsyncRevalidationDriverTest, DeferOnStart) { + raw_ptr_resource_throttle_->set_defer_request_on_will_start_request(true); + + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(raw_ptr_request_->is_pending()); + EXPECT_FALSE(async_revalidation_complete_called()); +} + +// Verifies that resuming a deferred request works. Assumes that DeferOnStart +// passes. +TEST_F(AsyncRevalidationDriverTest, ResumeDeferredRequestWorks) { + raw_ptr_resource_throttle_->set_defer_request_on_will_start_request(true); + + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + + ResourceController* driver_as_resource_controller = driver_.get(); + driver_as_resource_controller->Resume(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// Verifies that redirects are not followed. +TEST_F(AsyncRevalidationDriverTest, RedirectsAreNotFollowed) { + SetUpAsyncRevalidationDriverWithRequestToUrl( + net::URLRequestTestJob::test_url_redirect_to_url_2()); + + driver_->StartRequest(); + while (net::URLRequestTestJob::ProcessOnePendingMessage()) + base::RunLoop().RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(last_status().is_success()); + EXPECT_EQ(net::ERR_ABORTED, last_status().error()); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// A mock URLRequestJob which simulates an SSL client auth request. +class MockClientCertURLRequestJob : public net::URLRequestTestJob { + public: + MockClientCertURLRequestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate) + : net::URLRequestTestJob(request, network_delegate, true), + weak_factory_(this) {} + + // net::URLRequestTestJob implementation: + void Start() override { + scoped_refptr<net::SSLCertRequestInfo> cert_request_info( + new net::SSLCertRequestInfo); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&MockClientCertURLRequestJob::NotifyCertificateRequested, + weak_factory_.GetWeakPtr(), cert_request_info)); + } + + void ContinueWithCertificate( + net::X509Certificate* cert, + net::SSLPrivateKey* client_private_key) override { + ADD_FAILURE() << "Certificate supplied."; + } + + void Kill() override { + weak_factory_.InvalidateWeakPtrs(); + URLRequestJob::Kill(); + } + + private: + base::WeakPtrFactory<MockClientCertURLRequestJob> weak_factory_; +}; + +class AsyncRevalidationDriverClientCertTest + : public AsyncRevalidationDriverTest { + protected: + AsyncRevalidationDriverClientCertTest() + : AsyncRevalidationDriverTest( + BindCreateProtocolHandlerCallback<MockClientCertURLRequestJob>()) {} +}; + +// Test browser client that causes the test to fail if SelectClientCertificate() +// is called. Automatically sets itself as the browser client when constructed +// and restores the old browser client in the destructor. +class ScopedDontSelectCertificateBrowserClient + : public TestContentBrowserClient { + public: + ScopedDontSelectCertificateBrowserClient() { + old_client_ = SetBrowserClientForTesting(this); + } + + ~ScopedDontSelectCertificateBrowserClient() override { + SetBrowserClientForTesting(old_client_); + } + + void SelectClientCertificate( + WebContents* web_contents, + net::SSLCertRequestInfo* cert_request_info, + scoped_ptr<ClientCertificateDelegate> delegate) override { + ADD_FAILURE() << "SelectClientCertificate was called."; + } + + private: + ContentBrowserClient* old_client_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDontSelectCertificateBrowserClient); +}; + +// Verifies that async revalidation requests do not attempt to provide client +// certificates. +TEST_F(AsyncRevalidationDriverClientCertTest, RequestRejected) { + // Ensure that SelectClientCertificate is not called during this test. + ScopedDontSelectCertificateBrowserClient test_client; + + // Start the request and wait for it to pause. + driver_->StartRequest(); + + // Because TestBrowserThreadBundle only uses one real thread, this is + // sufficient to ensure that tasks posted to the "UI thread" have run. + base::RunLoop().RunUntilIdle(); + + // Check that the request aborted. + EXPECT_FALSE(last_status().is_success()); + EXPECT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, last_status().error()); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// A mock URLRequestJob which simulates an SSL certificate error. +class MockSSLErrorURLRequestJob : public net::URLRequestTestJob { + public: + MockSSLErrorURLRequestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate) + : net::URLRequestTestJob(request, network_delegate, true), + weak_factory_(this) {} + + // net::URLRequestTestJob implementation: + void Start() override { + // This SSLInfo isn't really valid, but it is good enough for testing. + net::SSLInfo ssl_info; + ssl_info.SetCertError(net::ERR_CERT_DATE_INVALID); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&MockSSLErrorURLRequestJob::NotifySSLCertificateError, + weak_factory_.GetWeakPtr(), ssl_info, false)); + } + + void ContinueDespiteLastError() override { + ADD_FAILURE() << "ContinueDespiteLastError called."; + } + + private: + base::WeakPtrFactory<MockSSLErrorURLRequestJob> weak_factory_; +}; + +class AsyncRevalidationDriverSSLErrorTest : public AsyncRevalidationDriverTest { + protected: + AsyncRevalidationDriverSSLErrorTest() + : AsyncRevalidationDriverTest( + BindCreateProtocolHandlerCallback<MockSSLErrorURLRequestJob>()) {} +}; + +// Verifies that async revalidation requests do not attempt to recover from SSL +// certificate errors. +TEST_F(AsyncRevalidationDriverSSLErrorTest, RequestWithSSLErrorRejected) { + // Start the request and wait for it to pause. + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + + // Check that the request has been aborted. + EXPECT_FALSE(last_status().is_success()); + EXPECT_EQ(net::ERR_ABORTED, last_status().error()); + EXPECT_TRUE(async_revalidation_complete_called()); +} + +// A URLRequestTestJob that sets |request_time| and |was_cached| on their +// response_info, and causes the test to fail if Read() is called. +class FromCacheURLRequestJob : public net::URLRequestTestJob { + public: + FromCacheURLRequestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate) + : net::URLRequestTestJob(request, network_delegate, true) {} + + void GetResponseInfo(net::HttpResponseInfo* info) override { + URLRequestTestJob::GetResponseInfo(info); + info->request_time = base::Time::Now(); + info->was_cached = true; + } + + int ReadRawData(net::IOBuffer* buf, int buf_size) override { + ADD_FAILURE() << "ReadRawData() was called."; + return URLRequestTestJob::ReadRawData(buf, buf_size); + } + + private: + ~FromCacheURLRequestJob() override {} + + DISALLOW_COPY_AND_ASSIGN(FromCacheURLRequestJob); +}; + +class AsyncRevalidationDriverFromCacheTest + : public AsyncRevalidationDriverTest { + protected: + AsyncRevalidationDriverFromCacheTest() + : AsyncRevalidationDriverTest( + BindCreateProtocolHandlerCallback<FromCacheURLRequestJob>()) {} +}; + +TEST_F(AsyncRevalidationDriverFromCacheTest, + CacheNotReadOnSuccessfulRevalidation) { + driver_->StartRequest(); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(async_revalidation_complete_called()); +} + +} // namespace +} // namespace content diff --git a/chromium/content/browser/loader/async_revalidation_manager.cc b/chromium/content/browser/loader/async_revalidation_manager.cc new file mode 100644 index 00000000000..aaaeb68ab83 --- /dev/null +++ b/chromium/content/browser/loader/async_revalidation_manager.cc @@ -0,0 +1,191 @@ +// Copyright 2015 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 "content/browser/loader/async_revalidation_manager.h" + +#include <tuple> +#include <utility> + +#include "base/logging.h" +#include "content/browser/loader/async_revalidation_driver.h" +#include "content/browser/loader/resource_message_filter.h" +#include "content/browser/loader/resource_request_info_impl.h" +#include "content/browser/loader/resource_scheduler.h" +#include "content/common/resource_messages.h" +#include "content/public/browser/resource_throttle.h" +#include "net/base/load_flags.h" +#include "net/http/http_transaction_factory.h" +#include "net/http/http_util.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "url/gurl.h" + +namespace content { + +AsyncRevalidationManager::AsyncRevalidationKey::AsyncRevalidationKey( + const ResourceContext* resource_context, + const net::HttpCache* http_cache, + const GURL& url) + : resource_context(resource_context), + http_cache(http_cache), + url_key(net::HttpUtil::SpecForRequest(url)) {} + +AsyncRevalidationManager::AsyncRevalidationKey::AsyncRevalidationKey( + const ResourceContext* resource_context) + : resource_context(resource_context), http_cache(nullptr), url_key() {} + +AsyncRevalidationManager::AsyncRevalidationKey::~AsyncRevalidationKey() {} + +bool AsyncRevalidationManager::AsyncRevalidationKey::LessThan::operator()( + const AsyncRevalidationKey& lhs, + const AsyncRevalidationKey& rhs) const { + return std::tie(lhs.resource_context, lhs.http_cache, lhs.url_key) < + std::tie(rhs.resource_context, rhs.http_cache, rhs.url_key); +} + +AsyncRevalidationManager::AsyncRevalidationManager() {} + +AsyncRevalidationManager::~AsyncRevalidationManager() { + DCHECK(in_progress_.empty()); +} + +void AsyncRevalidationManager::BeginAsyncRevalidation( + const net::URLRequest& for_request, + ResourceScheduler* scheduler) { + DCHECK_EQ(for_request.url_chain().size(), 1u); + const ResourceRequestInfoImpl* info = + ResourceRequestInfoImpl::ForRequest(&for_request); + DCHECK(info); + if (!info->filter()) { + // The child has gone away and we can no longer get ResourceContext and + // URLRequestContext to perform async revalidation. + // This can happen in the following cases, ordered from bad to not-so-bad + // + // 1. PlzNavigate (but enabling PlzNavigate automatically disables + // stale-while-revalidate; see crbug.com/561609) + // 2. <link rel=prefetch> may read a stale cache entry without a + // revalidation being performed if the original renderer goes away. This + // is a lost optimisation opportunity. + // + // Not an issue: + // 1. Implicit downloads. This method is called before + // MimeTypeResourceHandler calls set_is_download, so the renderer process + // must still exist for the request not to have been canceled. + // 2. Explicit downloads (ie. started via "Save As"). These never use + // stale-while-revalidate. + // 3. Non-PlzNavigate navigations between renderers. The old renderer + // still exists when this method is called. + // 4. <a ping>, navigation.sendBeacon() and Content-Security-Policy reports + // are POST requests, so they never use stale-while-revalidate. + // + // TODO(ricea): Resolve these lifetime issues. crbug.com/561591 + return; + } + + ResourceContext* resource_context = nullptr; + net::URLRequestContext* request_context = nullptr; + + // The embedder of //content needs to ensure that the URLRequestContext object + // remains valid until after the ResourceContext object is destroyed. + info->filter()->GetContexts(info->GetResourceType(), info->GetOriginPID(), + &resource_context, &request_context); + + AsyncRevalidationKey async_revalidation_key( + resource_context, request_context->http_transaction_factory()->GetCache(), + for_request.url()); + std::pair<AsyncRevalidationMap::iterator, bool> insert_result = + in_progress_.insert(AsyncRevalidationMap::value_type( + async_revalidation_key, scoped_ptr<AsyncRevalidationDriver>())); + if (!insert_result.second) { + // A matching async revalidation is already in progress for this cache; we + // don't need another one. + return; + } + + net::HttpRequestHeaders headers; + headers.AddHeadersFromString(info->original_headers()); + + // Construct the request. + scoped_ptr<net::URLRequest> new_request = request_context->CreateRequest( + for_request.url(), net::MINIMUM_PRIORITY, nullptr); + + new_request->set_method(for_request.method()); + new_request->set_first_party_for_cookies( + for_request.first_party_for_cookies()); + new_request->set_initiator(for_request.initiator()); + new_request->set_first_party_url_policy(for_request.first_party_url_policy()); + + new_request->SetReferrer(for_request.referrer()); + new_request->set_referrer_policy(for_request.referrer_policy()); + + new_request->SetExtraRequestHeaders(headers); + + // Remove LOAD_SUPPORT_ASYNC_REVALIDATION and LOAD_MAIN_FRAME flags. + // Also remove things which shouldn't have been there to begin with, + // and unrecognised flags. + int load_flags = + for_request.load_flags() & + (net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_BYPASS_PROXY | + net::LOAD_VERIFY_EV_CERT | net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SEND_AUTH_DATA | net::LOAD_MAYBE_USER_GESTURE | + net::LOAD_DO_NOT_USE_EMBEDDED_IDENTITY); + new_request->SetLoadFlags(load_flags); + + // These values would be -1 if the request was created by PlzNavigate. This + // would cause the async revalidation to start immediately. + // stale-while-revalidate is disabled when PlzNavigate is enabled + // to prevent this and other issues. See crbug.com/561610. + int child_id = info->GetChildID(); + int route_id = info->GetRouteID(); + + scoped_ptr<ResourceThrottle> throttle = + scheduler->ScheduleRequest(child_id, route_id, false, new_request.get()); + + // AsyncRevalidationDriver does not outlive its entry in |in_progress_|, + // so the iterator and |this| may be passed to base::Bind directly. + insert_result.first->second.reset(new AsyncRevalidationDriver( + std::move(new_request), std::move(throttle), + base::Bind(&AsyncRevalidationManager::OnAsyncRevalidationComplete, + base::Unretained(this), insert_result.first))); + insert_result.first->second->StartRequest(); +} + +void AsyncRevalidationManager::CancelAsyncRevalidationsForResourceContext( + ResourceContext* resource_context) { + // |resource_context| is the first part of the key, so elements to be + // cancelled are contiguous in the map. + AsyncRevalidationKey partial_key(resource_context); + for (auto it = in_progress_.lower_bound(partial_key); + it != in_progress_.end() && + it->first.resource_context == resource_context;) { + it = in_progress_.erase(it); + } +} + +bool AsyncRevalidationManager::QualifiesForAsyncRevalidation( + const ResourceHostMsg_Request& request) { + if (request.load_flags & + (net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | + net::LOAD_VALIDATE_CACHE | net::LOAD_PREFERRING_CACHE | + net::LOAD_ONLY_FROM_CACHE | net::LOAD_IGNORE_LIMITS | + net::LOAD_PREFETCH)) { + return false; + } + if (request.method != "GET") + return false; + // A GET request should not have a body. + if (request.request_body.get()) + return false; + if (!request.url.SchemeIsHTTPOrHTTPS()) + return false; + + return true; +} + +void AsyncRevalidationManager::OnAsyncRevalidationComplete( + AsyncRevalidationMap::iterator it) { + in_progress_.erase(it); +} + +} // namespace content diff --git a/chromium/content/browser/loader/async_revalidation_manager.h b/chromium/content/browser/loader/async_revalidation_manager.h new file mode 100644 index 00000000000..1070ea9c8bc --- /dev/null +++ b/chromium/content/browser/loader/async_revalidation_manager.h @@ -0,0 +1,107 @@ +// Copyright 2015 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 CONTENT_BROWSER_LOADER_ASYNC_REVALIDATION_MANAGER_H_ +#define CONTENT_BROWSER_LOADER_ASYNC_REVALIDATION_MANAGER_H_ + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" + +class GURL; +struct ResourceHostMsg_Request; + +namespace net { +class URLRequest; +class HttpCache; +} + +namespace content { + +class AsyncRevalidationDriver; +class ResourceContext; +class ResourceScheduler; + +// One instance of this class manages all active AsyncRevalidationDriver objects +// for all profiles. It is created by and owned by +// ResourceDispatcherHostImpl. It also implements the creation of a new +// net::URLRequest and AsyncRevalidationDriver from an existing net::URLRequest +// that has had the stale-while-revalidate algorithm applied to it. +class AsyncRevalidationManager { + public: + AsyncRevalidationManager(); + ~AsyncRevalidationManager(); + + // Starts an async revalidation by copying |for_request|. |scheduler| must + // remain valid until this object is destroyed. + void BeginAsyncRevalidation(const net::URLRequest& for_request, + ResourceScheduler* scheduler); + + // Cancel all pending async revalidations that use ResourceContext. + void CancelAsyncRevalidationsForResourceContext( + ResourceContext* resource_context); + + static bool QualifiesForAsyncRevalidation( + const ResourceHostMsg_Request& request); + + private: + // The key of the map of pending async revalidations. This key has a distinct + // value for every in-progress async revalidation. It is used to avoid + // duplicate async revalidations, and also to cancel affected async + // revalidations when a ResourceContext is removed. + // + // Request headers are intentionally not included in the key for simplicity, + // as they usually don't affect caching. + // + // TODO(ricea): Behave correctly in cases where the request headers do make a + // difference. crbug.com/567721 + struct AsyncRevalidationKey { + AsyncRevalidationKey(const ResourceContext* resource_context, + const net::HttpCache* http_cache, + const GURL& url); + + // Create a prefix key that is used to match all of the + // AsyncRevalidationDrivers using |resource_context| in the map. + explicit AsyncRevalidationKey(const ResourceContext* resource_context); + + // The key for a map needs to be copyable. + AsyncRevalidationKey(const AsyncRevalidationKey& rhs) = default; + ~AsyncRevalidationKey(); + + // No operator= is generated because the struct members are immutable. + + // |resource_context| and |http_cache| are never dereferenced; they are only + // compared to other values. + const ResourceContext* const resource_context; + + // There are multiple independent HttpCache objects per ResourceContext. + const net::HttpCache* const http_cache; + + // Derived from the url via net::HttpUtil::SpecForRequest(). + const std::string url_key; + + struct LessThan { + bool operator()(const AsyncRevalidationKey& lhs, + const AsyncRevalidationKey& rhs) const; + }; + }; + + using AsyncRevalidationMap = std::map<AsyncRevalidationKey, + scoped_ptr<AsyncRevalidationDriver>, + AsyncRevalidationKey::LessThan>; + + void OnAsyncRevalidationComplete(AsyncRevalidationMap::iterator it); + + // Map of AsyncRevalidationDriver instances that are currently in-flight: + // either waiting to be scheduled or active on the network. + AsyncRevalidationMap in_progress_; + + DISALLOW_COPY_AND_ASSIGN(AsyncRevalidationManager); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_LOADER_ASYNC_REVALIDATION_MANAGER_H_ diff --git a/chromium/content/browser/loader/async_revalidation_manager_browsertest.cc b/chromium/content/browser/loader/async_revalidation_manager_browsertest.cc new file mode 100644 index 00000000000..f7999431b42 --- /dev/null +++ b/chromium/content/browser/loader/async_revalidation_manager_browsertest.cc @@ -0,0 +1,220 @@ +// Copyright 2015 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 <string> +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/command_line.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/run_loop.h" +#include "base/strings/string16.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/shell/browser/shell.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace content { + +namespace { + +using net::test_server::HttpResponse; +using net::test_server::HttpRequest; +using net::test_server::BasicHttpResponse; + +const char kCountedHtmlPath[] = "/counted.html"; +const char kCookieHtmlPath[] = "/cookie.html"; + +class AsyncRevalidationManagerBrowserTest : public ContentBrowserTest { + protected: + AsyncRevalidationManagerBrowserTest() {} + ~AsyncRevalidationManagerBrowserTest() override {} + + void SetUp() override { + ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); + ContentBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + embedded_test_server()->StartAcceptingConnections(); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch("enable-stale-while-revalidate"); + } + + base::RunLoop* run_loop() { return &run_loop_; } + int requests_counted() const { return requests_counted_; } + + // This method lacks diagnostics for the failure case because TitleWatcher + // will just wait until the test times out if |expected_title| does not + // appear. + bool TitleBecomes(const GURL& url, const std::string& expected_title) { + base::string16 expected_title16(base::ASCIIToUTF16(expected_title)); + TitleWatcher title_watcher(shell()->web_contents(), expected_title16); + NavigateToURL(shell(), url); + return title_watcher.WaitAndGetTitle() == expected_title16; + } + + void RegisterCountingRequestHandler() { + embedded_test_server()->RegisterRequestHandler(base::Bind( + &AsyncRevalidationManagerBrowserTest::CountingRequestHandler, this)); + } + + void RegisterCookieRequestHandler() { + embedded_test_server()->RegisterRequestHandler(base::Bind( + &AsyncRevalidationManagerBrowserTest::CookieRequestHandler, this)); + } + + private: + // A request handler which increases the number in the title tag on every + // request. + scoped_ptr<HttpResponse> CountingRequestHandler(const HttpRequest& request) { + if (request.relative_url != kCountedHtmlPath) + return nullptr; + + int version = ++requests_counted_; + + scoped_ptr<BasicHttpResponse> http_response(StaleWhileRevalidateHeaders()); + http_response->set_content( + base::StringPrintf("<title>Version %d</title>", version)); + + // The second time this handler is run is the async revalidation. Tests can + // use this for synchronisation. + if (version == 2) + run_loop_.Quit(); + return std::move(http_response); + } + + // A request handler which increases a cookie value on every request. + scoped_ptr<HttpResponse> CookieRequestHandler(const HttpRequest& request) { + static const char kHtml[] = + "<script>\n" + "var intervalId;\n" + "function checkCookie() {\n" + " if (document.cookie.search(/version=2/) != -1) {\n" + " clearInterval(intervalId);\n" + " document.title = \"PASS\";\n" + " }\n" + "}\n" + "intervalId = setInterval(checkCookie, 10);\n" + "</script>\n" + "<title>Loaded</title>\n"; + + if (request.relative_url != kCookieHtmlPath) + return nullptr; + + int version = ++requests_counted_; + + scoped_ptr<BasicHttpResponse> http_response(StaleWhileRevalidateHeaders()); + http_response->AddCustomHeader("Set-Cookie", + base::StringPrintf("version=%d", version)); + http_response->set_content(kHtml); + + return std::move(http_response); + } + + // Generate the standard response headers common to all request handlers. + scoped_ptr<BasicHttpResponse> StaleWhileRevalidateHeaders() { + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); + http_response->set_code(net::HTTP_OK); + http_response->set_content_type("text/html; charset=utf-8"); + http_response->AddCustomHeader("Cache-Control", + "max-age=0, stale-while-revalidate=86400"); + // A validator is needed for revalidations, and hence + // stale-while-revalidate, to work. + std::string etag = base::StringPrintf( + "\"AsyncRevalidationManagerBrowserTest%d\"", requests_counted_); + http_response->AddCustomHeader("ETag", etag); + return http_response; + } + + base::RunLoop run_loop_; + int requests_counted_ = 0; + + DISALLOW_COPY_AND_ASSIGN(AsyncRevalidationManagerBrowserTest); +}; + +// Verify that the "Cache-Control: stale-while-revalidate" directive correctly +// triggers an async revalidation. +IN_PROC_BROWSER_TEST_F(AsyncRevalidationManagerBrowserTest, + StaleWhileRevalidateIsApplied) { + RegisterCountingRequestHandler(); + GURL url(embedded_test_server()->GetURL(kCountedHtmlPath)); + + EXPECT_TRUE(TitleBecomes(url, "Version 1")); + + // The first request happens synchronously. + EXPECT_EQ(1, requests_counted()); + + // Force the renderer to be destroyed so that the Blink cache doesn't + // interfere with the result. + NavigateToURL(shell(), GURL("about:blank")); + + // Load the page again. We should get the stale version from the cache. + EXPECT_TRUE(TitleBecomes(url, "Version 1")); + + // Wait for the async revalidation to complete. + run_loop()->Run(); + EXPECT_EQ(2, requests_counted()); +} + +// The fresh cache entry must become visible once the async revalidation request +// has been sent. +IN_PROC_BROWSER_TEST_F(AsyncRevalidationManagerBrowserTest, CacheIsUpdated) { + using base::ASCIIToUTF16; + RegisterCountingRequestHandler(); + GURL url(embedded_test_server()->GetURL(kCountedHtmlPath)); + + EXPECT_TRUE(TitleBecomes(url, "Version 1")); + + // Reset the renderer cache. + NavigateToURL(shell(), GURL("about:blank")); + + // Load the page again. We should get the stale version from the cache. + EXPECT_TRUE(TitleBecomes(url, "Version 1")); + + // Wait for the async revalidation request to be processed by the + // EmbeddedTestServer. + run_loop()->Run(); + + // Reset the renderer cache. + NavigateToURL(shell(), GURL("about:blank")); + + // Since the async revalidation request has been sent, the cache can no + // longer return the stale contents. + EXPECT_TRUE(TitleBecomes(url, "Version 2")); +} + +// When the asynchronous revalidation arrives, any cookies it contains must be +// applied immediately. +IN_PROC_BROWSER_TEST_F(AsyncRevalidationManagerBrowserTest, + CookieSetAsynchronously) { + RegisterCookieRequestHandler(); + GURL url(embedded_test_server()->GetURL(kCookieHtmlPath)); + + // Set cookie to version=1 + NavigateToURL(shell(), url); + + // Reset render cache. + NavigateToURL(shell(), GURL("about:blank")); + + // The page will load from the cache, then when the async revalidation + // completes the cookie will update. + EXPECT_TRUE(TitleBecomes(url, "PASS")); +} + +} // namespace + +} // namespace content diff --git a/chromium/content/browser/loader/async_revalidation_manager_unittest.cc b/chromium/content/browser/loader/async_revalidation_manager_unittest.cc new file mode 100644 index 00000000000..d0ec206130d --- /dev/null +++ b/chromium/content/browser/loader/async_revalidation_manager_unittest.cc @@ -0,0 +1,554 @@ +// Copyright 2015 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 "content/browser/loader/async_revalidation_manager.h" + +#include <deque> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/shared_memory_handle.h" +#include "base/pickle.h" +#include "base/run_loop.h" +#include "base/strings/string_util.h" +#include "content/browser/child_process_security_policy_impl.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/browser/loader/resource_message_filter.h" +#include "content/common/child_process_host_impl.h" +#include "content/common/resource_messages.h" +#include "content/public/browser/resource_context.h" +#include "content/public/common/appcache_info.h" +#include "content/public/common/process_type.h" +#include "content/public/common/resource_type.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "ipc/ipc_param_traits.h" +#include "net/base/load_flags.h" +#include "net/base/network_delegate.h" +#include "net/http/http_util.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_job.h" +#include "net/url_request/url_request_job_factory.h" +#include "net/url_request/url_request_test_job.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/page_transition_types.h" +#include "url/gurl.h" +#include "url/url_constants.h" + +namespace content { + +namespace { + +// This class is a variation on URLRequestTestJob that +// returns ERR_IO_PENDING before every read, not just the first one. +class URLRequestTestDelayedCompletionJob : public net::URLRequestTestJob { + public: + URLRequestTestDelayedCompletionJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const std::string& response_headers, + const std::string& response_data) + : net::URLRequestTestJob(request, + network_delegate, + response_headers, + response_data, + false) {} + + private: + bool NextReadAsync() override { return true; } +}; + +// A URLRequestJob implementation which sets the |async_revalidation_required| +// flag on the HttpResponseInfo object to true if the request has the +// LOAD_SUPPORT_ASYNC_REVALIDATION flag. +class AsyncRevalidationRequiredURLRequestTestJob + : public net::URLRequestTestJob { + public: + AsyncRevalidationRequiredURLRequestTestJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) + : URLRequestTestJob(request, + network_delegate, + net::URLRequestTestJob::test_headers(), + std::string(), + false) {} + + void GetResponseInfo(net::HttpResponseInfo* info) override { + URLRequestTestJob::GetResponseInfo(info); + if (request()->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION) + info->async_revalidation_required = true; + } +}; + +// A URLRequestJob implementation which serves a redirect and sets the +// |async_revalidation_required| flag on the HttpResponseInfo object to true if +// the request has the LOAD_SUPPORT_ASYNC_REVALIDATION flag. +class RedirectAndRevalidateURLRequestTestJob : public net::URLRequestTestJob { + public: + RedirectAndRevalidateURLRequestTestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate) + : URLRequestTestJob(request, + network_delegate, + CreateRedirectHeaders(), + std::string(), + false) {} + + void GetResponseInfo(net::HttpResponseInfo* info) override { + URLRequestTestJob::GetResponseInfo(info); + if (request()->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION) + info->async_revalidation_required = true; + } + + private: + static std::string CreateRedirectHeaders() { + static const char kRedirectHeaders[] = + "HTTP/1.1 302 MOVED\n" + "Location: http://example.com/async-revalidate/from-redirect\n" + "\n"; + return std::string(kRedirectHeaders, arraysize(kRedirectHeaders) - 1); + } +}; + +class TestURLRequestJobFactory : public net::URLRequestJobFactory { + public: + TestURLRequestJobFactory() = default; + + // Sets the contents of the response. |headers| should have "\n" as line + // breaks and end in "\n\n". + void SetResponse(const std::string& headers, const std::string& data) { + response_headers_ = headers; + response_data_ = data; + } + + net::URLRequestJob* MaybeCreateJobWithProtocolHandler( + const std::string& scheme, + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override { + std::string path = request->url().path(); + if (base::StartsWith(path, "/async-revalidate", + base::CompareCase::SENSITIVE)) { + return new AsyncRevalidationRequiredURLRequestTestJob(request, + network_delegate); + } + if (base::StartsWith(path, "/redirect", base::CompareCase::SENSITIVE)) { + return new RedirectAndRevalidateURLRequestTestJob(request, + network_delegate); + } + return new URLRequestTestDelayedCompletionJob( + request, network_delegate, response_headers_, response_data_); + } + + net::URLRequestJob* MaybeInterceptRedirect( + net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const GURL& location) const override { + return nullptr; + } + + net::URLRequestJob* MaybeInterceptResponse( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override { + return nullptr; + } + + bool IsHandledProtocol(const std::string& scheme) const override { + // If non-standard schemes need to be tested in future it will be + // necessary to call ChildProcessSecurityPolicyImpl:: + // RegisterWebSafeScheme() for them. + return scheme == url::kHttpScheme || scheme == url::kHttpsScheme; + } + + bool IsHandledURL(const GURL& url) const override { + return IsHandledProtocol(url.scheme()); + } + + bool IsSafeRedirectTarget(const GURL& location) const override { + return false; + } + + private: + std::string response_headers_; + std::string response_data_; + + DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobFactory); +}; + +// On Windows, ResourceMsg_SetDataBuffer supplies a HANDLE which is not +// automatically released. +// +// See ResourceDispatcher::ReleaseResourcesInDataMessage. +// +// TODO(ricea): Maybe share this implementation with +// resource_dispatcher_host_unittest.cc. +void ReleaseHandlesInMessage(const IPC::Message& message) { + if (message.type() == ResourceMsg_SetDataBuffer::ID) { + base::PickleIterator iter(message); + int request_id; + CHECK(iter.ReadInt(&request_id)); + base::SharedMemoryHandle shm_handle; + if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message, &iter, + &shm_handle)) { + if (base::SharedMemory::IsHandleValid(shm_handle)) + base::SharedMemory::CloseHandle(shm_handle); + } + } +} + +// This filter just deletes any messages that are sent through it. +class BlackholeFilter : public ResourceMessageFilter { + public: + explicit BlackholeFilter(ResourceContext* resource_context) + : ResourceMessageFilter( + ChildProcessHostImpl::GenerateChildProcessUniqueId(), + PROCESS_TYPE_RENDERER, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + base::Bind(&BlackholeFilter::GetContexts, base::Unretained(this))), + resource_context_(resource_context) { + ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id()); + } + + bool Send(IPC::Message* msg) override { + scoped_ptr<IPC::Message> take_ownership(msg); + ReleaseHandlesInMessage(*msg); + return true; + } + + private: + ~BlackholeFilter() override { + ChildProcessSecurityPolicyImpl::GetInstance()->Remove(child_id()); + } + + void GetContexts(ResourceType resource_type, + int origin_pid, + ResourceContext** resource_context, + net::URLRequestContext** request_context) { + *resource_context = resource_context_; + *request_context = resource_context_->GetRequestContext(); + } + + ResourceContext* resource_context_; + + DISALLOW_COPY_AND_ASSIGN(BlackholeFilter); +}; + +ResourceHostMsg_Request CreateResourceRequest(const char* method, + ResourceType type, + const GURL& url) { + ResourceHostMsg_Request request; + request.method = std::string(method); + request.url = url; + request.first_party_for_cookies = url; // Bypass third-party cookie blocking. + request.referrer_policy = blink::WebReferrerPolicyDefault; + request.load_flags = 0; + request.origin_pid = 0; + request.resource_type = type; + request.request_context = 0; + request.appcache_host_id = kAppCacheNoHostId; + request.download_to_file = false; + request.should_reset_appcache = false; + request.is_main_frame = true; + request.parent_is_main_frame = false; + request.parent_render_frame_id = -1; + request.transition_type = ui::PAGE_TRANSITION_LINK; + request.allow_download = true; + return request; +} + +class AsyncRevalidationManagerTest : public ::testing::Test { + protected: + AsyncRevalidationManagerTest( + scoped_ptr<net::TestNetworkDelegate> network_delegate) + : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), + network_delegate_(std::move(network_delegate)) { + browser_context_.reset(new TestBrowserContext()); + BrowserContext::EnsureResourceContextInitialized(browser_context_.get()); + base::RunLoop().RunUntilIdle(); + ResourceContext* resource_context = browser_context_->GetResourceContext(); + filter_ = new BlackholeFilter(resource_context); + net::URLRequestContext* request_context = + resource_context->GetRequestContext(); + job_factory_.reset(new TestURLRequestJobFactory); + request_context->set_job_factory(job_factory_.get()); + request_context->set_network_delegate(network_delegate_.get()); + host_.EnableStaleWhileRevalidateForTesting(); + } + + AsyncRevalidationManagerTest() + : AsyncRevalidationManagerTest( + make_scoped_ptr(new net::TestNetworkDelegate)) {} + + void TearDown() override { + host_.CancelRequestsForProcess(filter_->child_id()); + host_.Shutdown(); + host_.CancelRequestsForContext(browser_context_->GetResourceContext()); + browser_context_.reset(); + base::RunLoop().RunUntilIdle(); + } + + void SetResponse(const std::string& headers, const std::string& data) { + job_factory_->SetResponse(headers, data); + } + + // Creates a request using the current test object as the filter and + // SubResource as the resource type. + void MakeTestRequest(int render_view_id, int request_id, const GURL& url) { + ResourceHostMsg_Request request = + CreateResourceRequest("GET", RESOURCE_TYPE_SUB_RESOURCE, url); + ResourceHostMsg_RequestResource msg(render_view_id, request_id, request); + host_.OnMessageReceived(msg, filter_.get()); + base::RunLoop().RunUntilIdle(); + } + + void EnsureSchemeIsAllowed(const std::string& scheme) { + ChildProcessSecurityPolicyImpl* policy = + ChildProcessSecurityPolicyImpl::GetInstance(); + if (!policy->IsWebSafeScheme(scheme)) + policy->RegisterWebSafeScheme(scheme); + } + + content::TestBrowserThreadBundle thread_bundle_; + scoped_ptr<TestBrowserContext> browser_context_; + scoped_ptr<TestURLRequestJobFactory> job_factory_; + scoped_refptr<BlackholeFilter> filter_; + scoped_ptr<net::TestNetworkDelegate> network_delegate_; + ResourceDispatcherHostImpl host_; +}; + +TEST_F(AsyncRevalidationManagerTest, SupportsAsyncRevalidation) { + SetResponse(net::URLRequestTestJob::test_headers(), "delay complete"); + MakeTestRequest(0, 1, GURL("http://example.com/baz")); + + net::URLRequest* url_request( + host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1))); + ASSERT_TRUE(url_request); + + EXPECT_TRUE(url_request->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION); +} + +TEST_F(AsyncRevalidationManagerTest, AsyncRevalidationNotSupportedForPOST) { + SetResponse(net::URLRequestTestJob::test_headers(), "delay complete"); + // Create POST request. + ResourceHostMsg_Request request = CreateResourceRequest( + "POST", RESOURCE_TYPE_SUB_RESOURCE, GURL("http://example.com/baz.php")); + ResourceHostMsg_RequestResource msg(0, 1, request); + host_.OnMessageReceived(msg, filter_.get()); + base::RunLoop().RunUntilIdle(); + + net::URLRequest* url_request( + host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1))); + ASSERT_TRUE(url_request); + + EXPECT_FALSE(url_request->load_flags() & + net::LOAD_SUPPORT_ASYNC_REVALIDATION); +} + +TEST_F(AsyncRevalidationManagerTest, + AsyncRevalidationNotSupportedAfterRedirect) { + static const char kRedirectHeaders[] = + "HTTP/1.1 302 MOVED\n" + "Location: http://example.com/var\n" + "\n"; + SetResponse(kRedirectHeaders, ""); + + MakeTestRequest(0, 1, GURL("http://example.com/baz")); + + net::URLRequest* url_request( + host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1))); + ASSERT_TRUE(url_request); + + EXPECT_FALSE(url_request->load_flags() & + net::LOAD_SUPPORT_ASYNC_REVALIDATION); +} + +// A NetworkDelegate that records the URLRequests as they are created. +class URLRequestRecordingNetworkDelegate : public net::TestNetworkDelegate { + public: + URLRequestRecordingNetworkDelegate() : requests_() {} + + net::URLRequest* NextRequest() { + EXPECT_FALSE(requests_.empty()); + if (requests_.empty()) + return nullptr; + net::URLRequest* request = requests_.front(); + EXPECT_TRUE(request); + requests_.pop_front(); + return request; + } + + bool NextRequestWasDestroyed() { + net::URLRequest* request = requests_.front(); + requests_.pop_front(); + return request == nullptr; + } + + bool IsEmpty() const { return requests_.empty(); } + + int OnBeforeURLRequest(net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) override { + requests_.push_back(request); + return TestNetworkDelegate::OnBeforeURLRequest(request, callback, new_url); + } + + void OnURLRequestDestroyed(net::URLRequest* request) override { + for (auto& recorded_request : requests_) { + if (recorded_request == request) + recorded_request = nullptr; + } + net::TestNetworkDelegate::OnURLRequestDestroyed(request); + } + + private: + std::deque<net::URLRequest*> requests_; + + DISALLOW_COPY_AND_ASSIGN(URLRequestRecordingNetworkDelegate); +}; + +class AsyncRevalidationManagerRecordingTest + : public AsyncRevalidationManagerTest { + public: + AsyncRevalidationManagerRecordingTest() + : AsyncRevalidationManagerTest( + make_scoped_ptr(new URLRequestRecordingNetworkDelegate)) {} + + void TearDown() override { + EXPECT_TRUE(IsEmpty()); + AsyncRevalidationManagerTest::TearDown(); + } + + URLRequestRecordingNetworkDelegate* recording_network_delegate() const { + return static_cast<URLRequestRecordingNetworkDelegate*>( + network_delegate_.get()); + } + + bool NextRequestWasDestroyed() { + return recording_network_delegate()->NextRequestWasDestroyed(); + } + + net::URLRequest* NextRequest() { + return recording_network_delegate()->NextRequest(); + } + + bool IsEmpty() const { return recording_network_delegate()->IsEmpty(); } +}; + +// Verify that an async revalidation is actually created when needed. +TEST_F(AsyncRevalidationManagerRecordingTest, Issued) { + // Create the original request. + MakeTestRequest(0, 1, GURL("http://example.com/async-revalidate")); + + net::URLRequest* initial_request = NextRequest(); + ASSERT_TRUE(initial_request); + EXPECT_TRUE(initial_request->load_flags() & + net::LOAD_SUPPORT_ASYNC_REVALIDATION); + + net::URLRequest* async_request = NextRequest(); + ASSERT_TRUE(async_request); +} + +// Verify the the URL of the async revalidation matches the original request. +TEST_F(AsyncRevalidationManagerRecordingTest, URLMatches) { + // Create the original request. + MakeTestRequest(0, 1, GURL("http://example.com/async-revalidate/u")); + + // Discard the original request. + NextRequest(); + + net::URLRequest* async_request = NextRequest(); + ASSERT_TRUE(async_request); + EXPECT_EQ(GURL("http://example.com/async-revalidate/u"), + async_request->url()); +} + +TEST_F(AsyncRevalidationManagerRecordingTest, + AsyncRevalidationsDoNotSupportAsyncRevalidation) { + // Create the original request. + MakeTestRequest(0, 1, GURL("http://example.com/async-revalidate")); + + // Discard the original request. + NextRequest(); + + // Get the async revalidation request. + net::URLRequest* async_request = NextRequest(); + ASSERT_TRUE(async_request); + EXPECT_FALSE(async_request->load_flags() & + net::LOAD_SUPPORT_ASYNC_REVALIDATION); +} + +TEST_F(AsyncRevalidationManagerRecordingTest, AsyncRevalidationsNotDuplicated) { + // Create the original request. + MakeTestRequest(0, 1, GURL("http://example.com/async-revalidate")); + + // Discard the original request. + NextRequest(); + + // Get the async revalidation request. + net::URLRequest* async_request = NextRequest(); + EXPECT_TRUE(async_request); + + // Start a second request to the same URL. + MakeTestRequest(0, 2, GURL("http://example.com/async-revalidate")); + + // Discard the second request. + NextRequest(); + + // There should not be another async revalidation request. + EXPECT_TRUE(IsEmpty()); +} + +// Async revalidation to different URLs should not be treated as duplicates. +TEST_F(AsyncRevalidationManagerRecordingTest, + AsyncRevalidationsToSeparateURLsAreSeparate) { + // Create two requests to two URLs. + MakeTestRequest(0, 1, GURL("http://example.com/async-revalidate")); + MakeTestRequest(0, 2, GURL("http://example.com/async-revalidate2")); + + net::URLRequest* initial_request = NextRequest(); + ASSERT_TRUE(initial_request); + net::URLRequest* initial_async_revalidation = NextRequest(); + ASSERT_TRUE(initial_async_revalidation); + net::URLRequest* second_request = NextRequest(); + ASSERT_TRUE(second_request); + net::URLRequest* second_async_revalidation = NextRequest(); + ASSERT_TRUE(second_async_revalidation); + + EXPECT_EQ("http://example.com/async-revalidate", + initial_request->url().spec()); + EXPECT_EQ("http://example.com/async-revalidate", + initial_async_revalidation->url().spec()); + EXPECT_EQ("http://example.com/async-revalidate2", + second_request->url().spec()); + EXPECT_EQ("http://example.com/async-revalidate2", + second_async_revalidation->url().spec()); +} + +// Nothing after the first redirect leg has stale-while-revalidate applied. +// TODO(ricea): s-w-r should work with redirects. Change this test when it does. +TEST_F(AsyncRevalidationManagerRecordingTest, NoSWRAfterFirstRedirectLeg) { + MakeTestRequest(0, 1, GURL("http://example.com/redirect")); + + net::URLRequest* initial_request = NextRequest(); + EXPECT_TRUE(initial_request); + + EXPECT_FALSE(initial_request->load_flags() & + net::LOAD_SUPPORT_ASYNC_REVALIDATION); + + // An async revalidation happens for the redirect. It has no body, so it has + // already completed. + EXPECT_TRUE(NextRequestWasDestroyed()); + + // But no others. + EXPECT_TRUE(IsEmpty()); +} + +} // namespace + +} // namespace content diff --git a/chromium/content/browser/loader/certificate_resource_handler.cc b/chromium/content/browser/loader/certificate_resource_handler.cc deleted file mode 100644 index fd49181f8da..00000000000 --- a/chromium/content/browser/loader/certificate_resource_handler.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012 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 "content/browser/loader/certificate_resource_handler.h" - -#include <limits.h> - -#include "components/mime_util/mime_util.h" -#include "content/browser/loader/resource_request_info_impl.h" -#include "content/public/browser/content_browser_client.h" -#include "content/public/common/resource_response.h" -#include "net/base/io_buffer.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_status.h" - -namespace content { - -CertificateResourceHandler::CertificateResourceHandler(net::URLRequest* request) - : ResourceHandler(request), - buffer_(new net::GrowableIOBuffer), - cert_type_(net::CERTIFICATE_MIME_TYPE_UNKNOWN) { -} - -CertificateResourceHandler::~CertificateResourceHandler() { -} - -bool CertificateResourceHandler::OnRequestRedirected( - const net::RedirectInfo& redirect_info, - ResourceResponse* resp, - bool* defer) { - return true; -} - -bool CertificateResourceHandler::OnResponseStarted(ResourceResponse* resp, - bool* defer) { - cert_type_ = - mime_util::GetCertificateMimeTypeForMimeType(resp->head.mime_type); - return cert_type_ != net::CERTIFICATE_MIME_TYPE_UNKNOWN; -} - -bool CertificateResourceHandler::OnWillStart(const GURL& url, bool* defer) { - return true; -} - -bool CertificateResourceHandler::OnBeforeNetworkStart(const GURL& url, - bool* defer) { - return true; -} - -bool CertificateResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, - int* buf_size, - int min_size) { - static const int kInitialBufferSizeInBytes = 32768; - static const int kMaxCertificateSizeInBytes = 1024 * 1024; - - // TODO(gauravsh): Should we use 'min_size' here? - DCHECK(buf); - DCHECK(buf_size); - - if (buffer_->capacity() == 0) { - buffer_->SetCapacity(kInitialBufferSizeInBytes); - } else if (buffer_->RemainingCapacity() == 0) { - int capacity = buffer_->capacity(); - if (capacity >= kMaxCertificateSizeInBytes) - return false; - static_assert(kMaxCertificateSizeInBytes < INT_MAX / 2, - "The size limit ensures the capacity remains in bounds."); - capacity *= 2; - if (capacity > kMaxCertificateSizeInBytes) - capacity = kMaxCertificateSizeInBytes; - buffer_->SetCapacity(capacity); - } - - *buf = buffer_.get(); - *buf_size = buffer_->RemainingCapacity(); - - return true; -} - -bool CertificateResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - DCHECK_LE(0, bytes_read); - DCHECK_LE(bytes_read, buffer_->RemainingCapacity()); - if (!bytes_read) - return true; - - buffer_->set_offset(buffer_->offset() + bytes_read); - return true; -} - -void CertificateResourceHandler::OnResponseCompleted( - const net::URLRequestStatus& urs, - const std::string& sec_info, - bool* defer) { - if (urs.status() != net::URLRequestStatus::SUCCESS) - return; - - // Note that it's up to the browser to verify that the certificate - // data is well-formed. - const ResourceRequestInfo* info = GetRequestInfo(); - GetContentClient()->browser()->AddCertificate( - cert_type_, buffer_->StartOfBuffer(), - static_cast<size_t>(buffer_->offset()), info->GetChildID(), - info->GetRenderFrameID()); -} - -void CertificateResourceHandler::OnDataDownloaded(int bytes_downloaded) { - NOTREACHED(); -} - -} // namespace content diff --git a/chromium/content/browser/loader/certificate_resource_handler.h b/chromium/content/browser/loader/certificate_resource_handler.h deleted file mode 100644 index 743bf9d71dc..00000000000 --- a/chromium/content/browser/loader/certificate_resource_handler.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2012 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 CONTENT_BROWSER_LOADER_CERTIFICATE_RESOURCE_HANDLER_H_ -#define CONTENT_BROWSER_LOADER_CERTIFICATE_RESOURCE_HANDLER_H_ - -#include <string> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "content/browser/loader/resource_handler.h" -#include "net/base/mime_util.h" - -namespace net { -class GrowableIOBuffer; -class URLRequest; -class URLRequestStatus; -} // namespace net - -namespace content { - -// This class handles certificate mime types such as: -// - "application/x-x509-user-cert" -// - "application/x-x509-ca-cert" -// - "application/x-pkcs12" -// -class CertificateResourceHandler : public ResourceHandler { - public: - explicit CertificateResourceHandler(net::URLRequest* request); - ~CertificateResourceHandler() override; - - // Not needed, as this event handler ought to be the final resource. - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* resp, - bool* defer) override; - - // Check if this indeed an X509 cert. - bool OnResponseStarted(ResourceResponse* resp, bool* defer) override; - - // Pass-through implementation. - bool OnWillStart(const GURL& url, bool* defer) override; - - // Pass-through implementation. - bool OnBeforeNetworkStart(const GURL& url, bool* defer) override; - - // Create a new buffer to store received data. - bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, - int* buf_size, - int min_size) override; - - // A read was completed, maybe allocate a new buffer for further data. - bool OnReadCompleted(int bytes_read, bool* defer) override; - - // Done downloading the certificate. - void OnResponseCompleted(const net::URLRequestStatus& urs, - const std::string& sec_info, - bool* defer) override; - - // N/A to cert downloading. - void OnDataDownloaded(int bytes_downloaded) override; - - private: - scoped_refptr<net::GrowableIOBuffer> buffer_; - net::CertificateMimeType cert_type_; - - DISALLOW_COPY_AND_ASSIGN(CertificateResourceHandler); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_CERTIFICATE_RESOURCE_HANDLER_H_ diff --git a/chromium/content/browser/loader/cross_site_resource_handler.cc b/chromium/content/browser/loader/cross_site_resource_handler.cc index 7ffb659744a..fad4024ee28 100644 --- a/chromium/content/browser/loader/cross_site_resource_handler.cc +++ b/chromium/content/browser/loader/cross_site_resource_handler.cc @@ -5,6 +5,7 @@ #include "content/browser/loader/cross_site_resource_handler.h" #include <string> +#include <utility> #include "base/bind.h" #include "base/command_line.h" @@ -76,9 +77,9 @@ void OnCrossSiteResponseHelper(const CrossSiteResponseParams& params) { CHECK(SiteIsolationPolicy::AreCrossProcessFramesPossible()); } rfh->OnCrossSiteResponse( - params.global_request_id, cross_site_transferring_request.Pass(), - params.transfer_url_chain, params.referrer, - params.page_transition, params.should_replace_current_entry); + params.global_request_id, std::move(cross_site_transferring_request), + params.transfer_url_chain, params.referrer, params.page_transition, + params.should_replace_current_entry); } else if (leak_requests_for_testing_ && cross_site_transferring_request) { // Some unit tests expect requests to be leaked in this case, so they can // pass them along manually. @@ -87,42 +88,23 @@ void OnCrossSiteResponseHelper(const CrossSiteResponseParams& params) { } // Returns whether a transfer is needed by doing a check on the UI thread. -bool CheckNavigationPolicyOnUI(GURL real_url, - int process_id, - int render_frame_id) { +CrossSiteResourceHandler::NavigationDecision +CheckNavigationPolicyOnUI(GURL real_url, int process_id, int render_frame_id) { CHECK(SiteIsolationPolicy::AreCrossProcessFramesPossible()); RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(process_id, render_frame_id); + + // Without a valid RFH against which to check, we must cancel the request, + // to prevent the resource at |url| from being delivered to a potentially + // unsuitable renderer process. if (!rfh) - return false; - - // A transfer is not needed if the current SiteInstance doesn't yet have a - // site. This is the case for tests that use NavigateToURL. - if (!rfh->GetSiteInstance()->HasSite()) - return false; - - // For now, GuestViews never transfer on cross-site navigations. - WebContentsImpl* web_contents = - static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost(rfh)); - if (web_contents->GetBrowserPluginGuest()) - return false; - - GURL effective_url = SiteInstanceImpl::GetEffectiveURL( - rfh->GetSiteInstance()->GetBrowserContext(), real_url); - - // TODO(nasko, nick): These following --site-per-process checks are - // overly simplistic. Update them to match all the cases - // considered by RenderFrameHostManager::DetermineSiteInstanceForURL. - if (SiteInstance::IsSameWebSite(rfh->GetSiteInstance()->GetBrowserContext(), - rfh->GetSiteInstance()->GetSiteURL(), - real_url)) { - return false; // The same site, no transition needed. - } + return CrossSiteResourceHandler::NavigationDecision::CANCEL_REQUEST; - // The sites differ. If either one requires a dedicated process, - // then a transfer is needed. - return rfh->GetSiteInstance()->RequiresDedicatedProcess() || - SiteIsolationPolicy::DoesSiteRequireDedicatedProcess(effective_url); + RenderFrameHostManager* manager = rfh->frame_tree_node()->render_manager(); + if (manager->IsRendererTransferNeededForNavigation(rfh, real_url)) + return CrossSiteResourceHandler::NavigationDecision::TRANSFER_REQUIRED; + else + return CrossSiteResourceHandler::NavigationDecision::USE_EXISTING_RENDERER; } } // namespace @@ -130,13 +112,12 @@ bool CheckNavigationPolicyOnUI(GURL real_url, CrossSiteResourceHandler::CrossSiteResourceHandler( scoped_ptr<ResourceHandler> next_handler, net::URLRequest* request) - : LayeredResourceHandler(request, next_handler.Pass()), + : LayeredResourceHandler(request, std::move(next_handler)), has_started_response_(false), in_cross_site_transition_(false), completed_during_transition_(false), did_defer_(false), - weak_ptr_factory_(this) { -} + weak_ptr_factory_(this) {} CrossSiteResourceHandler::~CrossSiteResourceHandler() { // Cleanup back-pointer stored on the request info. @@ -236,11 +217,18 @@ bool CrossSiteResourceHandler::OnNormalResponseStarted( return next_handler_->OnResponseStarted(response, defer); } -void CrossSiteResourceHandler::ResumeOrTransfer(bool is_transfer) { - if (is_transfer) { - StartCrossSiteTransition(response_.get()); - } else { - ResumeResponse(); +void CrossSiteResourceHandler::ResumeOrTransfer(NavigationDecision decision) { + switch (decision) { + case NavigationDecision::CANCEL_REQUEST: + // TODO(nick): What kind of cleanup do we need here? + controller()->Cancel(); + break; + case NavigationDecision::USE_EXISTING_RENDERER: + ResumeResponse(); + break; + case NavigationDecision::TRANSFER_REQUIRED: + StartCrossSiteTransition(response_.get()); + break; } } diff --git a/chromium/content/browser/loader/cross_site_resource_handler.h b/chromium/content/browser/loader/cross_site_resource_handler.h index b73a5a1475b..3c38fc758d4 100644 --- a/chromium/content/browser/loader/cross_site_resource_handler.h +++ b/chromium/content/browser/loader/cross_site_resource_handler.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_LOADER_CROSS_SITE_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_CROSS_SITE_RESOURCE_HANDLER_H_ +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "content/browser/loader/layered_resource_handler.h" @@ -26,6 +27,12 @@ struct TransitionLayerData; // and not a download. class CrossSiteResourceHandler : public LayeredResourceHandler { public: + enum class NavigationDecision { + TRANSFER_REQUIRED, + USE_EXISTING_RENDERER, + CANCEL_REQUEST + }; + CrossSiteResourceHandler(scoped_ptr<ResourceHandler> next_handler, net::URLRequest* request); ~CrossSiteResourceHandler() override; @@ -63,7 +70,7 @@ class CrossSiteResourceHandler : public LayeredResourceHandler { bool OnNormalResponseStarted(ResourceResponse* response, bool* defer); - void ResumeOrTransfer(bool is_transfer); + void ResumeOrTransfer(NavigationDecision decision); void ResumeIfDeferred(); // Called when about to defer a request. Sets |did_defer_| and logs the diff --git a/chromium/content/browser/loader/cross_site_resource_handler_browsertest.cc b/chromium/content/browser/loader/cross_site_resource_handler_browsertest.cc new file mode 100644 index 00000000000..a6df689d032 --- /dev/null +++ b/chromium/content/browser/loader/cross_site_resource_handler_browsertest.cc @@ -0,0 +1,278 @@ +// Copyright 2015 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/callback.h" +#include "base/command_line.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/frame_messages.h" +#include "content/public/browser/resource_dispatcher_host.h" +#include "content/public/browser/resource_dispatcher_host_delegate.h" +#include "content/public/browser/resource_throttle.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_resource_dispatcher_host_delegate.h" +#include "ipc/ipc_security_test_util.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/url_request/url_request.h" + +namespace content { + +namespace { + +// A ResourceDispatchHostDelegate that uses ResourceThrottles to pause a +// targeted request temporarily, to run a chunk of test code. +class TestResourceDispatcherHostDelegate + : public ShellResourceDispatcherHostDelegate { + public: + using RequestDeferredHook = base::Callback<void(const base::Closure& resume)>; + TestResourceDispatcherHostDelegate() : throttle_created_(false) {} + + void RequestBeginning(net::URLRequest* request, + ResourceContext* resource_context, + AppCacheService* appcache_service, + ResourceType resource_type, + ScopedVector<ResourceThrottle>* throttles) override { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ShellResourceDispatcherHostDelegate::RequestBeginning( + request, resource_context, appcache_service, resource_type, throttles); + + // If this is a request for the tracked URL, add a throttle to track it. + if (request->url() == tracked_url_) { + // Expect only a single request for the tracked url. + ASSERT_FALSE(throttle_created_); + throttle_created_ = true; + + throttles->push_back( + new CallbackRunningResourceThrottle(request, this, run_on_start_)); + } + } + + // Starts tracking a URL. The request for previously tracked URL, if any, + // must have been made and deleted before calling this function. + void SetTrackedURL(const GURL& tracked_url, + const RequestDeferredHook& run_on_start) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Should not currently be tracking any URL. + ASSERT_FALSE(run_loop_); + + // Create a RunLoop that will be stopped once the request for the tracked + // URL has been destroyed, to allow tracking the URL while also waiting for + // other events. + run_loop_.reset(new base::RunLoop()); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&TestResourceDispatcherHostDelegate::SetTrackedURLOnIOThread, + base::Unretained(this), tracked_url, run_on_start, + run_loop_->QuitClosure())); + } + + // Waits until the tracked URL has been requested, and the request for it has + // been destroyed. + bool WaitForTrackedURLAndGetCompleted() { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + run_loop_->Run(); + run_loop_.reset(); + return tracked_request_completed_; + } + + private: + // A ResourceThrottle which defers the request at WillStartRequest time until + // a test-supplied callback completes. Notifies |tracker| when the request is + // destroyed. + class CallbackRunningResourceThrottle : public ResourceThrottle { + public: + CallbackRunningResourceThrottle(net::URLRequest* request, + TestResourceDispatcherHostDelegate* tracker, + const RequestDeferredHook& run_on_start) + : request_(request), + tracker_(tracker), + run_on_start_(run_on_start), + weak_factory_(this) {} + + void WillStartRequest(bool* defer) override { + *defer = true; + base::Closure resume_request_on_io_thread = base::Bind( + base::IgnoreResult(&BrowserThread::PostTask), BrowserThread::IO, + FROM_HERE, base::Bind(&CallbackRunningResourceThrottle::Resume, + weak_factory_.GetWeakPtr())); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(run_on_start_, resume_request_on_io_thread)); + } + + ~CallbackRunningResourceThrottle() override { + // If the request is deleted without being cancelled, its status will + // indicate it succeeded, so have to check if the request is still pending + // as well. + tracker_->OnTrackedRequestDestroyed(!request_->is_pending() && + request_->status().is_success()); + } + + // ResourceThrottle implementation: + const char* GetNameForLogging() const override { + return "CallbackRunningResourceThrottle"; + } + + private: + void Resume() { controller()->Resume(); } + net::URLRequest* request_; + TestResourceDispatcherHostDelegate* tracker_; + RequestDeferredHook run_on_start_; + base::WeakPtrFactory<CallbackRunningResourceThrottle> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(CallbackRunningResourceThrottle); + }; + + void SetTrackedURLOnIOThread(const GURL& tracked_url, + const RequestDeferredHook& run_on_start, + const base::Closure& run_loop_quit_closure) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + throttle_created_ = false; + tracked_url_ = tracked_url; + run_on_start_ = run_on_start; + run_loop_quit_closure_ = run_loop_quit_closure; + } + + void OnTrackedRequestDestroyed(bool completed) { + CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + tracked_request_completed_ = completed; + tracked_url_ = GURL(); + run_on_start_ = RequestDeferredHook(); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + run_loop_quit_closure_); + } + + // These live on the IO thread. + GURL tracked_url_; + bool throttle_created_; + base::Closure run_loop_quit_closure_; + RequestDeferredHook run_on_start_; + + // This lives on the UI thread. + scoped_ptr<base::RunLoop> run_loop_; + + // Set on the IO thread while |run_loop_| is non-nullptr, read on the UI + // thread after deleting run_loop_. + bool tracked_request_completed_; + + DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate); +}; + +class CrossSiteResourceHandlerTest : public ContentBrowserTest { + public: + CrossSiteResourceHandlerTest() : old_delegate_(nullptr) {} + + // ContentBrowserTest implementation: + void SetUpOnMainThread() override { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind( + &CrossSiteResourceHandlerTest::InjectResourceDispatcherHostDelegate, + base::Unretained(this))); + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + content::SetupCrossSiteRedirector(embedded_test_server()); + } + + void TearDownOnMainThread() override { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&CrossSiteResourceHandlerTest:: + RestoreResourceDispatcherHostDelegate, + base::Unretained(this))); + } + + protected: + void SetUpCommandLine(base::CommandLine* command_line) override { + IsolateAllSitesForTesting(command_line); + } + + void InjectResourceDispatcherHostDelegate() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + old_delegate_ = ResourceDispatcherHostImpl::Get()->delegate(); + ResourceDispatcherHostImpl::Get()->SetDelegate(&tracking_delegate_); + } + + void RestoreResourceDispatcherHostDelegate() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate_); + old_delegate_ = nullptr; + } + + TestResourceDispatcherHostDelegate& tracking_delegate() { + return tracking_delegate_; + } + + private: + TestResourceDispatcherHostDelegate tracking_delegate_; + ResourceDispatcherHostDelegate* old_delegate_; +}; + +void SimulateMaliciousFrameDetachOnUIThread(int render_process_id, + int frame_routing_id, + const base::Closure& done_cb) { + RenderFrameHostImpl* rfh = + RenderFrameHostImpl::FromID(render_process_id, frame_routing_id); + CHECK(rfh); + + // Inject a frame detach message. An attacker-controlled renderer could do + // this without also cancelling the pending navigation (as blink would, if you + // removed the iframe from the document via js). + rfh->OnMessageReceived(FrameHostMsg_Detach(frame_routing_id)); + done_cb.Run(); +} + +} // namespace + +// Regression test for https://crbug.com/538784 -- ensures that one can't +// sidestep CrossSiteResourceHandler by detaching a frame mid-request. +IN_PROC_BROWSER_TEST_F(CrossSiteResourceHandlerTest, + NoDeliveryToDetachedFrame) { + GURL attacker_page = embedded_test_server()->GetURL( + "evil.com", "/cross_site_iframe_factory.html?evil(evil)"); + EXPECT_TRUE(NavigateToURL(shell(), attacker_page)); + + FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) + ->GetFrameTree() + ->root(); + + RenderFrameHost* child_frame = root->child_at(0)->current_frame_host(); + + // Attacker initiates a navigation to a cross-site document. Under --site-per- + // process, these bytes must not be sent to the attacker process. + GURL target_resource = + embedded_test_server()->GetURL("a.com", "/title1.html"); + + // We add a testing hook to simulate the attacker-controlled process sending + // FrameHostMsg_Detach before the http response arrives. At the time this test + // was written, the resource request had a lifetime separate from the RFH, + tracking_delegate().SetTrackedURL( + target_resource, base::Bind(&SimulateMaliciousFrameDetachOnUIThread, + child_frame->GetProcess()->GetID(), + child_frame->GetRoutingID())); + EXPECT_TRUE(ExecuteScript( + shell()->web_contents()->GetMainFrame(), + base::StringPrintf("document.getElementById('child-0').src='%s'", + target_resource.spec().c_str()))); + + // Wait for the scenario to play out. If this returns false, it means the + // request did not succeed, which is good in this case. + EXPECT_FALSE(tracking_delegate().WaitForTrackedURLAndGetCompleted()) + << "Request should have been cancelled before reaching the renderer."; +} + +} // namespace content diff --git a/chromium/content/browser/loader/detachable_resource_handler.cc b/chromium/content/browser/loader/detachable_resource_handler.cc index df9c357a888..786252f5b6b 100644 --- a/chromium/content/browser/loader/detachable_resource_handler.cc +++ b/chromium/content/browser/loader/detachable_resource_handler.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/detachable_resource_handler.h" +#include <utility> + #include "base/logging.h" #include "base/time/time.h" #include "content/browser/loader/resource_request_info_impl.h" @@ -24,7 +26,7 @@ DetachableResourceHandler::DetachableResourceHandler( base::TimeDelta cancel_delay, scoped_ptr<ResourceHandler> next_handler) : ResourceHandler(request), - next_handler_(next_handler.Pass()), + next_handler_(std::move(next_handler)), cancel_delay_(cancel_delay), is_deferred_(false), is_finished_(false) { diff --git a/chromium/content/browser/loader/detachable_resource_handler.h b/chromium/content/browser/loader/detachable_resource_handler.h index 4bb1763ec8d..95072269956 100644 --- a/chromium/content/browser/loader/detachable_resource_handler.h +++ b/chromium/content/browser/loader/detachable_resource_handler.h @@ -5,8 +5,8 @@ #ifndef CONTENT_BROWSER_LOADER_DETACHABLE_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_DETACHABLE_RESOURCE_HANDLER_H_ -#include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" diff --git a/chromium/content/browser/loader/global_routing_id.h b/chromium/content/browser/loader/global_routing_id.h index a15e93d78da..df5f202c22e 100644 --- a/chromium/content/browser/loader/global_routing_id.h +++ b/chromium/content/browser/loader/global_routing_id.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_LOADER_GLOBAL_ROUTING_ID_H_ #define CONTENT_BROWSER_LOADER_GLOBAL_ROUTING_ID_H_ +#include <tuple> + namespace content { // Uniquely identifies the route from which a net::URLRequest comes. @@ -24,9 +26,8 @@ struct GlobalRoutingID { int route_id; bool operator<(const GlobalRoutingID& other) const { - if (child_id == other.child_id) - return route_id < other.route_id; - return child_id < other.child_id; + return std::tie(child_id, route_id) < + std::tie(other.child_id, other.route_id); } bool operator==(const GlobalRoutingID& other) const { return child_id == other.child_id && diff --git a/chromium/content/browser/loader/layered_resource_handler.cc b/chromium/content/browser/loader/layered_resource_handler.cc index 00048ae520a..9ff3d55e6f3 100644 --- a/chromium/content/browser/loader/layered_resource_handler.cc +++ b/chromium/content/browser/loader/layered_resource_handler.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/layered_resource_handler.h" +#include <utility> + #include "base/logging.h" namespace content { @@ -11,9 +13,7 @@ namespace content { LayeredResourceHandler::LayeredResourceHandler( net::URLRequest* request, scoped_ptr<ResourceHandler> next_handler) - : ResourceHandler(request), - next_handler_(next_handler.Pass()) { -} + : ResourceHandler(request), next_handler_(std::move(next_handler)) {} LayeredResourceHandler::~LayeredResourceHandler() { } diff --git a/chromium/content/browser/loader/mime_type_resource_handler.cc b/chromium/content/browser/loader/mime_type_resource_handler.cc index 59c2b531066..3c69c0ad4d2 100644 --- a/chromium/content/browser/loader/mime_type_resource_handler.cc +++ b/chromium/content/browser/loader/mime_type_resource_handler.cc @@ -4,6 +4,7 @@ #include "content/browser/loader/mime_type_resource_handler.h" +#include <utility> #include <vector> #include "base/bind.h" @@ -16,7 +17,6 @@ #include "components/mime_util/mime_util.h" #include "content/browser/download/download_resource_handler.h" #include "content/browser/download/download_stats.h" -#include "content/browser/loader/certificate_resource_handler.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/loader/stream_resource_handler.h" @@ -86,7 +86,7 @@ MimeTypeResourceHandler::MimeTypeResourceHandler( ResourceDispatcherHostImpl* host, PluginService* plugin_service, net::URLRequest* request) - : LayeredResourceHandler(request, next_handler.Pass()), + : LayeredResourceHandler(request, std::move(next_handler)), state_(STATE_STARTING), host_(host), plugin_service_(plugin_service), @@ -94,8 +94,7 @@ MimeTypeResourceHandler::MimeTypeResourceHandler( bytes_read_(0), must_download_(false), must_download_is_set_(false), - weak_ptr_factory_(this) { -} + weak_ptr_factory_(this) {} MimeTypeResourceHandler::~MimeTypeResourceHandler() { } @@ -329,7 +328,7 @@ bool MimeTypeResourceHandler::SelectPluginHandler(bool* defer, plugin_path, request(), response_.get(), &payload)); if (handler) { *handled_by_plugin = true; - return UseAlternateNextHandler(handler.Pass(), payload); + return UseAlternateNextHandler(std::move(handler), payload); } #endif return true; @@ -341,12 +340,14 @@ bool MimeTypeResourceHandler::SelectNextHandler(bool* defer) { ResourceRequestInfoImpl* info = GetRequestInfo(); const std::string& mime_type = response_->head.mime_type; - if (mime_util::IsSupportedCertificateMimeType(mime_type)) { - // Install certificate file. - info->set_is_download(true); - scoped_ptr<ResourceHandler> handler( - new CertificateResourceHandler(request())); - return UseAlternateNextHandler(handler.Pass(), std::string()); + // https://crbug.com/568184 - Temporary hack to track servers that aren't + // setting Content-Disposition when sending x-x509-user-cert and expecting + // the browser to automatically install certificates; this is being + // deprecated and will be removed upon full <keygen> removal. + if (mime_type == "application/x-x509-user-cert") { + UMA_HISTOGRAM_BOOLEAN( + "UserCert.ContentDisposition", + response_->head.headers->HasHeader("Content-Disposition")); } // Allow requests for object/embed tags to be intercepted as streams. @@ -391,7 +392,7 @@ bool MimeTypeResourceHandler::SelectNextHandler(bool* defer) { DownloadItem::kInvalidId, scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()), DownloadUrlParameters::OnStartedCallback())); - return UseAlternateNextHandler(handler.Pass(), std::string()); + return UseAlternateNextHandler(std::move(handler), std::string()); } bool MimeTypeResourceHandler::UseAlternateNextHandler( @@ -443,7 +444,7 @@ bool MimeTypeResourceHandler::UseAlternateNextHandler( // This is handled entirely within the new ResourceHandler, so just reset the // original ResourceHandler. - next_handler_ = new_handler.Pass(); + next_handler_ = std::move(new_handler); next_handler_->SetController(this); return CopyReadBufferToNextHandler(); diff --git a/chromium/content/browser/loader/mime_type_resource_handler.h b/chromium/content/browser/loader/mime_type_resource_handler.h index b5e56c21b70..9217683584d 100644 --- a/chromium/content/browser/loader/mime_type_resource_handler.h +++ b/chromium/content/browser/loader/mime_type_resource_handler.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/macros.h" #include "base/memory/weak_ptr.h" #include "content/browser/loader/layered_resource_handler.h" #include "content/common/content_export.h" diff --git a/chromium/content/browser/loader/mime_type_resource_handler_unittest.cc b/chromium/content/browser/loader/mime_type_resource_handler_unittest.cc index 92fbbcb1fec..1b3ddcdbdf0 100644 --- a/chromium/content/browser/loader/mime_type_resource_handler_unittest.cc +++ b/chromium/content/browser/loader/mime_type_resource_handler_unittest.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/mime_type_resource_handler.h" +#include <stdint.h> + #include "base/files/file_path.h" #include "base/logging.h" #include "base/macros.h" @@ -90,10 +92,10 @@ class TestResourceDispatcherHost : public ResourceDispatcherHostImpl { net::URLRequest* request, bool is_content_initiated, bool must_download, - uint32 id, + uint32_t id, scoped_ptr<DownloadSaveInfo> save_info, const DownloadUrlParameters::OnStartedCallback& started_cb) override { - return scoped_ptr<ResourceHandler>(new TestResourceHandler).Pass(); + return scoped_ptr<ResourceHandler>(new TestResourceHandler); } scoped_ptr<ResourceHandler> MaybeInterceptAsStream( @@ -104,7 +106,7 @@ class TestResourceDispatcherHost : public ResourceDispatcherHostImpl { intercepted_as_stream_count_++; if (stream_has_handler_) { intercepted_as_stream_ = true; - return scoped_ptr<ResourceHandler>(new TestResourceHandler).Pass(); + return scoped_ptr<ResourceHandler>(new TestResourceHandler); } else { return scoped_ptr<ResourceHandler>(); } @@ -248,17 +250,17 @@ bool MimeTypeResourceHandlerTest::TestStreamIsIntercepted( is_main_frame, // is_main_frame false, // parent_is_main_frame allow_download, // allow_download - true); // is_async + true, // is_async + false); // is_using_lofi TestResourceDispatcherHost host(stream_has_handler_); TestResourceDispatcherHostDelegate host_delegate(must_download); host.SetDelegate(&host_delegate); TestFakePluginService plugin_service(plugin_available_, plugin_stale_); - scoped_ptr<ResourceHandler> mime_sniffing_handler( - new MimeTypeResourceHandler( - scoped_ptr<ResourceHandler>(new TestResourceHandler()).Pass(), &host, - &plugin_service, request.get())); + scoped_ptr<ResourceHandler> mime_sniffing_handler(new MimeTypeResourceHandler( + scoped_ptr<ResourceHandler>(new TestResourceHandler()), &host, + &plugin_service, request.get())); TestResourceController resource_controller; mime_sniffing_handler->SetController(&resource_controller); @@ -317,6 +319,14 @@ TEST_F(MimeTypeResourceHandlerTest, StreamHandling) { EXPECT_FALSE( TestStreamIsIntercepted(allow_download, must_download, resource_type)); + // Plugin resource request with download not allowed. Stream shouldn't be + // intercepted. + allow_download = false; + must_download = false; + resource_type = RESOURCE_TYPE_PLUGIN_RESOURCE; + EXPECT_FALSE( + TestStreamIsIntercepted(allow_download, must_download, resource_type)); + // Object request with download not allowed. Stream should be intercepted. allow_download = false; must_download = false; diff --git a/chromium/content/browser/loader/navigation_resource_throttle.cc b/chromium/content/browser/loader/navigation_resource_throttle.cc index 8dd143cc079..44fd0c131ff 100644 --- a/chromium/content/browser/loader/navigation_resource_throttle.cc +++ b/chromium/content/browser/loader/navigation_resource_throttle.cc @@ -23,6 +23,14 @@ namespace { typedef base::Callback<void(NavigationThrottle::ThrottleCheckResult)> UIChecksPerformedCallback; +void SendCheckResultToIOThread(UIChecksPerformedCallback callback, + NavigationThrottle::ThrottleCheckResult result) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + CHECK(result != NavigationThrottle::DEFER); + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(callback, result)); +} + void CheckWillStartRequestOnUIThread(UIChecksPerformedCallback callback, int render_process_id, int render_frame_host_id, @@ -32,48 +40,76 @@ void CheckWillStartRequestOnUIThread(UIChecksPerformedCallback callback, ui::PageTransition transition, bool is_external_protocol) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - NavigationThrottle::ThrottleCheckResult result = NavigationThrottle::PROCEED; RenderFrameHostImpl* render_frame_host = RenderFrameHostImpl::FromID(render_process_id, render_frame_host_id); - if (render_frame_host) { - NavigationHandleImpl* navigation_handle = - render_frame_host->navigation_handle(); - if (navigation_handle) { - result = navigation_handle->WillStartRequest(is_post, sanitized_referrer, - has_user_gesture, transition, - is_external_protocol); - } + if (!render_frame_host) { + SendCheckResultToIOThread(callback, NavigationThrottle::PROCEED); + return; } - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(callback, result)); + + NavigationHandleImpl* navigation_handle = + render_frame_host->navigation_handle(); + if (!navigation_handle) { + SendCheckResultToIOThread(callback, NavigationThrottle::PROCEED); + return; + } + + navigation_handle->WillStartRequest( + is_post, sanitized_referrer, has_user_gesture, transition, + is_external_protocol, base::Bind(&SendCheckResultToIOThread, callback)); } -void CheckWillRedirectRequestOnUIThread(UIChecksPerformedCallback callback, - int render_process_id, - int render_frame_host_id, - const GURL& new_url, - bool new_method_is_post, - const GURL& new_referrer_url, - bool new_is_external_protocol) { +void CheckWillRedirectRequestOnUIThread( + UIChecksPerformedCallback callback, + int render_process_id, + int render_frame_host_id, + const GURL& new_url, + bool new_method_is_post, + const GURL& new_referrer_url, + bool new_is_external_protocol, + scoped_refptr<net::HttpResponseHeaders> headers) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - NavigationThrottle::ThrottleCheckResult result = NavigationThrottle::PROCEED; RenderFrameHostImpl* render_frame_host = RenderFrameHostImpl::FromID(render_process_id, render_frame_host_id); - if (render_frame_host) { - NavigationHandleImpl* navigation_handle = - render_frame_host->navigation_handle(); - if (navigation_handle) { - RenderProcessHost* rph = RenderProcessHost::FromID(render_process_id); - GURL new_validated_url = new_url; - rph->FilterURL(false, &new_validated_url); - result = navigation_handle->WillRedirectRequest( - new_validated_url, new_method_is_post, new_referrer_url, - new_is_external_protocol); - } + if (!render_frame_host) { + SendCheckResultToIOThread(callback, NavigationThrottle::PROCEED); + return; } - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(callback, result)); + + NavigationHandleImpl* navigation_handle = + render_frame_host->navigation_handle(); + if (!navigation_handle) { + SendCheckResultToIOThread(callback, NavigationThrottle::PROCEED); + return; + } + + GURL new_validated_url(new_url); + RenderProcessHost::FromID(render_process_id) + ->FilterURL(false, &new_validated_url); + navigation_handle->WillRedirectRequest( + new_validated_url, new_method_is_post, new_referrer_url, + new_is_external_protocol, headers, + base::Bind(&SendCheckResultToIOThread, callback)); } + +void WillProcessResponseOnUIThread( + int render_process_id, + int render_frame_host_id, + scoped_refptr<net::HttpResponseHeaders> headers) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + RenderFrameHostImpl* render_frame_host = + RenderFrameHostImpl::FromID(render_process_id, render_frame_host_id); + if (!render_frame_host) + return; + + NavigationHandleImpl* navigation_handle = + render_frame_host->navigation_handle(); + if (!navigation_handle) + return; + + navigation_handle->ReadyToCommitNavigation(render_frame_host, headers); +} + } // namespace NavigationResourceThrottle::NavigationResourceThrottle(net::URLRequest* request) @@ -82,6 +118,7 @@ NavigationResourceThrottle::NavigationResourceThrottle(net::URLRequest* request) NavigationResourceThrottle::~NavigationResourceThrottle() {} void NavigationResourceThrottle::WillStartRequest(bool* defer) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); if (!info) return; @@ -112,6 +149,7 @@ void NavigationResourceThrottle::WillStartRequest(bool* defer) { void NavigationResourceThrottle::WillRedirectRequest( const net::RedirectInfo& redirect_info, bool* defer) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); if (!info) return; @@ -128,15 +166,50 @@ void NavigationResourceThrottle::WillRedirectRequest( UIChecksPerformedCallback callback = base::Bind(&NavigationResourceThrottle::OnUIChecksPerformed, weak_ptr_factory_.GetWeakPtr()); + + // Send the redirect info to the NavigationHandle on the UI thread. + // Note: to avoid threading issues, a copy of the HttpResponseHeaders is sent + // in lieu of the original. + scoped_refptr<net::HttpResponseHeaders> response_headers; + if (request_->response_headers()) { + response_headers = new net::HttpResponseHeaders( + request_->response_headers()->raw_headers()); + } + BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&CheckWillRedirectRequestOnUIThread, callback, render_process_id, render_frame_id, redirect_info.new_url, redirect_info.new_method == "POST", - GURL(redirect_info.new_referrer), new_is_external_protocol)); + GURL(redirect_info.new_referrer), new_is_external_protocol, + response_headers)); *defer = true; } +void NavigationResourceThrottle::WillProcessResponse(bool* defer) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); + if (!info) + return; + + int render_process_id, render_frame_id; + if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_id)) + return; + + // Send a copy of the response headers to the NavigationHandle on the UI + // thread. + scoped_refptr<net::HttpResponseHeaders> response_headers; + if (request_->response_headers()) { + response_headers = new net::HttpResponseHeaders( + request_->response_headers()->raw_headers()); + } + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&WillProcessResponseOnUIThread, render_process_id, + render_frame_id, response_headers)); +} + const char* NavigationResourceThrottle::GetNameForLogging() const { return "NavigationResourceThrottle"; } @@ -146,6 +219,8 @@ void NavigationResourceThrottle::OnUIChecksPerformed( DCHECK_CURRENTLY_ON(BrowserThread::IO); if (result == NavigationThrottle::CANCEL_AND_IGNORE) { controller()->CancelAndIgnore(); + } else if (result == NavigationThrottle::CANCEL) { + controller()->Cancel(); } else { controller()->Resume(); } diff --git a/chromium/content/browser/loader/navigation_resource_throttle.h b/chromium/content/browser/loader/navigation_resource_throttle.h index e46b59d0669..6357b20aad4 100644 --- a/chromium/content/browser/loader/navigation_resource_throttle.h +++ b/chromium/content/browser/loader/navigation_resource_throttle.h @@ -5,7 +5,7 @@ #ifndef CONTENT_BROWSER_LOADER_NAVIGATION_RESOURCE_THROTTLE_H_ #define CONTENT_BROWSER_LOADER_NAVIGATION_RESOURCE_THROTTLE_H_ -#include "base/basictypes.h" +#include "base/macros.h" #include "base/memory/weak_ptr.h" #include "content/public/browser/navigation_throttle.h" #include "content/public/browser/resource_throttle.h" @@ -28,6 +28,7 @@ class NavigationResourceThrottle : public ResourceThrottle { void WillStartRequest(bool* defer) override; void WillRedirectRequest(const net::RedirectInfo& redirect_info, bool* defer) override; + void WillProcessResponse(bool* defer) override; const char* GetNameForLogging() const override; private: diff --git a/chromium/content/browser/loader/navigation_url_loader.cc b/chromium/content/browser/loader/navigation_url_loader.cc index c1513dc4a5d..e2507650a81 100644 --- a/chromium/content/browser/loader/navigation_url_loader.cc +++ b/chromium/content/browser/loader/navigation_url_loader.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/navigation_url_loader.h" +#include <utility> + #include "content/browser/frame_host/navigation_request_info.h" #include "content/browser/loader/navigation_url_loader_factory.h" #include "content/browser/loader/navigation_url_loader_impl.h" @@ -15,13 +17,15 @@ static NavigationURLLoaderFactory* g_factory = nullptr; scoped_ptr<NavigationURLLoader> NavigationURLLoader::Create( BrowserContext* browser_context, scoped_ptr<NavigationRequestInfo> request_info, + ServiceWorkerNavigationHandle* service_worker_handle, NavigationURLLoaderDelegate* delegate) { if (g_factory) { - return g_factory->CreateLoader(browser_context, request_info.Pass(), - delegate); + return g_factory->CreateLoader(browser_context, std::move(request_info), + service_worker_handle, delegate); } - return scoped_ptr<NavigationURLLoader>(new NavigationURLLoaderImpl( - browser_context, request_info.Pass(), delegate)); + return scoped_ptr<NavigationURLLoader>( + new NavigationURLLoaderImpl(browser_context, std::move(request_info), + service_worker_handle, delegate)); } void NavigationURLLoader::SetFactoryForTesting( diff --git a/chromium/content/browser/loader/navigation_url_loader.h b/chromium/content/browser/loader/navigation_url_loader.h index 89553f6e30f..0ba836a8d67 100644 --- a/chromium/content/browser/loader/navigation_url_loader.h +++ b/chromium/content/browser/loader/navigation_url_loader.h @@ -5,7 +5,6 @@ #ifndef CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_H_ #define CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_H_ -#include "base/basictypes.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "content/common/content_export.h" @@ -15,6 +14,7 @@ namespace content { class BrowserContext; class NavigationURLLoaderDelegate; class NavigationURLLoaderFactory; +class ServiceWorkerNavigationHandle; struct CommonNavigationParams; struct NavigationRequestInfo; @@ -35,6 +35,7 @@ class CONTENT_EXPORT NavigationURLLoader { static scoped_ptr<NavigationURLLoader> Create( BrowserContext* browser_context, scoped_ptr<NavigationRequestInfo> request_info, + ServiceWorkerNavigationHandle* service_worker_handle, NavigationURLLoaderDelegate* delegate); // For testing purposes; sets the factory for use in testing. diff --git a/chromium/content/browser/loader/navigation_url_loader_factory.h b/chromium/content/browser/loader/navigation_url_loader_factory.h index 770644e0519..a2fd37136a7 100644 --- a/chromium/content/browser/loader/navigation_url_loader_factory.h +++ b/chromium/content/browser/loader/navigation_url_loader_factory.h @@ -18,6 +18,7 @@ class NavigationURLLoaderFactory { virtual scoped_ptr<NavigationURLLoader> CreateLoader( BrowserContext* browser_context, scoped_ptr<NavigationRequestInfo> request_info, + ServiceWorkerNavigationHandle* service_worker_handle, NavigationURLLoaderDelegate* delegate) = 0; protected: diff --git a/chromium/content/browser/loader/navigation_url_loader_impl.cc b/chromium/content/browser/loader/navigation_url_loader_impl.cc index 7cbd9112d6d..1f95ce3165f 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl.cc +++ b/chromium/content/browser/loader/navigation_url_loader_impl.cc @@ -4,11 +4,15 @@ #include "content/browser/loader/navigation_url_loader_impl.h" +#include <utility> + #include "base/bind.h" #include "base/location.h" +#include "base/trace_event/trace_event.h" #include "content/browser/frame_host/navigation_request_info.h" #include "content/browser/loader/navigation_url_loader_delegate.h" #include "content/browser/loader/navigation_url_loader_impl_core.h" +#include "content/browser/service_worker/service_worker_navigation_handle.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/stream_handle.h" @@ -18,17 +22,28 @@ namespace content { NavigationURLLoaderImpl::NavigationURLLoaderImpl( BrowserContext* browser_context, scoped_ptr<NavigationRequestInfo> request_info, + ServiceWorkerNavigationHandle* service_worker_handle, NavigationURLLoaderDelegate* delegate) - : delegate_(delegate), - weak_factory_(this) { + : delegate_(delegate), weak_factory_(this) { DCHECK_CURRENTLY_ON(BrowserThread::UI); core_ = new NavigationURLLoaderImplCore(weak_factory_.GetWeakPtr()); + + // TODO(carlosk): extend this trace to support non-PlzNavigate navigations. + // For the trace below we're using the NavigationURLLoaderImplCore as the + // async trace id, |navigation_start| as the timestamp and reporting the + // FrameTreeNode id as a parameter. + TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1( + "navigation", "Navigation timeToResponseStarted", core_, + request_info->common_params.navigation_start.ToInternalValue(), + "FrameTreeNode id", request_info->frame_tree_node_id); + ServiceWorkerNavigationHandleCore* service_worker_handle_core = + service_worker_handle ? service_worker_handle->core() : nullptr; BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&NavigationURLLoaderImplCore::Start, base::Unretained(core_), browser_context->GetResourceContext(), - base::Passed(&request_info))); + service_worker_handle_core, base::Passed(&request_info))); } NavigationURLLoaderImpl::~NavigationURLLoaderImpl() { @@ -60,7 +75,7 @@ void NavigationURLLoaderImpl::NotifyResponseStarted( scoped_ptr<StreamHandle> body) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - delegate_->OnResponseStarted(response, body.Pass()); + delegate_->OnResponseStarted(response, std::move(body)); } void NavigationURLLoaderImpl::NotifyRequestFailed(bool in_cache, diff --git a/chromium/content/browser/loader/navigation_url_loader_impl.h b/chromium/content/browser/loader/navigation_url_loader_impl.h index 6a88fc8650c..5c7fcd0d2b0 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl.h +++ b/chromium/content/browser/loader/navigation_url_loader_impl.h @@ -19,6 +19,7 @@ struct RedirectInfo; namespace content { class NavigationURLLoaderImplCore; +class ServiceWorkerNavigationHandle; class StreamHandle; struct ResourceResponse; @@ -27,6 +28,7 @@ class NavigationURLLoaderImpl : public NavigationURLLoader { // The caller is responsible for ensuring that |delegate| outlives the loader. NavigationURLLoaderImpl(BrowserContext* browser_context, scoped_ptr<NavigationRequestInfo> request_info, + ServiceWorkerNavigationHandle* service_worker_handle, NavigationURLLoaderDelegate* delegate); ~NavigationURLLoaderImpl() override; diff --git a/chromium/content/browser/loader/navigation_url_loader_impl_core.cc b/chromium/content/browser/loader/navigation_url_loader_impl_core.cc index 7fe69513040..bd7ef98f8ea 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl_core.cc +++ b/chromium/content/browser/loader/navigation_url_loader_impl_core.cc @@ -10,6 +10,7 @@ #include "content/browser/frame_host/navigation_request_info.h" #include "content/browser/loader/navigation_resource_handler.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/browser/service_worker/service_worker_navigation_handle_core.h" #include "content/common/navigation_params.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/stream_handle.h" @@ -37,6 +38,7 @@ NavigationURLLoaderImplCore::~NavigationURLLoaderImplCore() { void NavigationURLLoaderImplCore::Start( ResourceContext* resource_context, + ServiceWorkerNavigationHandleCore* service_worker_handle_core, scoped_ptr<NavigationRequestInfo> request_info) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -45,8 +47,11 @@ void NavigationURLLoaderImplCore::Start( base::Bind(&NavigationURLLoaderImpl::NotifyRequestStarted, loader_, base::TimeTicks::Now())); - ResourceDispatcherHostImpl::Get()->BeginNavigationRequest( - resource_context, *request_info, this); + // The ResourceDispatcherHostImpl can be null in unit tests. + if (ResourceDispatcherHostImpl::Get()) { + ResourceDispatcherHostImpl::Get()->BeginNavigationRequest( + resource_context, *request_info, this, service_worker_handle_core); + } } void NavigationURLLoaderImplCore::FollowRedirect() { @@ -56,11 +61,11 @@ void NavigationURLLoaderImplCore::FollowRedirect() { resource_handler_->FollowRedirect(); } - void NavigationURLLoaderImplCore::NotifyRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + TRACE_EVENT_ASYNC_END0("navigation", "Navigation redirectDelay", this); // Make a copy of the ResourceResponse before it is passed to another thread. // @@ -71,12 +76,22 @@ void NavigationURLLoaderImplCore::NotifyRequestRedirected( BrowserThread::UI, FROM_HERE, base::Bind(&NavigationURLLoaderImpl::NotifyRequestRedirected, loader_, redirect_info, response->DeepCopy())); + + // TODO(carlosk): extend this trace to support non-PlzNavigate navigations. + // For the trace below we're using the NavigationURLLoaderImplCore as the + // async trace id and reporting the new redirect URL as a parameter. + TRACE_EVENT_ASYNC_BEGIN2("navigation", "Navigation redirectDelay", this, + "&NavigationURLLoaderImplCore", this, "New URL", + redirect_info.new_url.spec()); } void NavigationURLLoaderImplCore::NotifyResponseStarted( ResourceResponse* response, scoped_ptr<StreamHandle> body) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + TRACE_EVENT_ASYNC_END0("navigation", "Navigation redirectDelay", this); + TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this, + "&NavigationURLLoaderImplCore", this, "success", true); // If, by the time the task reaches the UI thread, |loader_| has already been // destroyed, NotifyResponseStarted will not run. |body| will be destructed @@ -96,6 +111,10 @@ void NavigationURLLoaderImplCore::NotifyResponseStarted( void NavigationURLLoaderImplCore::NotifyRequestFailed(bool in_cache, int net_error) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + TRACE_EVENT_ASYNC_END0("navigation", "Navigation redirectDelay", this); + TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this, + "&NavigationURLLoaderImplCore", this, "success", + false); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, diff --git a/chromium/content/browser/loader/navigation_url_loader_impl_core.h b/chromium/content/browser/loader/navigation_url_loader_impl_core.h index 8a93469c93b..b12629d92f2 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl_core.h +++ b/chromium/content/browser/loader/navigation_url_loader_impl_core.h @@ -5,7 +5,6 @@ #ifndef CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_IMPL_CORE_H_ #define CONTENT_BROWSER_LOADER_NAVIGATION_URL_LOADER_IMPL_CORE_H_ -#include "base/basictypes.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" @@ -23,6 +22,7 @@ class NavigationResourceHandler; class ResourceContext; class ResourceHandler; class ResourceRequestBody; +class ServiceWorkerNavigationHandleCore; class StreamHandle; struct ResourceResponse; @@ -40,6 +40,7 @@ class NavigationURLLoaderImplCore { // Starts the request. void Start(ResourceContext* resource_context, + ServiceWorkerNavigationHandleCore* service_worker_handle_core, scoped_ptr<NavigationRequestInfo> request_info); // Follows the current pending redirect. diff --git a/chromium/content/browser/loader/navigation_url_loader_unittest.cc b/chromium/content/browser/loader/navigation_url_loader_unittest.cc index 0a4e26b6f91..efee272495d 100644 --- a/chromium/content/browser/loader/navigation_url_loader_unittest.cc +++ b/chromium/content/browser/loader/navigation_url_loader_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <utility> + #include "base/command_line.h" #include "base/macros.h" #include "base/memory/ref_counted.h" @@ -34,6 +36,7 @@ #include "net/url_request/url_request_test_job.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" +#include "url/origin.h" namespace content { @@ -109,7 +112,7 @@ class TestNavigationURLLoaderDelegate : public NavigationURLLoaderDelegate { void OnResponseStarted(const scoped_refptr<ResourceResponse>& response, scoped_ptr<StreamHandle> body) override { response_ = response; - body_ = body.Pass(); + body_ = std::move(body); ASSERT_TRUE(response_started_); response_started_->Quit(); } @@ -178,16 +181,17 @@ class NavigationURLLoaderTest : public testing::Test { scoped_ptr<NavigationURLLoader> MakeTestLoader( const GURL& url, NavigationURLLoaderDelegate* delegate) { - BeginNavigationParams begin_params( - "GET", std::string(), net::LOAD_NORMAL, false); + BeginNavigationParams begin_params("GET", std::string(), net::LOAD_NORMAL, + false, false, + REQUEST_CONTEXT_TYPE_LOCATION); CommonNavigationParams common_params; common_params.url = url; - scoped_ptr<NavigationRequestInfo> request_info( - new NavigationRequestInfo(common_params, begin_params, url, true, false, - -1, scoped_refptr<ResourceRequestBody>())); + scoped_ptr<NavigationRequestInfo> request_info(new NavigationRequestInfo( + common_params, begin_params, url, url::Origin(url), true, false, -1, + scoped_refptr<ResourceRequestBody>())); - return NavigationURLLoader::Create(browser_context_.get(), - request_info.Pass(), delegate); + return NavigationURLLoader::Create( + browser_context_.get(), std::move(request_info), nullptr, delegate); } // Helper function for fetching the body of a URL to a string. diff --git a/chromium/content/browser/loader/power_save_block_resource_throttle.cc b/chromium/content/browser/loader/power_save_block_resource_throttle.cc index 42e0b0e46d8..76afe12de13 100644 --- a/chromium/content/browser/loader/power_save_block_resource_throttle.cc +++ b/chromium/content/browser/loader/power_save_block_resource_throttle.cc @@ -14,8 +14,9 @@ const int kPowerSaveBlockDelaySeconds = 30; } // namespace -PowerSaveBlockResourceThrottle::PowerSaveBlockResourceThrottle() { -} +PowerSaveBlockResourceThrottle::PowerSaveBlockResourceThrottle( + const std::string& host) + : host_(host) {} PowerSaveBlockResourceThrottle::~PowerSaveBlockResourceThrottle() { } @@ -41,7 +42,7 @@ const char* PowerSaveBlockResourceThrottle::GetNameForLogging() const { void PowerSaveBlockResourceThrottle::ActivatePowerSaveBlocker() { power_save_blocker_ = PowerSaveBlocker::Create( PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, - PowerSaveBlocker::kReasonOther, "Uploading data"); + PowerSaveBlocker::kReasonOther, "Uploading data to " + host_); } } // namespace content diff --git a/chromium/content/browser/loader/power_save_block_resource_throttle.h b/chromium/content/browser/loader/power_save_block_resource_throttle.h index 0d0c0803bfd..4c678421a05 100644 --- a/chromium/content/browser/loader/power_save_block_resource_throttle.h +++ b/chromium/content/browser/loader/power_save_block_resource_throttle.h @@ -5,8 +5,10 @@ #ifndef CONTENT_BROWSER_LOADER_POWER_SAVE_BLOCK_RESOURCE_THROTTLE_H_ #define CONTENT_BROWSER_LOADER_POWER_SAVE_BLOCK_RESOURCE_THROTTLE_H_ -#include "base/basictypes.h" +#include <string> + #include "base/compiler_specific.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/timer/timer.h" #include "content/public/browser/resource_throttle.h" @@ -18,7 +20,7 @@ class PowerSaveBlocker; // This ResourceThrottle blocks power save until large upload request finishes. class PowerSaveBlockResourceThrottle : public ResourceThrottle { public: - PowerSaveBlockResourceThrottle(); + explicit PowerSaveBlockResourceThrottle(const std::string& host); ~PowerSaveBlockResourceThrottle() override; // ResourceThrottle overrides: @@ -29,6 +31,7 @@ class PowerSaveBlockResourceThrottle : public ResourceThrottle { private: void ActivatePowerSaveBlocker(); + const std::string host_; base::OneShotTimer timer_; scoped_ptr<PowerSaveBlocker> power_save_blocker_; diff --git a/chromium/content/browser/loader/redirect_to_file_resource_handler.cc b/chromium/content/browser/loader/redirect_to_file_resource_handler.cc index 12fd7d38e54..670b3343d30 100644 --- a/chromium/content/browser/loader/redirect_to_file_resource_handler.cc +++ b/chromium/content/browser/loader/redirect_to_file_resource_handler.cc @@ -4,8 +4,11 @@ #include "content/browser/loader/redirect_to_file_resource_handler.h" +#include <utility> + #include "base/bind.h" #include "base/logging.h" +#include "base/macros.h" #include "base/threading/thread_restrictions.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/loader/temporary_file_stream.h" @@ -61,7 +64,7 @@ class RedirectToFileResourceHandler::Writer { scoped_ptr<net::FileStream> file_stream, ShareableFileReference* deletable_file) : handler_(handler), - file_stream_(file_stream.Pass()), + file_stream_(std::move(file_stream)), is_writing_(false), deletable_file_(deletable_file) { DCHECK(!deletable_file_->path().empty()); @@ -129,7 +132,7 @@ class RedirectToFileResourceHandler::Writer { RedirectToFileResourceHandler::RedirectToFileResourceHandler( scoped_ptr<ResourceHandler> next_handler, net::URLRequest* request) - : LayeredResourceHandler(request, next_handler.Pass()), + : LayeredResourceHandler(request, std::move(next_handler)), buf_(new net::GrowableIOBuffer()), buf_write_pending_(false), write_cursor_(0), @@ -137,8 +140,7 @@ RedirectToFileResourceHandler::RedirectToFileResourceHandler( next_buffer_size_(kInitialReadBufSize), did_defer_(false), completed_during_write_(false), - weak_factory_(this) { -} + weak_factory_(this) {} RedirectToFileResourceHandler::~RedirectToFileResourceHandler() { // Orphan the writer to asynchronously close and release the temporary file. @@ -249,7 +251,7 @@ void RedirectToFileResourceHandler::DidCreateTemporaryFile( return; } - writer_ = new Writer(this, file_stream.Pass(), deletable_file); + writer_ = new Writer(this, std::move(file_stream), deletable_file); // Resume the request. DCHECK(did_defer_); diff --git a/chromium/content/browser/loader/redirect_to_file_resource_handler.h b/chromium/content/browser/loader/redirect_to_file_resource_handler.h index 1778921e370..cd4d60dc058 100644 --- a/chromium/content/browser/loader/redirect_to_file_resource_handler.h +++ b/chromium/content/browser/loader/redirect_to_file_resource_handler.h @@ -5,11 +5,11 @@ #ifndef CONTENT_BROWSER_LOADER_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_ -#include "base/basictypes.h" #include "base/callback.h" #include "base/compiler_specific.h" #include "base/files/file.h" #include "base/files/file_path.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" diff --git a/chromium/content/browser/loader/resource_buffer.h b/chromium/content/browser/loader/resource_buffer.h index 0908edbc083..e323bf19a41 100644 --- a/chromium/content/browser/loader/resource_buffer.h +++ b/chromium/content/browser/loader/resource_buffer.h @@ -7,7 +7,7 @@ #include <queue> -#include "base/basictypes.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/shared_memory.h" #include "content/common/content_export.h" diff --git a/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc b/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc index b612d302df5..bad9b869c9f 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc @@ -2,15 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "content/public/browser/resource_dispatcher_host.h" + +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" #include "content/browser/download/download_manager_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/resource_dispatcher_host_delegate.h" #include "content/public/browser/resource_request_info.h" #include "content/public/browser/web_contents.h" @@ -18,6 +26,7 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" #include "content/shell/browser/shell_content_browser_client.h" @@ -28,6 +37,8 @@ #include "net/test/embedded_test_server/http_response.h" #include "net/test/url_request/url_request_failed_job.h" #include "net/test/url_request/url_request_mock_http_job.h" +#include "net/url_request/url_request.h" +#include "url/gurl.h" using base::ASCIIToUTF16; @@ -57,11 +68,6 @@ class ResourceDispatcherHostBrowserTest : public ContentBrowserTest, got_downloads_ = !!manager->InProgressCount(); } - GURL GetMockURL(const std::string& file) { - return net::URLRequestMockHTTPJob::GetMockUrl( - base::FilePath().AppendASCII(file)); - } - void CheckTitleTest(const GURL& url, const std::string& expected_title) { base::string16 expected_title16(ASCIIToUTF16(expected_title)); @@ -98,7 +104,7 @@ class ResourceDispatcherHostBrowserTest : public ContentBrowserTest, // Test title for content created by javascript window.open(). // See http://crbug.com/5988 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DynamicTitle1) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/dynamic1.html")); base::string16 title; @@ -111,7 +117,7 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DynamicTitle1) { // Test title for content created by javascript window.open(). // See http://crbug.com/5988 IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DynamicTitle2) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/dynamic2.html")); base::string16 title; @@ -123,26 +129,29 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DynamicTitle2) { IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, SniffHTMLWithNoContentType) { - CheckTitleTest(GetMockURL("content-sniffer-test0.html"), - "Content Sniffer Test 0"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-sniffer-test0.html"), + "Content Sniffer Test 0"); } IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, RespectNoSniffDirective) { - CheckTitleTest(GetMockURL("nosniff-test.html"), + CheckTitleTest(net::URLRequestMockHTTPJob::GetMockUrl("nosniff-test.html"), "mock.http/nosniff-test.html"); } IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DoNotSniffHTMLFromTextPlain) { - CheckTitleTest(GetMockURL("content-sniffer-test1.html"), - "mock.http/content-sniffer-test1.html"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-sniffer-test1.html"), + "mock.http/content-sniffer-test1.html"); } IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DoNotSniffHTMLFromImageGIF) { - CheckTitleTest(GetMockURL("content-sniffer-test2.html"), - "mock.http/content-sniffer-test2.html"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-sniffer-test2.html"), + "mock.http/content-sniffer-test2.html"); } IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, @@ -150,25 +159,30 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, // Make sure no downloads start. BrowserContext::GetDownloadManager( shell()->web_contents()->GetBrowserContext())->AddObserver(this); - CheckTitleTest(GetMockURL("content-sniffer-test3.html"), - "Content Sniffer Test 3"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-sniffer-test3.html"), + "Content Sniffer Test 3"); EXPECT_EQ(1u, Shell::windows().size()); ASSERT_FALSE(got_downloads()); } IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, ContentDispositionEmpty) { - CheckTitleTest(GetMockURL("content-disposition-empty.html"), "success"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-disposition-empty.html"), + "success"); } IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, ContentDispositionInline) { - CheckTitleTest(GetMockURL("content-disposition-inline.html"), "success"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-disposition-inline.html"), + "success"); } // Test for bug #1091358. IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, SyncXMLHttpRequest) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); NavigateToURL( shell(), embedded_test_server()->GetURL("/sync_xmlhttprequest.html")); @@ -184,7 +198,7 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, SyncXMLHttpRequest) { // If this flakes, use http://crbug.com/62776. IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, SyncXMLHttpRequest_Disallowed) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); NavigateToURL( shell(), embedded_test_server()->GetURL("/sync_xmlhttprequest_disallowed.html")); @@ -210,7 +224,7 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, #endif IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, MAYBE_SyncXMLHttpRequest_DuringUnload) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); BrowserContext::GetDownloadManager( shell()->web_contents()->GetBrowserContext())->AddObserver(this); @@ -230,15 +244,16 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, // Tests that onunload is run for cross-site requests. (Bug 1114994) IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DISABLED_CrossSiteOnunloadCookie) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); GURL url = embedded_test_server()->GetURL("/onunload_cookie.html"); CheckTitleTest(url, "set cookie on unload"); // Navigate to a new cross-site page, to dispatch unload event and set the // cookie. - CheckTitleTest(GetMockURL("content-sniffer-test0.html"), - "Content Sniffer Test 0"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-sniffer-test0.html"), + "Content Sniffer Test 0"); // Check that the cookie was set. EXPECT_EQ("onunloadCookie=foo", GetCookies(url)); @@ -249,7 +264,7 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, // without network loads (e.g., about:blank, data URLs). IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DISABLED_CrossSiteImmediateLoadOnunloadCookie) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); GURL url = embedded_test_server()->GetURL("/onunload_cookie.html"); CheckTitleTest(url, "set cookie on unload"); @@ -275,7 +290,7 @@ scoped_ptr<net::test_server::HttpResponse> NoContentResponseHandler( scoped_ptr<net::test_server::BasicHttpResponse> http_response( new net::test_server::BasicHttpResponse); http_response->set_code(net::HTTP_NO_CONTENT); - return http_response.Pass(); + return std::move(http_response); } } // namespace @@ -284,7 +299,7 @@ scoped_ptr<net::test_server::HttpResponse> NoContentResponseHandler( // If this flakes use http://crbug.com/80596. IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CrossSiteNoUnloadOn204) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); // Start with a URL that sets a cookie in its unload handler. GURL url = embedded_test_server()->GetURL("/onunload_cookie.html"); @@ -328,8 +343,9 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, // Navigate to a new cross-site page. The browser should not wait around for // the old renderer's on{before}unload handlers to run. - CheckTitleTest(GetMockURL("content-sniffer-test0.html"), - "Content Sniffer Test 0"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-sniffer-test0.html"), + "Content Sniffer Test 0"); } // Tests that cross-site navigations work when the new page does not go through @@ -337,8 +353,9 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CrossSiteNavigationNonBuffered) { // Start with an HTTP page. - CheckTitleTest(GetMockURL("content-sniffer-test0.html"), - "Content Sniffer Test 0"); + CheckTitleTest( + net::URLRequestMockHTTPJob::GetMockUrl("content-sniffer-test0.html"), + "Content Sniffer Test 0"); // Now load a file:// page, which does not use the BufferedEventHandler. // Make sure that the page loads and displays a title, and doesn't get stuck. @@ -352,7 +369,7 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, // away from the link doctor page. (Bug 1235537) IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, DISABLED_CrossSiteNavigationErrorPage) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/onunload_cookie.html")); CheckTitleTest(url, "set cookie on unload"); @@ -397,7 +414,7 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CrossSiteNavigationErrorPage2) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/title2.html")); CheckTitleTest(url, "Title Of Awesomeness"); @@ -428,7 +445,8 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, // // If the redirect in #2 were not blocked, we'd also see a request // for http://mock.http:4000/title2.html, and the title would be different. - CheckTitleTest(GetMockURL("cross-origin-redirect-blocked.html"), + CheckTitleTest(net::URLRequestMockHTTPJob::GetMockUrl( + "cross-origin-redirect-blocked.html"), "Title Of More Awesomeness"); } @@ -459,7 +477,7 @@ scoped_ptr<net::test_server::HttpResponse> HandleRedirectRequest( http_response->set_code(net::HTTP_FOUND); http_response->AddCustomHeader( "Location", request.relative_url.substr(request_path.length())); - return http_response.Pass(); + return std::move(http_response); } } // namespace @@ -467,7 +485,7 @@ scoped_ptr<net::test_server::HttpResponse> HandleRedirectRequest( // Test that we update the cookie policy URLs correctly when transferring // navigations. IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CookiePolicy) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); embedded_test_server()->RegisterRequestHandler( base::Bind(&HandleRedirectRequest, "/redirect?")); @@ -511,7 +529,7 @@ class PageTransitionResourceDispatcherHostDelegate // when encountering a meta refresh tag. IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, PageTransitionClientRedirect) { - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + ASSERT_TRUE(embedded_test_server()->Start()); PageTransitionResourceDispatcherHostDelegate delegate( embedded_test_server()->GetURL("/title1.html")); @@ -526,4 +544,215 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, delegate.page_transition() & ui::PAGE_TRANSITION_CLIENT_REDIRECT); } +namespace { + +// Checks whether the given urls are requested, and that IsUsingLofi() returns +// the appropriate value when the Lo-Fi state is set. +class LoFiModeResourceDispatcherHostDelegate + : public ResourceDispatcherHostDelegate { + public: + LoFiModeResourceDispatcherHostDelegate(const GURL& main_frame_url, + const GURL& subresource_url, + const GURL& iframe_url) + : main_frame_url_(main_frame_url), + subresource_url_(subresource_url), + iframe_url_(iframe_url), + main_frame_url_seen_(false), + subresource_url_seen_(false), + iframe_url_seen_(false), + use_lofi_(false), + should_enable_lofi_mode_called_(false) {} + + ~LoFiModeResourceDispatcherHostDelegate() override {} + + // ResourceDispatcherHostDelegate implementation: + void RequestBeginning(net::URLRequest* request, + ResourceContext* resource_context, + AppCacheService* appcache_service, + ResourceType resource_type, + ScopedVector<ResourceThrottle>* throttles) override { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); + if (request->url() != main_frame_url_ && request->url() != subresource_url_ + && request->url() != iframe_url_) + return; + if (request->url() == main_frame_url_) { + EXPECT_FALSE(main_frame_url_seen_); + main_frame_url_seen_ = true; + } else if (request->url() == subresource_url_) { + EXPECT_TRUE(main_frame_url_seen_); + EXPECT_FALSE(subresource_url_seen_); + subresource_url_seen_ = true; + } else if (request->url() == iframe_url_) { + EXPECT_TRUE(main_frame_url_seen_); + EXPECT_FALSE(iframe_url_seen_); + iframe_url_seen_ = true; + } + EXPECT_EQ(use_lofi_, info->IsUsingLoFi()); + } + + void SetDelegate() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + ResourceDispatcherHost::Get()->SetDelegate(this); + } + + bool ShouldEnableLoFiMode( + const net::URLRequest& request, + content::ResourceContext* resource_context) override { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + EXPECT_FALSE(should_enable_lofi_mode_called_); + should_enable_lofi_mode_called_ = true; + EXPECT_EQ(main_frame_url_, request.url()); + return use_lofi_; + } + + void Reset(bool use_lofi) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + main_frame_url_seen_ = false; + subresource_url_seen_ = false; + iframe_url_seen_ = false; + use_lofi_ = use_lofi; + should_enable_lofi_mode_called_ = false; + } + + void CheckResourcesRequested(bool should_enable_lofi_mode_called) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + EXPECT_EQ(should_enable_lofi_mode_called, should_enable_lofi_mode_called_); + EXPECT_TRUE(main_frame_url_seen_); + EXPECT_TRUE(subresource_url_seen_); + EXPECT_TRUE(iframe_url_seen_); + } + + private: + const GURL main_frame_url_; + const GURL subresource_url_; + const GURL iframe_url_; + + bool main_frame_url_seen_; + bool subresource_url_seen_; + bool iframe_url_seen_; + bool use_lofi_; + bool should_enable_lofi_mode_called_; + + DISALLOW_COPY_AND_ASSIGN(LoFiModeResourceDispatcherHostDelegate); +}; + +} // namespace + +class LoFiResourceDispatcherHostBrowserTest : public ContentBrowserTest { + public: + ~LoFiResourceDispatcherHostBrowserTest() override {} + + protected: + void SetUpOnMainThread() override { + ContentBrowserTest::SetUpOnMainThread(); + + ASSERT_TRUE(embedded_test_server()->Start()); + + delegate_.reset(new LoFiModeResourceDispatcherHostDelegate( + embedded_test_server()->GetURL("/page_with_iframe.html"), + embedded_test_server()->GetURL("/image.jpg"), + embedded_test_server()->GetURL("/title1.html"))); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(&LoFiModeResourceDispatcherHostDelegate::SetDelegate, + base::Unretained(delegate_.get()))); + } + + void Reset(bool use_lofi) { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&LoFiModeResourceDispatcherHostDelegate::Reset, + base::Unretained(delegate_.get()), use_lofi)); + } + + void CheckResourcesRequested( + bool should_enable_lofi_mode_called) { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind( + &LoFiModeResourceDispatcherHostDelegate::CheckResourcesRequested, + base::Unretained(delegate_.get()), should_enable_lofi_mode_called)); + } + + private: + scoped_ptr<LoFiModeResourceDispatcherHostDelegate> delegate_; +}; + +// Test that navigating with ShouldEnableLoFiMode returning true fetches the +// resources with LOFI_ON. +IN_PROC_BROWSER_TEST_F(LoFiResourceDispatcherHostBrowserTest, + ShouldEnableLoFiModeOn) { + // Navigate with ShouldEnableLoFiMode returning true. + Reset(true); + NavigateToURLBlockUntilNavigationsComplete( + shell(), embedded_test_server()->GetURL("/page_with_iframe.html"), 1); + CheckResourcesRequested(true); +} + +// Test that navigating with ShouldEnableLoFiMode returning false fetches the +// resources with LOFI_OFF. +IN_PROC_BROWSER_TEST_F(LoFiResourceDispatcherHostBrowserTest, + ShouldEnableLoFiModeOff) { + // Navigate with ShouldEnableLoFiMode returning false. + NavigateToURLBlockUntilNavigationsComplete( + shell(), embedded_test_server()->GetURL("/page_with_iframe.html"), 1); + CheckResourcesRequested(true); +} + +// Test that reloading calls ShouldEnableLoFiMode again and changes the Lo-Fi +// state. +IN_PROC_BROWSER_TEST_F(LoFiResourceDispatcherHostBrowserTest, + ShouldEnableLoFiModeReload) { + // Navigate with ShouldEnableLoFiMode returning false. + NavigateToURLBlockUntilNavigationsComplete( + shell(), embedded_test_server()->GetURL("/page_with_iframe.html"), 1); + CheckResourcesRequested(true); + + // Reload. ShouldEnableLoFiMode should be called. + Reset(true); + ReloadBlockUntilNavigationsComplete(shell(), 1); + CheckResourcesRequested(true); +} + +// Test that navigating backwards calls ShouldEnableLoFiMode again and changes +// the Lo-Fi state. +IN_PROC_BROWSER_TEST_F(LoFiResourceDispatcherHostBrowserTest, + ShouldEnableLoFiModeNavigateBackThenForward) { + // Navigate with ShouldEnableLoFiMode returning false. + NavigateToURLBlockUntilNavigationsComplete( + shell(), embedded_test_server()->GetURL("/page_with_iframe.html"), 1); + CheckResourcesRequested(true); + + // Go to a different page. + NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); + + // Go back with ShouldEnableLoFiMode returning true. + Reset(true); + TestNavigationObserver tab_observer(shell()->web_contents(), 1); + shell()->GoBackOrForward(-1); + tab_observer.Wait(); + CheckResourcesRequested(true); +} + +// Test that reloading with Lo-Fi disabled doesn't call ShouldEnableLoFiMode and +// already has LOFI_OFF. +IN_PROC_BROWSER_TEST_F(LoFiResourceDispatcherHostBrowserTest, + ShouldEnableLoFiModeReloadDisableLoFi) { + // Navigate with ShouldEnableLoFiMode returning true. + Reset(true); + NavigateToURLBlockUntilNavigationsComplete( + shell(), embedded_test_server()->GetURL("/page_with_iframe.html"), 1); + CheckResourcesRequested(true); + + // Reload with Lo-Fi disabled. + Reset(false); + TestNavigationObserver tab_observer(shell()->web_contents(), 1); + shell()->web_contents()->GetController().ReloadDisableLoFi(true); + tab_observer.Wait(); + CheckResourcesRequested(false); +} + } // namespace content diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc index 2d7512b63ae..529b9781866 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc @@ -6,8 +6,10 @@ #include "content/browser/loader/resource_dispatcher_host_impl.h" +#include <stddef.h> #include <algorithm> #include <set> +#include <utility> #include <vector> #include "base/bind.h" @@ -16,13 +18,16 @@ #include "base/compiler_specific.h" #include "base/debug/alias.h" #include "base/logging.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/shared_memory.h" #include "base/message_loop/message_loop.h" +#include "base/metrics/field_trial.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/sparse_histogram.h" #include "base/profiler/scoped_tracker.h" #include "base/stl_util.h" +#include "base/strings/string_util.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/time/time.h" #include "content/browser/appcache/appcache_interceptor.h" @@ -37,6 +42,7 @@ #include "content/browser/frame_host/navigation_request_info.h" #include "content/browser/frame_host/navigator.h" #include "content/browser/loader/async_resource_handler.h" +#include "content/browser/loader/async_revalidation_manager.h" #include "content/browser/loader/cross_site_resource_handler.h" #include "content/browser/loader/detachable_resource_handler.h" #include "content/browser/loader/mime_type_resource_handler.h" @@ -54,6 +60,7 @@ #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/resource_context_impl.h" +#include "content/browser/service_worker/foreign_fetch_request_handler.h" #include "content/browser/service_worker/service_worker_request_handler.h" #include "content/browser/streams/stream.h" #include "content/browser/streams/stream_context.h" @@ -77,6 +84,7 @@ #include "content/public/browser/stream_handle.h" #include "content/public/browser/stream_info.h" #include "content/public/browser/user_metrics.h" +#include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/content_switches.h" #include "content/public/common/process_type.h" #include "ipc/ipc_message_macros.h" @@ -202,6 +210,7 @@ bool IsDetachableResourceType(ResourceType type) { switch (type) { case RESOURCE_TYPE_PREFETCH: case RESOURCE_TYPE_PING: + case RESOURCE_TYPE_CSP_REPORT: return true; default: return false; @@ -429,13 +438,33 @@ void LogResourceRequestTimeOnUI( DCHECK_CURRENTLY_ON(BrowserThread::UI); RenderFrameHostImpl* host = RenderFrameHostImpl::FromID(render_process_id, render_frame_id); - if (host != NULL) { + if (host != nullptr) { DCHECK(host->frame_tree_node()->IsMainFrame()); host->frame_tree_node()->navigator()->LogResourceRequestTime( timestamp, url); } } +bool IsUsingLoFi(LoFiState lofi_state, + ResourceDispatcherHostDelegate* delegate, + const net::URLRequest& request, + ResourceContext* resource_context) { + if (lofi_state == LOFI_UNSPECIFIED && delegate) + return delegate->ShouldEnableLoFiMode(request, resource_context); + return lofi_state == LOFI_ON; +} + +// Record RAPPOR for aborted main frame loads. Separate into a fast and +// slow bucket because a shocking number of aborts happen under 100ms. +void RecordAbortRapporOnUI(const GURL& url, + base::TimeDelta request_loading_time) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (request_loading_time.InMilliseconds() < 100) + GetContentClient()->browser()->RecordURLMetric("Net.ErrAborted.Fast", url); + else + GetContentClient()->browser()->RecordURLMetric("Net.ErrAborted.Slow", url); +} + } // namespace // static @@ -473,6 +502,24 @@ ResourceDispatcherHostImpl::ResourceDispatcherHostImpl() base::Unretained(this))); update_load_states_timer_.reset(new base::RepeatingTimer()); + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + // This needs to be called to mark the trial as active, even if the result + // isn't used. + std::string stale_while_revalidate_trial_group = + base::FieldTrialList::FindFullName("StaleWhileRevalidate"); + // stale-while-revalidate currently doesn't work with browser-side navigation. + // Only enable stale-while-revalidate if browser navigation is not enabled. + // + // TODO(ricea): Make stale-while-revalidate and browser-side navigation work + // together. Or disable stale-while-revalidate completely before browser-side + // navigation becomes the default. crbug.com/561610 + if (!IsBrowserSideNavigationEnabled() && + (base::StartsWith(stale_while_revalidate_trial_group, "Enabled", + base::CompareCase::SENSITIVE) || + command_line->HasSwitch(switches::kEnableStaleWhileRevalidate))) { + async_revalidation_manager_.reset(new AsyncRevalidationManager); + } } ResourceDispatcherHostImpl::~ResourceDispatcherHostImpl() { @@ -578,6 +625,13 @@ void ResourceDispatcherHostImpl::CancelRequestsForContext( loaders_to_cancel.clear(); + if (async_revalidation_manager_) { + // Cancelling async revalidations should not result in the creation of new + // requests. Do it before the CHECKs to ensure this does not happen. + async_revalidation_manager_->CancelAsyncRevalidationsForResourceContext( + context); + } + // Validate that no more requests for this context were added. for (LoaderMap::const_iterator i = pending_loaders_.begin(); i != pending_loaders_.end(); ++i) { @@ -607,7 +661,7 @@ DownloadInterruptReason ResourceDispatcherHostImpl::BeginDownload( bool prefer_cache, bool do_not_prompt_for_login, scoped_ptr<DownloadSaveInfo> save_info, - uint32 download_id, + uint32_t download_id, const DownloadStartedCallback& started_callback) { if (is_shutdown_) return CallbackAndReturn(started_callback, @@ -682,12 +736,11 @@ DownloadInterruptReason ResourceDispatcherHostImpl::BeginDownload( // From this point forward, the |DownloadResourceHandler| is responsible for // |started_callback|. - scoped_ptr<ResourceHandler> handler( - CreateResourceHandlerForDownload(request.get(), is_content_initiated, - true, download_id, save_info.Pass(), - started_callback)); + scoped_ptr<ResourceHandler> handler(CreateResourceHandlerForDownload( + request.get(), is_content_initiated, true, download_id, + std::move(save_info), started_callback)); - BeginRequestInternal(request.Pass(), handler.Pass()); + BeginRequestInternal(std::move(request), std::move(handler)); return DOWNLOAD_INTERRUPT_REASON_NONE; } @@ -715,14 +768,14 @@ ResourceDispatcherHostImpl::CreateResourceHandlerForDownload( net::URLRequest* request, bool is_content_initiated, bool must_download, - uint32 id, + uint32_t id, scoped_ptr<DownloadSaveInfo> save_info, const DownloadUrlParameters::OnStartedCallback& started_cb) { - scoped_ptr<ResourceHandler> handler( - new DownloadResourceHandler(id, request, started_cb, save_info.Pass())); + scoped_ptr<ResourceHandler> handler(new DownloadResourceHandler( + id, request, started_cb, std::move(save_info))); if (delegate_) { - const ResourceRequestInfo* request_info( - ResourceRequestInfo::ForRequest(request)); + const ResourceRequestInfoImpl* request_info( + ResourceRequestInfoImpl::ForRequest(request)); ScopedVector<ResourceThrottle> throttles; delegate_->DownloadStarting( @@ -730,12 +783,11 @@ ResourceDispatcherHostImpl::CreateResourceHandlerForDownload( request_info->GetRouteID(), request_info->GetRequestID(), is_content_initiated, must_download, &throttles); if (!throttles.empty()) { - handler.reset( - new ThrottlingResourceHandler( - handler.Pass(), request, throttles.Pass())); + handler.reset(new ThrottlingResourceHandler(std::move(handler), request, + std::move(throttles))); } } - return handler.Pass(); + return handler; } scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::MaybeInterceptAsStream( @@ -774,8 +826,8 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::MaybeInterceptAsStream( stream_info->response_headers = new net::HttpResponseHeaders(response->head.headers->raw_headers()); } - delegate_->OnStreamCreated(request, stream_info.Pass()); - return handler.Pass(); + delegate_->OnStreamCreated(request, std::move(stream_info)); + return std::move(handler); } ResourceDispatcherHostLoginDelegate* @@ -804,8 +856,8 @@ bool ResourceDispatcherHostImpl::HandleExternalProtocol(ResourceLoader* loader, return false; return delegate_->HandleExternalProtocol( - url, info->GetChildID(), info->GetRouteID(), info->IsMainFrame(), - info->GetPageTransition(), info->HasUserGesture()); + url, info->GetChildID(), info->GetWebContentsGetterForRequest(), + info->IsMainFrame(), info->GetPageTransition(), info->HasUserGesture()); } void ResourceDispatcherHostImpl::DidStartRequest(ResourceLoader* loader) { @@ -826,6 +878,28 @@ void ResourceDispatcherHostImpl::DidReceiveRedirect(ResourceLoader* loader, if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_host)) return; + net::URLRequest* request = loader->request(); + if (request->response_info().async_revalidation_required) { + // Async revalidation is only supported for the first redirect leg. + DCHECK_EQ(request->url_chain().size(), 1u); + DCHECK(async_revalidation_manager_); + + async_revalidation_manager_->BeginAsyncRevalidation(*request, + scheduler_.get()); + } + + // Remove the LOAD_SUPPORT_ASYNC_REVALIDATION flag if it is present. + // It is difficult to create a URLRequest with the correct flags and headers + // for redirect legs other than the first one. Since stale-while-revalidate in + // combination with redirects isn't needed for experimental use, punt on it + // for now. + // TODO(ricea): Fix this before launching the feature. + if (request->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION) { + int new_load_flags = + request->load_flags() & ~net::LOAD_SUPPORT_ASYNC_REVALIDATION; + request->SetLoadFlags(new_load_flags); + } + // Don't notify WebContents observers for requests known to be // downloads; they aren't really associated with the Webcontents. // Note that not all downloads are known before content sniffing. @@ -854,6 +928,12 @@ void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) { info->GetChildID(), info->GetRouteID()); } + if (request->response_info().async_revalidation_required) { + DCHECK(async_revalidation_manager_); + async_revalidation_manager_->BeginAsyncRevalidation(*request, + scheduler_.get()); + } + int render_process_id, render_frame_host; if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_host)) return; @@ -875,7 +955,7 @@ void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) { } void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) { - ResourceRequestInfo* info = loader->GetRequestInfo(); + ResourceRequestInfoImpl* info = loader->GetRequestInfo(); // Record final result of all resource loads. if (info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME) { @@ -895,8 +975,19 @@ void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) { "Net.RequestTime2.Success", request_loading_time); break; case net::ERR_ABORTED: + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.SentBytes", + loader->request()->GetTotalSentBytes(), 1, + 50000000, 50); + UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.ReceivedBytes", + loader->request()->GetTotalReceivedBytes(), + 1, 50000000, 50); UMA_HISTOGRAM_LONG_TIMES( "Net.RequestTime2.ErrAborted", request_loading_time); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&RecordAbortRapporOnUI, loader->request()->url(), + request_loading_time)); break; case net::ERR_CONNECTION_RESET: UMA_HISTOGRAM_LONG_TIMES( @@ -1046,10 +1137,11 @@ void ResourceDispatcherHostImpl::OnRequestResource( "477117 ResourceDispatcherHostImpl::OnRequestResource")); // When logging time-to-network only care about main frame and non-transfer // navigations. + // PlzNavigate: this log happens from NavigationRequest::OnRequestStarted + // instead. if (request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME && request_data.transferred_request_request_id == -1 && - !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)) { + !IsBrowserSideNavigationEnabled()) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -1105,8 +1197,8 @@ void ResourceDispatcherHostImpl::UpdateRequestForTransfer( // ResourceHandlers should always get state related to the request from the // ResourceRequestInfo rather than caching it locally. This lets us update // the info object when a transfer occurs. - info->UpdateForTransfer(child_id, route_id, request_data.origin_pid, - request_id, request_data.parent_render_frame_id, + info->UpdateForTransfer(child_id, route_id, request_data.render_frame_id, + request_data.origin_pid, request_id, filter_->GetWeakPtr()); // Update maps that used the old IDs, if necessary. Some transfers in tests @@ -1163,8 +1255,7 @@ void ResourceDispatcherHostImpl::BeginRequest( int child_id = filter_->child_id(); // PlzNavigate: reject invalid renderer main resource request. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation) && + if (IsBrowserSideNavigationEnabled() && IsResourceTypeFrame(request_data.resource_type) && !request_data.url.SchemeIs(url::kBlobScheme)) { bad_message::ReceivedBadMessage(filter_, bad_message::RDH_INVALID_URL); @@ -1207,7 +1298,8 @@ void ResourceDispatcherHostImpl::BeginRequest( ResourceContext* resource_context = NULL; net::URLRequestContext* request_context = NULL; - filter_->GetContexts(request_data, &resource_context, &request_context); + filter_->GetContexts(request_data.resource_type, request_data.origin_pid, + &resource_context, &request_context); // http://crbug.com/90971 CHECK(ContainsKey(active_resource_contexts_, resource_context)); @@ -1239,6 +1331,7 @@ void ResourceDispatcherHostImpl::BeginRequest( new_request->set_method(request_data.method); new_request->set_first_party_for_cookies( request_data.first_party_for_cookies); + new_request->set_initiator(request_data.request_initiator); // If the request is a MAIN_FRAME request, the first-party URL gets updated on // redirects. @@ -1312,6 +1405,13 @@ void ResourceDispatcherHostImpl::BeginRequest( load_flags |= net::LOAD_DO_NOT_USE_EMBEDDED_IDENTITY; } + bool support_async_revalidation = + !is_sync_load && async_revalidation_manager_ && + AsyncRevalidationManager::QualifiesForAsyncRevalidation(request_data); + + if (support_async_revalidation) + load_flags |= net::LOAD_SUPPORT_ASYNC_REVALIDATION; + // Sync loads should have maximum priority and should be the only // requets that have the ignore limits flag set. if (is_sync_load) { @@ -1331,7 +1431,6 @@ void ResourceDispatcherHostImpl::BeginRequest( request_data.render_frame_id, request_data.is_main_frame, request_data.parent_is_main_frame, - request_data.parent_render_frame_id, request_data.resource_type, request_data.transition_type, request_data.should_replace_current_entry, @@ -1346,7 +1445,10 @@ void ResourceDispatcherHostImpl::BeginRequest( request_data.visiblity_state, resource_context, filter_->GetWeakPtr(), report_raw_headers, - !is_sync_load); + !is_sync_load, + IsUsingLoFi(request_data.lofi_state, delegate_, + *new_request, resource_context), + support_async_revalidation ? request_data.headers : std::string()); // Request takes ownership. extra_info->AssociateWithRequest(new_request.get()); @@ -1361,15 +1463,29 @@ void ResourceDispatcherHostImpl::BeginRequest( // Initialize the service worker handler for the request. We don't use // ServiceWorker for synchronous loads to avoid renderer deadlocks. + const bool should_skip_service_worker = + request_data.skip_service_worker || is_sync_load; ServiceWorkerRequestHandler::InitializeHandler( new_request.get(), filter_->service_worker_context(), blob_context, child_id, request_data.service_worker_provider_id, - request_data.skip_service_worker || is_sync_load, + should_skip_service_worker, request_data.fetch_request_mode, request_data.fetch_credentials_mode, request_data.fetch_redirect_mode, request_data.resource_type, request_data.fetch_request_context_type, request_data.fetch_frame_type, request_data.request_body); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalWebPlatformFeatures)) { + ForeignFetchRequestHandler::InitializeHandler( + new_request.get(), filter_->service_worker_context(), blob_context, + child_id, request_data.service_worker_provider_id, + should_skip_service_worker, + request_data.fetch_request_mode, request_data.fetch_credentials_mode, + request_data.fetch_redirect_mode, request_data.resource_type, + request_data.fetch_request_context_type, request_data.fetch_frame_type, + request_data.request_body); + } + // Have the appcache associate its extra info with the request. AppCacheInterceptor::SetExtraRequestInfo( new_request.get(), filter_->appcache_service(), child_id, @@ -1383,7 +1499,7 @@ void ResourceDispatcherHostImpl::BeginRequest( resource_context)); if (handler) - BeginRequestInternal(new_request.Pass(), handler.Pass()); + BeginRequestInternal(std::move(new_request), std::move(handler)); } scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler( @@ -1414,7 +1530,7 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler( // The RedirectToFileResourceHandler depends on being next in the chain. if (request_data.download_to_file) { handler.reset( - new RedirectToFileResourceHandler(handler.Pass(), request)); + new RedirectToFileResourceHandler(std::move(handler), request)); } } @@ -1423,15 +1539,14 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler( handler.reset(new DetachableResourceHandler( request, base::TimeDelta::FromMilliseconds(kDefaultDetachableCancelDelayMs), - handler.Pass())); + std::move(handler))); } // PlzNavigate: If using --enable-browser-side-navigation, the // CrossSiteResourceHandler is not needed. This codepath is not used for the // actual navigation request, but only the subsequent blob URL load. This does // not require request transfers. - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)) { + if (!IsBrowserSideNavigationEnabled()) { // Install a CrossSiteResourceHandler for all main frame requests. This will // check whether a transfer is required and, if so, pause for the UI thread // to drive the transfer. @@ -1446,12 +1561,12 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler( request_data.resource_type == RESOURCE_TYPE_SUB_FRAME; } if (is_swappable_navigation && process_type == PROCESS_TYPE_RENDERER) - handler.reset(new CrossSiteResourceHandler(handler.Pass(), request)); + handler.reset(new CrossSiteResourceHandler(std::move(handler), request)); } return AddStandardHandlers(request, request_data.resource_type, resource_context, filter_->appcache_service(), - child_id, route_id, handler.Pass()); + child_id, route_id, std::move(handler)); } scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers( @@ -1465,11 +1580,10 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers( // PlzNavigate: do not add ResourceThrottles for main resource requests from // the renderer. Decisions about the navigation should have been done in the // initial request. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation) && - IsResourceTypeFrame(resource_type) && child_id != -1) { + if (IsBrowserSideNavigationEnabled() && IsResourceTypeFrame(resource_type) && + child_id != -1) { DCHECK(request->url().SchemeIs(url::kBlobScheme)); - return handler.Pass(); + return handler; } PluginService* plugin_service = nullptr; @@ -1477,7 +1591,7 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers( plugin_service = PluginService::GetInstance(); #endif // Insert a buffered event handler before the actual one. - handler.reset(new MimeTypeResourceHandler(handler.Pass(), this, + handler.reset(new MimeTypeResourceHandler(std::move(handler), this, plugin_service, request)); ScopedVector<ResourceThrottle> throttles; @@ -1485,11 +1599,8 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers( // Add a NavigationResourceThrottle for navigations. // PlzNavigate: the throttle is unnecessary as communication with the UI // thread is handled by the NavigationURLloader. - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation) && - IsResourceTypeFrame(resource_type)) { + if (!IsBrowserSideNavigationEnabled() && IsResourceTypeFrame(resource_type)) throttles.push_back(new NavigationResourceThrottle(request)); - } if (delegate_) { delegate_->RequestBeginning(request, @@ -1501,7 +1612,8 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers( if (request->has_upload()) { // Block power save while uploading data. - throttles.push_back(new PowerSaveBlockResourceThrottle()); + throttles.push_back( + new PowerSaveBlockResourceThrottle(request->url().host())); } // TODO(ricea): Stop looking this up so much. @@ -1509,10 +1621,10 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers( throttles.push_back(scheduler_->ScheduleRequest(child_id, route_id, info->IsAsync(), request)); - handler.reset( - new ThrottlingResourceHandler(handler.Pass(), request, throttles.Pass())); + handler.reset(new ThrottlingResourceHandler(std::move(handler), request, + std::move(throttles))); - return handler.Pass(); + return handler; } void ResourceDispatcherHostImpl::OnReleaseDownloadedFile(int request_id) { @@ -1616,7 +1728,6 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( render_frame_route_id, false, // is_main_frame false, // parent_is_main_frame - -1, // parent_render_frame_id RESOURCE_TYPE_SUB_RESOURCE, ui::PAGE_TRANSITION_LINK, false, // should_replace_current_entry @@ -1632,7 +1743,9 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( context, base::WeakPtr<ResourceMessageFilter>(), // filter false, // report_raw_headers - true); // is_async + true, // is_async + false, // is_using_lofi + std::string()); // original_headers } void ResourceDispatcherHostImpl::OnRenderViewHostCreated(int child_id, @@ -1679,13 +1792,14 @@ void ResourceDispatcherHostImpl::OnAudioRenderHostStreamStateChanged( } // This function is only used for saving feature. -void ResourceDispatcherHostImpl::BeginSaveFile( - const GURL& url, - const Referrer& referrer, - int child_id, - int render_view_route_id, - int render_frame_route_id, - ResourceContext* context) { +void ResourceDispatcherHostImpl::BeginSaveFile(const GURL& url, + const Referrer& referrer, + SaveItemId save_item_id, + SavePackageId save_package_id, + int child_id, + int render_view_route_id, + int render_frame_route_id, + ResourceContext* context) { if (is_shutdown_) return; @@ -1723,14 +1837,11 @@ void ResourceDispatcherHostImpl::BeginSaveFile( render_frame_route_id, false, context); extra_info->AssociateWithRequest(request.get()); // Request takes ownership. - scoped_ptr<ResourceHandler> handler( - new SaveFileResourceHandler(request.get(), - child_id, - render_frame_route_id, - url, - save_file_manager_.get())); + scoped_ptr<ResourceHandler> handler(new SaveFileResourceHandler( + request.get(), save_item_id, save_package_id, child_id, + render_frame_route_id, url, save_file_manager_.get())); - BeginRequestInternal(request.Pass(), handler.Pass()); + BeginRequestInternal(std::move(request), std::move(handler)); } void ResourceDispatcherHostImpl::MarkAsTransferredNavigation( @@ -1957,11 +2068,11 @@ void ResourceDispatcherHostImpl::FinishedWithResourcesForRequest( void ResourceDispatcherHostImpl::BeginNavigationRequest( ResourceContext* resource_context, const NavigationRequestInfo& info, - NavigationURLLoaderImplCore* loader) { + NavigationURLLoaderImplCore* loader, + ServiceWorkerNavigationHandleCore* service_worker_handle_core) { // PlzNavigate: BeginNavigationRequest currently should only be used for the // browser-side navigations project. - CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)); + CHECK(IsBrowserSideNavigationEnabled()); ResourceType resource_type = info.is_main_frame ? RESOURCE_TYPE_MAIN_FRAME : RESOURCE_TYPE_SUB_FRAME; @@ -2009,6 +2120,7 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( new_request->set_method(info.begin_params.method); new_request->set_first_party_for_cookies( info.first_party_for_cookies); + new_request->set_initiator(info.request_initiator); if (info.is_main_frame) { new_request->set_first_party_url_policy( net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); @@ -2022,10 +2134,11 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( new_request->SetLoadFlags(load_flags); + storage::BlobStorageContext* blob_context = GetBlobStorageContext( + GetChromeBlobStorageContextForResourceContext(resource_context)); + // Resolve elements from request_body and prepare upload data. if (info.request_body.get()) { - storage::BlobStorageContext* blob_context = GetBlobStorageContext( - GetChromeBlobStorageContextForResourceContext(resource_context)); AttachRequestBodyBlobDataHandles( info.request_body.get(), blob_context); @@ -2054,18 +2167,14 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( -1, // request_data.origin_pid, request_id_, -1, // request_data.render_frame_id, - info.is_main_frame, - info.parent_is_main_frame, - -1, // request_data.parent_render_frame_id, - resource_type, - info.common_params.transition, + info.is_main_frame, info.parent_is_main_frame, + resource_type, info.common_params.transition, // should_replace_current_entry. This was only maintained at layer for // request transfers and isn't needed for browser-side navigations. false, false, // is download false, // is stream - info.common_params.allow_download, - info.begin_params.has_user_gesture, + info.common_params.allow_download, info.begin_params.has_user_gesture, true, // enable_load_timing false, // enable_upload_progress false, // do_not_prompt_for_login @@ -2073,26 +2182,37 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( // TODO(davidben): This is only used for prerenders. Replace // is_showing with something for that. Or maybe it just comes from the // same mechanism as the cookie one. - blink::WebPageVisibilityStateVisible, - resource_context, + blink::WebPageVisibilityStateVisible, resource_context, base::WeakPtr<ResourceMessageFilter>(), // filter false, // request_data.report_raw_headers - true); + true, // is_async + IsUsingLoFi(info.common_params.lofi_state, delegate_, + *new_request, resource_context), + // The original_headers field is for stale-while-revalidate but the + // feature doesn't work with PlzNavigate, so it's just a placeholder + // here. + // TODO(ricea): Make the feature work with stale-while-revalidate + // and clean this up. + std::string()); // original_headers // Request takes ownership. extra_info->AssociateWithRequest(new_request.get()); if (new_request->url().SchemeIs(url::kBlobScheme)) { // Hang on to a reference to ensure the blob is not released prior // to the job being started. - ChromeBlobStorageContext* blob_context = - GetChromeBlobStorageContextForResourceContext(resource_context); storage::BlobProtocolHandler::SetRequestedBlobDataHandle( new_request.get(), - blob_context->context()->GetBlobDataFromPublicURL(new_request->url())); + blob_context->GetBlobDataFromPublicURL(new_request->url())); } - // TODO(davidben): Attach ServiceWorkerRequestHandler. - // TODO(michaeln): Help out with this and that. + RequestContextFrameType frame_type = + info.is_main_frame ? REQUEST_CONTEXT_FRAME_TYPE_TOP_LEVEL + : REQUEST_CONTEXT_FRAME_TYPE_NESTED; + ServiceWorkerRequestHandler::InitializeForNavigation( + new_request.get(), service_worker_handle_core, blob_context, + info.begin_params.skip_service_worker, resource_type, + info.begin_params.request_context_type, frame_type, info.request_body); + // TODO(davidben): Attach AppCacheInterceptor. scoped_ptr<ResourceHandler> handler(new NavigationResourceHandler( @@ -2101,14 +2221,19 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( // TODO(davidben): Pass in the appropriate appcache_service. Also fix the // dependency on child_id/route_id. Those are used by the ResourceScheduler; // currently it's a no-op. - handler = AddStandardHandlers(new_request.get(), resource_type, - resource_context, - nullptr, // appcache_service - -1, // child_id - -1, // route_id - handler.Pass()); + handler = + AddStandardHandlers(new_request.get(), resource_type, resource_context, + nullptr, // appcache_service + -1, // child_id + -1, // route_id + std::move(handler)); + + BeginRequestInternal(std::move(new_request), std::move(handler)); +} - BeginRequestInternal(new_request.Pass(), handler.Pass()); +void ResourceDispatcherHostImpl::EnableStaleWhileRevalidateForTesting() { + if (!async_revalidation_manager_) + async_revalidation_manager_.reset(new AsyncRevalidationManager); } // static @@ -2166,7 +2291,7 @@ void ResourceDispatcherHostImpl::BeginRequestInternal( } linked_ptr<ResourceLoader> loader( - new ResourceLoader(request.Pass(), handler.Pass(), this)); + new ResourceLoader(std::move(request), std::move(handler), this)); GlobalRoutingID id(info->GetGlobalRoutingID()); BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.find(id); @@ -2210,11 +2335,11 @@ bool ResourceDispatcherHostImpl::LoadInfoIsMoreInteresting(const LoadInfo& a, // Set |*_uploading_size| to be the size of the corresponding upload body if // it's currently being uploaded. - uint64 a_uploading_size = 0; + uint64_t a_uploading_size = 0; if (a.load_state.state == net::LOAD_STATE_SENDING_REQUEST) a_uploading_size = a.upload_size; - uint64 b_uploading_size = 0; + uint64_t b_uploading_size = 0; if (b.load_state.state == net::LOAD_STATE_SENDING_REQUEST) b_uploading_size = b.upload_size; @@ -2269,7 +2394,7 @@ ResourceDispatcherHostImpl::GetLoadInfoForAllRoutes() { (*info_map)[id] = load_info; } } - return info_map.Pass(); + return info_map; } void ResourceDispatcherHostImpl::UpdateLoadInfo() { diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.h b/chromium/content/browser/loader/resource_dispatcher_host_impl.h index 7772917990c..74922647f29 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.h +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.h @@ -12,19 +12,22 @@ #ifndef CONTENT_BROWSER_LOADER_RESOURCE_DISPATCHER_HOST_IMPL_H_ #define CONTENT_BROWSER_LOADER_RESOURCE_DISPATCHER_HOST_IMPL_H_ +#include <stdint.h> + #include <map> #include <set> #include <string> #include <vector> -#include "base/basictypes.h" #include "base/gtest_prod_util.h" +#include "base/macros.h" #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "content/browser/download/download_resource_handler.h" +#include "content/browser/download/save_types.h" #include "content/browser/loader/global_routing_id.h" #include "content/browser/loader/resource_loader.h" #include "content/browser/loader/resource_loader_delegate.h" @@ -60,6 +63,7 @@ class ShareableFileReference; namespace content { class AppCacheService; +class AsyncRevalidationManager; class NavigationURLLoaderImplCore; class ResourceContext; class ResourceDispatcherHostDelegate; @@ -67,6 +71,7 @@ class ResourceMessageDelegate; class ResourceMessageFilter; class ResourceRequestInfoImpl; class SaveFileManager; +class ServiceWorkerNavigationHandleCore; class WebContentsImpl; struct CommonNavigationParams; struct DownloadSaveInfo; @@ -98,7 +103,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl bool prefer_cache, bool do_not_prompt_for_login, scoped_ptr<DownloadSaveInfo> save_info, - uint32 download_id, + uint32_t download_id, const DownloadStartedCallback& started_callback) override; void ClearLoginDelegateForRequest(net::URLRequest* request) override; void BlockRequestsForRoute(int child_id, int route_id) override; @@ -127,6 +132,8 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl // request from the renderer or another child process). void BeginSaveFile(const GURL& url, const Referrer& referrer, + SaveItemId save_item_id, + SavePackageId save_package_id, int child_id, int render_view_route_id, int render_frame_route_id, @@ -236,7 +243,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl net::URLRequest* request, bool is_content_initiated, bool must_download, - uint32 id, + uint32_t id, scoped_ptr<DownloadSaveInfo> save_info, const DownloadUrlParameters::OnStartedCallback& started_cb); @@ -273,9 +280,15 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl // PlzNavigate: Begins a request for NavigationURLLoader. |loader| is the // loader to attach to the leaf resource handler. - void BeginNavigationRequest(ResourceContext* resource_context, - const NavigationRequestInfo& info, - NavigationURLLoaderImplCore* loader); + void BeginNavigationRequest( + ResourceContext* resource_context, + const NavigationRequestInfo& info, + NavigationURLLoaderImplCore* loader, + ServiceWorkerNavigationHandleCore* service_worker_handle_core); + + // Turns on stale-while-revalidate support, regardless of command-line flags + // or experiment status. For unit tests only. + void EnableStaleWhileRevalidateForTesting(); private: friend class ResourceDispatcherHostTest; @@ -301,8 +314,8 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl struct LoadInfo { GURL url; net::LoadStateWithParam load_state; - uint64 upload_position; - uint64 upload_size; + uint64_t upload_position; + uint64_t upload_size; }; // Map from ProcessID+RouteID pair to the "most interesting" LoadState. @@ -585,6 +598,10 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl bool allow_cross_origin_auth_prompt_; + // AsyncRevalidationManager is non-NULL if and only if + // stale-while-revalidate is enabled. + scoped_ptr<AsyncRevalidationManager> async_revalidation_manager_; + // http://crbug.com/90971 - Assists in tracking down use-after-frees on // shutdown. std::set<const ResourceContext*> active_resource_contexts_; diff --git a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc index da62f4a89ad..7fbe3995c21 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stddef.h> +#include <utility> #include <vector> -#include "base/basictypes.h" #include "base/bind.h" -#include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/location.h" +#include "base/macros.h" #include "base/memory/scoped_vector.h" #include "base/memory/shared_memory.h" #include "base/pickle.h" @@ -38,8 +39,8 @@ #include "content/public/browser/resource_throttle.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/child_process_host.h" -#include "content/public/common/content_switches.h" #include "content/public/common/process_type.h" #include "content/public/common/resource_response.h" #include "content/public/test/test_browser_context.h" @@ -51,6 +52,7 @@ #include "net/base/request_priority.h" #include "net/base/upload_bytes_element_reader.h" #include "net/http/http_util.h" +#include "net/test/url_request/url_request_failed_job.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_job.h" @@ -151,7 +153,6 @@ static ResourceHostMsg_Request CreateResourceRequest(const char* method, request.should_reset_appcache = false; request.is_main_frame = true; request.parent_is_main_frame = false; - request.parent_render_frame_id = -1; request.transition_type = ui::PAGE_TRANSITION_LINK; request.allow_download = true; return request; @@ -181,6 +182,7 @@ class ResourceIPCAccumulator { // within the groups will be in the order that they appeared. // Note that this clears messages_. The caller takes ownership of any // SharedMemoryHandles in messages placed into |msgs|. + // TODO(mmenke): This seems really fragile. Consider reworking ownership. typedef std::vector< std::vector<IPC::Message> > ClassifiedMessages; void GetClassifiedMessages(ClassifiedMessages* msgs); @@ -253,7 +255,8 @@ class TestFilterSpecifyingChild : public ResourceMessageFilter { ~TestFilterSpecifyingChild() override {} private: - void GetContexts(const ResourceHostMsg_Request& request, + void GetContexts(ResourceType resource_type, + int origin_pid, ResourceContext** resource_context, net::URLRequestContext** request_context) { *resource_context = resource_context_; @@ -340,12 +343,6 @@ class URLRequestTestDelayedStartJob : public net::URLRequestTestJob { } URLRequestTestDelayedStartJob(net::URLRequest* request, net::NetworkDelegate* network_delegate, - bool auto_advance) - : net::URLRequestTestJob(request, network_delegate, auto_advance) { - Init(); - } - URLRequestTestDelayedStartJob(net::URLRequest* request, - net::NetworkDelegate* network_delegate, const std::string& response_headers, const std::string& response_data, bool auto_advance) @@ -534,6 +531,7 @@ class TestURLRequestJobFactory : public net::URLRequestJobFactory { public: explicit TestURLRequestJobFactory(ResourceDispatcherHostTest* test_fixture) : test_fixture_(test_fixture), + hang_after_start_(false), delay_start_(false), delay_complete_(false), network_start_notification_(false), @@ -548,6 +546,11 @@ class TestURLRequestJobFactory : public net::URLRequestJobFactory { return url_request_jobs_created_count_; } + // When set, jobs will hang eternally once started. + void SetHangAfterStartJobGeneration(bool hang_after_start) { + hang_after_start_ = hang_after_start; + } + void SetDelayedStartJobGeneration(bool delay_job_start) { delay_start_ = delay_job_start; } @@ -588,6 +591,7 @@ class TestURLRequestJobFactory : public net::URLRequestJobFactory { private: ResourceDispatcherHostTest* test_fixture_; + bool hang_after_start_; bool delay_start_; bool delay_complete_; bool network_start_notification_; @@ -945,6 +949,9 @@ class ResourceDispatcherHostTest : public testing::Test, ResourceType type); void MakeWebContentsAssociatedTestRequest(int request_id, const GURL& url); + void MakeWebContentsAssociatedTestRequestWithResourceType(int request_id, + const GURL& url, + ResourceType type); // Generates a request with the given priority. void MakeTestRequestWithPriority(int render_view_id, @@ -974,11 +981,10 @@ class ResourceDispatcherHostTest : public testing::Test, } // Sets a particular response for any request from now on. To switch back to - // the default bahavior, pass an empty |headers|. |headers| should be raw- - // formatted (NULLs instead of EOLs). + // the default bahavior, pass an empty |headers|. |headers| should be CR[LF] + // terminated. void SetResponse(const std::string& headers, const std::string& data) { - response_headers_ = net::HttpUtil::AssembleRawHeaders(headers.data(), - headers.size()); + response_headers_ = headers; response_data_ = data; } void SetResponse(const std::string& headers) { @@ -1070,8 +1076,15 @@ void ResourceDispatcherHostTest::MakeTestRequestWithResourceType( void ResourceDispatcherHostTest::MakeWebContentsAssociatedTestRequest( int request_id, const GURL& url) { - ResourceHostMsg_Request request = - CreateResourceRequest("GET", RESOURCE_TYPE_SUB_RESOURCE, url); + MakeWebContentsAssociatedTestRequestWithResourceType( + request_id, url, RESOURCE_TYPE_SUB_RESOURCE); +} + +void ResourceDispatcherHostTest:: + MakeWebContentsAssociatedTestRequestWithResourceType(int request_id, + const GURL& url, + ResourceType type) { + ResourceHostMsg_Request request = CreateResourceRequest("GET", type, url); request.origin_pid = web_contents_->GetRenderProcessHost()->GetID(); request.render_frame_id = web_contents_->GetMainFrame()->GetRoutingID(); ResourceHostMsg_RequestResource msg(web_contents_->GetRoutingID(), request_id, @@ -1100,19 +1113,14 @@ void ResourceDispatcherHostTest::MakeWebContentsAssociatedDownloadRequest( browser_context_->GetResourceContext()->GetRequestContext(); scoped_ptr<net::URLRequest> request( request_context->CreateRequest(url, net::DEFAULT_PRIORITY, NULL)); - host_.BeginDownload( - request.Pass(), - Referrer(), - false, // is_content_initiated - browser_context_->GetResourceContext(), - web_contents_->GetRenderProcessHost()->GetID(), - web_contents_->GetRoutingID(), - web_contents_->GetMainFrame()->GetRoutingID(), - false, - false, - save_info.Pass(), - DownloadItem::kInvalidId, - ResourceDispatcherHostImpl::DownloadStartedCallback()); + host_.BeginDownload(std::move(request), Referrer(), + false, // is_content_initiated + browser_context_->GetResourceContext(), + web_contents_->GetRenderProcessHost()->GetID(), + web_contents_->GetRoutingID(), + web_contents_->GetMainFrame()->GetRoutingID(), false, + false, std::move(save_info), DownloadItem::kInvalidId, + ResourceDispatcherHostImpl::DownloadStartedCallback()); } void ResourceDispatcherHostTest::CancelRequest(int request_id) { @@ -1669,9 +1677,6 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) { child_ids_.insert(test_filter->child_id()); // request 1 goes to the test delegate - ResourceHostMsg_Request request = CreateResourceRequest( - "GET", RESOURCE_TYPE_SUB_RESOURCE, net::URLRequestTestJob::test_url_1()); - MakeTestRequestWithResourceType(test_filter.get(), 0, 1, net::URLRequestTestJob::test_url_1(), RESOURCE_TYPE_SUB_RESOURCE); @@ -1741,6 +1746,55 @@ TEST_F(ResourceDispatcherHostTest, TestProcessCancel) { EXPECT_EQ(0, network_delegate()->error_count()); } +// Tests whether the correct requests get canceled when a RenderViewHost is +// deleted. +TEST_F(ResourceDispatcherHostTest, CancelRequestsOnRenderViewHostDeleted) { + // Requests all hang once started. This prevents requests from being + // destroyed due to completion. + job_factory_->SetHangAfterStartJobGeneration(true); + HandleScheme("http"); + + TestResourceDispatcherHostDelegate delegate; + host_.SetDelegate(&delegate); + host_.OnRenderViewHostCreated(filter_->child_id(), 0, true, false); + + // One RenderView issues a high priority request and a low priority one. Both + // should be started. + MakeTestRequestWithPriority(0, 1, net::HIGHEST); + MakeTestRequestWithPriority(0, 2, net::LOWEST); + KickOffRequest(); + EXPECT_EQ(2, network_delegate_.created_requests()); + EXPECT_EQ(0, network_delegate_.canceled_requests()); + + // The same RenderView issues two more low priority requests. The + // ResourceScheduler shouldn't let them start immediately. + MakeTestRequestWithPriority(0, 3, net::LOWEST); + MakeTestRequestWithPriority(0, 4, net::LOWEST); + KickOffRequest(); + EXPECT_EQ(2, network_delegate_.created_requests()); + EXPECT_EQ(0, network_delegate_.canceled_requests()); + + // Another RenderView in the same process as the old one issues a request, + // which is then started. + MakeTestRequestWithPriority(1, 5, net::LOWEST); + KickOffRequest(); + EXPECT_EQ(3, network_delegate_.created_requests()); + EXPECT_EQ(0, network_delegate_.canceled_requests()); + + // The first RenderView is destroyed. All 4 of its requests should be + // cancelled, and none of the two deferred requests should be started. + host_.OnRenderViewHostDeleted(filter_->child_id(), 0); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(3, network_delegate_.created_requests()); + EXPECT_EQ(4, network_delegate_.canceled_requests()); + + // No messages should have been sent, since none of the jobs made any + // progress. + ResourceIPCAccumulator::ClassifiedMessages msgs; + accum_.GetClassifiedMessages(&msgs); + EXPECT_EQ(0U, msgs.size()); +} + TEST_F(ResourceDispatcherHostTest, TestProcessCancelDetachedTimesOut) { MakeTestRequestWithResourceType(filter_.get(), 0, 1, net::URLRequestTestJob::test_url_4(), @@ -1989,7 +2043,7 @@ TEST_F(ResourceDispatcherHostTest, CalculateApproximateMemoryCost) { scoped_ptr<net::UploadElementReader> reader(new net::UploadBytesElementReader( upload_content.data(), upload_content.size())); req->set_upload( - net::ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0)); + net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); // Since the upload throttling is disabled, this has no effect on the cost. EXPECT_EQ( @@ -2389,7 +2443,6 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextDetached) { TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) { EXPECT_EQ(0, host_.pending_requests()); - int render_view_id = 0; int request_id = 1; std::string raw_headers("HTTP/1.1 200 OK\n" @@ -2399,17 +2452,16 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) { SetResponse(raw_headers, response_data); HandleScheme("http"); - MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id, - GURL("http://example.com/blah"), - RESOURCE_TYPE_MAIN_FRAME); - + MakeWebContentsAssociatedTestRequestWithResourceType( + request_id, GURL("http://example.com/blah"), RESOURCE_TYPE_MAIN_FRAME); - GlobalRequestID global_request_id(filter_->child_id(), request_id); + GlobalRequestID global_request_id(web_contents_filter_->child_id(), + request_id); host_.MarkAsTransferredNavigation(global_request_id); // And now simulate a cancellation coming from the renderer. ResourceHostMsg_CancelRequest msg(request_id); - host_.OnMessageReceived(msg, filter_.get()); + host_.OnMessageReceived(msg, web_contents_filter_.get()); // Since the request is marked as being transferred, // the cancellation above should have been ignored and the request @@ -2417,19 +2469,18 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) { EXPECT_EQ(1, host_.pending_requests()); // Cancelling by other methods shouldn't work either. - host_.CancelRequestsForProcess(render_view_id); + host_.CancelRequestsForProcess(web_contents_->GetRoutingID()); EXPECT_EQ(1, host_.pending_requests()); // Cancelling by context should work. - host_.CancelRequestsForContext(filter_->resource_context()); + host_.CancelRequestsForContext(web_contents_filter_->resource_context()); EXPECT_EQ(0, host_.pending_requests()); } // Test transferred navigations with text/html, which doesn't trigger any // content sniffing. TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)) { + if (IsBrowserSideNavigationEnabled()) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } @@ -2503,8 +2554,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) { // Test transferring two navigations with text/html, to ensure the resource // accounting works. TEST_F(ResourceDispatcherHostTest, TransferTwoNavigationsHtml) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)) { + if (IsBrowserSideNavigationEnabled()) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } @@ -2591,8 +2641,7 @@ TEST_F(ResourceDispatcherHostTest, TransferTwoNavigationsHtml) { // MimeTypeResourceHandler to buffer the response to sniff the content before // the transfer occurs. TEST_F(ResourceDispatcherHostTest, TransferNavigationText) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)) { + if (IsBrowserSideNavigationEnabled()) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } @@ -2666,8 +2715,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationText) { } TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)) { + if (IsBrowserSideNavigationEnabled()) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } @@ -2757,8 +2805,7 @@ TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) { } TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableBrowserSideNavigation)) { + if (IsBrowserSideNavigationEnabled()) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } @@ -3444,10 +3491,14 @@ net::URLRequestJob* TestURLRequestJobFactory::MaybeCreateJobWithProtocolHandler( if (test_fixture_->loader_test_request_info_) { DCHECK_EQ(test_fixture_->loader_test_request_info_->url, request->url()); scoped_ptr<LoadInfoTestRequestInfo> info = - test_fixture_->loader_test_request_info_.Pass(); + std::move(test_fixture_->loader_test_request_info_); return new URLRequestLoadInfoJob(request, network_delegate, info->load_state, info->upload_progress); } + if (hang_after_start_) { + return new net::URLRequestFailedJob(request, network_delegate, + net::ERR_IO_PENDING); + } if (test_fixture_->response_headers_.empty()) { if (delay_start_) { return new URLRequestTestDelayedStartJob(request, network_delegate); diff --git a/chromium/content/browser/loader/resource_loader.cc b/chromium/content/browser/loader/resource_loader.cc index 64f5e7b341b..eee044c6aaf 100644 --- a/chromium/content/browser/loader/resource_loader.cc +++ b/chromium/content/browser/loader/resource_loader.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/resource_loader.h" +#include <utility> + #include "base/command_line.h" #include "base/location.h" #include "base/metrics/histogram.h" @@ -35,6 +37,8 @@ #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" #include "net/ssl/client_cert_store.h" +#include "net/ssl/ssl_platform_key.h" +#include "net/ssl/ssl_private_key.h" #include "net/url_request/redirect_info.h" #include "net/url_request/url_request_status.h" @@ -94,6 +98,10 @@ void PopulateResourceResponse(ResourceRequestInfoImpl* info, response->head.was_fetched_via_proxy = request->was_fetched_via_proxy(); response->head.proxy_server = response_info.proxy_server; response->head.socket_address = request->GetSocketAddress(); + const content::ResourceRequestInfo* request_info = + content::ResourceRequestInfo::ForRequest(request); + if (request_info) + response->head.is_using_lofi = request_info->IsUsingLoFi(); if (ServiceWorkerRequestHandler* handler = ServiceWorkerRequestHandler::GetHandler(request)) { handler->GetExtraResponseInfo(&response->head); @@ -109,6 +117,9 @@ void PopulateResourceResponse(ResourceRequestInfoImpl* info, GetSSLStatusForRequest(request->url(), request->ssl_info(), info->GetChildID(), &ssl_status); response->head.security_info = SerializeSecurityInfo(ssl_status); + response->head.has_major_certificate_errors = + net::IsCertStatusError(ssl_status.cert_status) && + !net::IsCertStatusMinorError(ssl_status.cert_status); } else { // We should not have any SSL state. DCHECK(!request->ssl_info().cert_status); @@ -124,8 +135,8 @@ ResourceLoader::ResourceLoader(scoped_ptr<net::URLRequest> request, scoped_ptr<ResourceHandler> handler, ResourceLoaderDelegate* delegate) : deferred_stage_(DEFERRED_NONE), - request_(request.Pass()), - handler_(handler.Pass()), + request_(std::move(request)), + handler_(std::move(handler)), delegate_(delegate), is_transferring_(false), times_cancelled_before_request_start_(0), @@ -306,19 +317,9 @@ void ResourceLoader::OnSSLCertificateError(net::URLRequest* request, bool fatal) { ResourceRequestInfoImpl* info = GetRequestInfo(); - int render_process_id; - int render_frame_id; - if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_id)) - NOTREACHED(); - SSLManager::OnSSLCertificateError( - weak_ptr_factory_.GetWeakPtr(), - info->GetResourceType(), - request_->url(), - render_process_id, - render_frame_id, - ssl_info, - fatal); + weak_ptr_factory_.GetWeakPtr(), info->GetResourceType(), request_->url(), + info->GetWebContentsGetterForRequest(), ssl_info, fatal); } void ResourceLoader::OnBeforeNetworkStart(net::URLRequest* unused, @@ -419,7 +420,13 @@ void ResourceLoader::ContinueSSLRequest() { void ResourceLoader::ContinueWithCertificate(net::X509Certificate* cert) { DCHECK(ssl_client_auth_handler_); ssl_client_auth_handler_.reset(); - request_->ContinueWithCertificate(cert); + if (!cert) { + request_->ContinueWithCertificate(nullptr, nullptr); + return; + } + scoped_refptr<net::SSLPrivateKey> private_key = + net::FetchClientCertPrivateKey(cert); + request_->ContinueWithCertificate(cert, private_key.get()); } void ResourceLoader::CancelCertificateSelection() { @@ -671,6 +678,12 @@ void ResourceLoader::CallDidFinishLoading() { } void ResourceLoader::RecordHistograms() { + if (request_->response_info().network_accessed) { + UMA_HISTOGRAM_ENUMERATION("Net.HttpResponseInfo.ConnectionInfo", + request_->response_info().connection_info, + net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS); + } + ResourceRequestInfoImpl* info = GetRequestInfo(); if (info->GetResourceType() == RESOURCE_TYPE_PREFETCH) { diff --git a/chromium/content/browser/loader/resource_loader.h b/chromium/content/browser/loader/resource_loader.h index 41f3d7e38b1..5b5ecdb500e 100644 --- a/chromium/content/browser/loader/resource_loader.h +++ b/chromium/content/browser/loader/resource_loader.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_LOADER_RESOURCE_LOADER_H_ #define CONTENT_BROWSER_LOADER_RESOURCE_LOADER_H_ +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/timer/timer.h" diff --git a/chromium/content/browser/loader/resource_loader_unittest.cc b/chromium/content/browser/loader/resource_loader_unittest.cc index 90418f3f114..05ed7fc4efb 100644 --- a/chromium/content/browser/loader/resource_loader_unittest.cc +++ b/chromium/content/browser/loader/resource_loader_unittest.cc @@ -4,11 +4,16 @@ #include "content/browser/loader/resource_loader.h" +#include <stddef.h> +#include <stdint.h> +#include <utility> + #include "base/files/file.h" #include "base/files/file_util.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" @@ -38,6 +43,7 @@ #include "net/cert/x509_certificate.h" #include "net/ssl/client_cert_store.h" #include "net/ssl/ssl_cert_request_info.h" +#include "net/ssl/ssl_private_key.h" #include "net/test/cert_test_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/url_request/url_request.h" @@ -146,7 +152,8 @@ class MockClientCertURLRequestJob : public net::URLRequestTestJob { public: MockClientCertURLRequestJob(net::URLRequest* request, net::NetworkDelegate* network_delegate) - : net::URLRequestTestJob(request, network_delegate) {} + : net::URLRequestTestJob(request, network_delegate), + weak_factory_(this) {} static std::vector<std::string> test_authorities() { return std::vector<std::string>(1, "dummy"); @@ -160,16 +167,19 @@ class MockClientCertURLRequestJob : public net::URLRequestTestJob { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&MockClientCertURLRequestJob::NotifyCertificateRequested, - this, cert_request_info)); + weak_factory_.GetWeakPtr(), cert_request_info)); } - void ContinueWithCertificate(net::X509Certificate* cert) override { + void ContinueWithCertificate(net::X509Certificate* cert, + net::SSLPrivateKey* private_key) override { net::URLRequestTestJob::Start(); } private: ~MockClientCertURLRequestJob() override {} + base::WeakPtrFactory<MockClientCertURLRequestJob> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(MockClientCertURLRequestJob); }; @@ -227,9 +237,9 @@ class MockHTTPSURLRequestJob : public net::URLRequestTestJob { }; const char kRedirectHeaders[] = - "HTTP/1.1 302 Found\0" - "Location: https://example.test\0" - "\0"; + "HTTP/1.1 302 Found\n" + "Location: https://example.test\n" + "\n"; class MockHTTPSJobURLRequestInterceptor : public net::URLRequestInterceptor { public: @@ -441,7 +451,7 @@ class SelectCertificateBrowserClient : public TestContentBrowserClient { ++call_count_; passed_certs_ = cert_request_info->client_certs; - delegate_ = delegate.Pass(); + delegate_ = std::move(delegate); select_certificate_run_loop_.Quit(); } @@ -471,11 +481,11 @@ class ResourceContextStub : public MockResourceContext { : MockResourceContext(test_request_context) {} scoped_ptr<net::ClientCertStore> CreateClientCertStore() override { - return dummy_cert_store_.Pass(); + return std::move(dummy_cert_store_); } void SetClientCertStore(scoped_ptr<net::ClientCertStore> store) { - dummy_cert_store_ = store.Pass(); + dummy_cert_store_ = std::move(store); } private: @@ -486,7 +496,7 @@ class ResourceContextStub : public MockResourceContext { // progress reporting. class NonChunkedUploadDataStream : public net::UploadDataStream { public: - explicit NonChunkedUploadDataStream(uint64 size) + explicit NonChunkedUploadDataStream(uint64_t size) : net::UploadDataStream(false, 0), stream_(0), size_(size) {} void AppendData(const char* data) { @@ -510,7 +520,7 @@ class NonChunkedUploadDataStream : public net::UploadDataStream { void ResetInternal() override { stream_.Reset(); } net::ChunkedUploadDataStream stream_; - uint64 size_; + uint64_t size_; DISALLOW_COPY_AND_ASSIGN(NonChunkedUploadDataStream); }; @@ -552,7 +562,7 @@ class ResourceLoaderTest : public testing::Test, virtual scoped_ptr<ResourceHandler> WrapResourceHandler( scoped_ptr<ResourceHandlerStub> leaf_handler, net::URLRequest* request) { - return leaf_handler.Pass(); + return std::move(leaf_handler); } // Replaces loader_ with a new one for |request|. @@ -565,13 +575,13 @@ class ResourceLoaderTest : public testing::Test, rfh->GetProcess()->GetID(), rfh->GetRenderViewHost()->GetRoutingID(), rfh->GetRoutingID(), true /* is_main_frame */, false /* parent_is_main_frame */, true /* allow_download */, - false /* is_async */); + false /* is_async */, false /* is_using_lofi_ */); scoped_ptr<ResourceHandlerStub> resource_handler( new ResourceHandlerStub(request.get())); raw_ptr_resource_handler_ = resource_handler.get(); loader_.reset(new ResourceLoader( - request.Pass(), - WrapResourceHandler(resource_handler.Pass(), raw_ptr_to_request_), + std::move(request), + WrapResourceHandler(std::move(resource_handler), raw_ptr_to_request_), this)); } @@ -589,7 +599,7 @@ class ResourceLoaderTest : public testing::Test, test_url(), net::DEFAULT_PRIORITY, nullptr /* delegate */)); - SetUpResourceLoader(request.Pass()); + SetUpResourceLoader(std::move(request)); } void TearDown() override { @@ -684,7 +694,7 @@ TEST_F(ClientCertResourceLoaderTest, WithStoreLookup) { new net::X509Certificate("test", "test", base::Time(), base::Time()))); scoped_ptr<ClientCertStoreStub> test_store(new ClientCertStoreStub( dummy_certs, &store_request_count, &store_requested_authorities)); - resource_context_.SetClientCertStore(test_store.Pass()); + resource_context_.SetClientCertStore(std::move(test_store)); // Plug in test content browser client. SelectCertificateBrowserClient test_client; @@ -707,7 +717,7 @@ TEST_F(ClientCertResourceLoaderTest, WithStoreLookup) { EXPECT_EQ(dummy_certs, test_client.passed_certs()); // Continue the request. - test_client.ContinueWithCertificate(dummy_certs[0].get()); + test_client.ContinueWithCertificate(nullptr); raw_ptr_resource_handler_->WaitForResponseComplete(); EXPECT_EQ(net::OK, raw_ptr_resource_handler_->status().error()); @@ -732,9 +742,7 @@ TEST_F(ClientCertResourceLoaderTest, WithNullStore) { EXPECT_EQ(net::CertificateList(), test_client.passed_certs()); // Continue the request. - scoped_refptr<net::X509Certificate> cert( - new net::X509Certificate("test", "test", base::Time(), base::Time())); - test_client.ContinueWithCertificate(cert.get()); + test_client.ContinueWithCertificate(nullptr); raw_ptr_resource_handler_->WaitForResponseComplete(); EXPECT_EQ(net::OK, raw_ptr_resource_handler_->status().error()); @@ -886,7 +894,7 @@ class ResourceLoaderRedirectToFileTest : public ResourceLoaderTest { // Create mock file streams and a ShareableFileReference. scoped_ptr<net::testing::MockFileStream> file_stream( - new net::testing::MockFileStream(file.Pass(), + new net::testing::MockFileStream(std::move(file), base::ThreadTaskRunnerHandle::Get())); file_stream_ = file_stream.get(); deletable_file_ = ShareableFileReference::GetOrCreate( @@ -897,13 +905,13 @@ class ResourceLoaderRedirectToFileTest : public ResourceLoaderTest { // Inject them into the handler. scoped_ptr<RedirectToFileResourceHandler> handler( - new RedirectToFileResourceHandler(leaf_handler.Pass(), request)); + new RedirectToFileResourceHandler(std::move(leaf_handler), request)); redirect_to_file_resource_handler_ = handler.get(); handler->SetCreateTemporaryFileStreamFunctionForTesting( base::Bind(&ResourceLoaderRedirectToFileTest::PostCallback, base::Unretained(this), base::Passed(&file_stream))); - return handler.Pass(); + return std::move(handler); } private: @@ -1074,7 +1082,7 @@ TEST_F(HTTPSSecurityInfoResourceLoaderTest, SecurityInfoOnHTTPSResource) { scoped_ptr<net::URLRequest> request( resource_context_.GetRequestContext()->CreateRequest( test_https_url(), net::DEFAULT_PRIORITY, nullptr /* delegate */)); - SetUpResourceLoader(request.Pass()); + SetUpResourceLoader(std::move(request)); // Send the request and wait until it completes. loader_->StartRequest(); @@ -1113,7 +1121,7 @@ TEST_F(HTTPSSecurityInfoResourceLoaderTest, resource_context_.GetRequestContext()->CreateRequest( test_https_redirect_url(), net::DEFAULT_PRIORITY, nullptr /* delegate */)); - SetUpResourceLoader(request.Pass()); + SetUpResourceLoader(std::move(request)); // Send the request and wait until it completes. loader_->StartRequest(); diff --git a/chromium/content/browser/loader/resource_message_delegate.h b/chromium/content/browser/loader/resource_message_delegate.h index 2a5dc45ebc4..f007f1ca356 100644 --- a/chromium/content/browser/loader/resource_message_delegate.h +++ b/chromium/content/browser/loader/resource_message_delegate.h @@ -5,7 +5,7 @@ #ifndef CONTENT_BROWSER_LOADER_RESOURCE_MESSAGE_DELEGATE_H_ #define CONTENT_BROWSER_LOADER_RESOURCE_MESSAGE_DELEGATE_H_ -#include "base/basictypes.h" +#include "base/macros.h" #include "content/common/content_export.h" #include "content/public/browser/global_request_id.h" diff --git a/chromium/content/browser/loader/resource_message_filter.cc b/chromium/content/browser/loader/resource_message_filter.cc index 4a20d9ac1df..186bf66316e 100644 --- a/chromium/content/browser/loader/resource_message_filter.cc +++ b/chromium/content/browser/loader/resource_message_filter.cc @@ -50,10 +50,12 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message) { } void ResourceMessageFilter::GetContexts( - const ResourceHostMsg_Request& request, + ResourceType resource_type, + int origin_pid, ResourceContext** resource_context, net::URLRequestContext** request_context) { - return get_contexts_callback_.Run(request, resource_context, request_context); + return get_contexts_callback_.Run(resource_type, origin_pid, resource_context, + request_context); } const HostZoomMap* ResourceMessageFilter::GetHostZoomMap() const { diff --git a/chromium/content/browser/loader/resource_message_filter.h b/chromium/content/browser/loader/resource_message_filter.h index 1240cb4f5b3..9bac4198f60 100644 --- a/chromium/content/browser/loader/resource_message_filter.h +++ b/chromium/content/browser/loader/resource_message_filter.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_LOADER_RESOURCE_MESSAGE_FILTER_H_ #include "base/callback_forward.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" @@ -38,12 +39,18 @@ class ServiceWorkerContextWrapper; // will not interfere with browser UI. class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { public: - typedef base::Callback<void(const ResourceHostMsg_Request&, + // TODO(ricea): Remove origin_pid when support for NPAPI plugins is removed. + // crbug.com/493212 is the tracking bug for NPAPI removal. + typedef base::Callback<void(ResourceType resource_type, + int origin_pid, ResourceContext**, net::URLRequestContext**)> GetContextsCallback; // |appcache_service|, |blob_storage_context|, |file_system_context| may be // NULL in unittests or for requests from the (NPAPI) plugin process. + // The |origin_pid| argument to |get_contexts_callback| is not used + // (and may be invalid) for requests that are NOT from the NPAPI plugin + // process. ResourceMessageFilter(int child_id, int process_type, ChromeAppCacheService* appcache_service, @@ -57,7 +64,10 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { void OnChannelClosing() override; bool OnMessageReceived(const IPC::Message& message) override; - void GetContexts(const ResourceHostMsg_Request& request, + // |origin_pid| is only required for NPAPI plugin processes. Its value is + // ignored otherwise. + void GetContexts(ResourceType resource_type, + int origin_pid, ResourceContext** resource_context, net::URLRequestContext** request_context); diff --git a/chromium/content/browser/loader/resource_request_info_impl.cc b/chromium/content/browser/loader/resource_request_info_impl.cc index eb400bca9cc..8511653ee50 100644 --- a/chromium/content/browser/loader/resource_request_info_impl.cc +++ b/chromium/content/browser/loader/resource_request_info_impl.cc @@ -4,15 +4,33 @@ #include "content/browser/loader/resource_request_info_impl.h" +#include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/loader/global_routing_id.h" #include "content/browser/loader/resource_message_filter.h" +#include "content/browser/web_contents/web_contents_impl.h" #include "content/common/net/url_request_user_data.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/global_request_id.h" +#include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/process_type.h" #include "net/url_request/url_request.h" namespace content { +namespace { + +WebContents* GetWebContentsFromFTNID(int frame_tree_node_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + FrameTreeNode* frame_tree_node = + FrameTreeNode::GloballyFindByID(frame_tree_node_id); + if (!frame_tree_node) + return nullptr; + + return WebContentsImpl::FromFrameTreeNode(frame_tree_node); +} + +} // namespace + // ---------------------------------------------------------------------------- // ResourceRequestInfo @@ -32,7 +50,8 @@ void ResourceRequestInfo::AllocateForTesting(net::URLRequest* request, bool is_main_frame, bool parent_is_main_frame, bool allow_download, - bool is_async) { + bool is_async, + bool is_using_lofi) { // Make sure both |is_main_frame| and |parent_is_main_frame| aren't set at the // same time. DCHECK(!(is_main_frame && parent_is_main_frame)); @@ -52,7 +71,6 @@ void ResourceRequestInfo::AllocateForTesting(net::URLRequest* request, render_frame_id, // render_frame_id is_main_frame, // is_main_frame parent_is_main_frame, // parent_is_main_frame - 0, // parent_render_frame_id resource_type, // resource_type ui::PAGE_TRANSITION_LINK, // transition_type false, // should_replace_current_entry @@ -68,7 +86,9 @@ void ResourceRequestInfo::AllocateForTesting(net::URLRequest* request, context, // context base::WeakPtr<ResourceMessageFilter>(), // filter false, // report_raw_headers - is_async); // is_async + is_async, // is_async + is_using_lofi, // is_using_lofi + std::string()); // original_headers info->AssociateWithRequest(request); } @@ -111,7 +131,6 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl( int render_frame_id, bool is_main_frame, bool parent_is_main_frame, - int parent_render_frame_id, ResourceType resource_type, ui::PageTransition transition_type, bool should_replace_current_entry, @@ -127,7 +146,9 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl( ResourceContext* context, base::WeakPtr<ResourceMessageFilter> filter, bool report_raw_headers, - bool is_async) + bool is_async, + bool is_using_lofi, + const std::string& original_headers) : cross_site_handler_(NULL), detachable_handler_(NULL), process_type_(process_type), @@ -139,7 +160,6 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl( render_frame_id_(render_frame_id), is_main_frame_(is_main_frame), parent_is_main_frame_(parent_is_main_frame), - parent_render_frame_id_(parent_render_frame_id), should_replace_current_entry_(should_replace_current_entry), is_download_(is_download), is_stream_(is_stream), @@ -158,12 +178,37 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl( context_(context), filter_(filter), report_raw_headers_(report_raw_headers), - is_async_(is_async) { + is_async_(is_async), + is_using_lofi_(is_using_lofi), + original_headers_(original_headers) { } ResourceRequestInfoImpl::~ResourceRequestInfoImpl() { } +ResourceRequestInfo::WebContentsGetter +ResourceRequestInfoImpl::GetWebContentsGetterForRequest() const { + // PlzNavigate: navigation requests are created with a valid FrameTreeNode ID + // and invalid RenderProcessHost and RenderFrameHost IDs. The FrameTreeNode + // ID should be used to access the WebContents. + if (frame_tree_node_id_ != -1) { + DCHECK(IsBrowserSideNavigationEnabled()); + return base::Bind(&GetWebContentsFromFTNID, frame_tree_node_id_); + } + + // In other cases, use the RenderProcessHost ID + RenderFrameHost ID to get + // the WebContents. + int render_process_host_id = -1; + int render_frame_host_id = -1; + if (!GetAssociatedRenderFrame(&render_process_host_id, + &render_frame_host_id)) { + NOTREACHED(); + } + + return base::Bind(&WebContentsImpl::FromRenderFrameHostID, + render_process_host_id, render_frame_host_id); +} + ResourceContext* ResourceRequestInfoImpl::GetContext() const { return context_; } @@ -180,10 +225,6 @@ int ResourceRequestInfoImpl::GetOriginPID() const { return origin_pid_; } -int ResourceRequestInfoImpl::GetRequestID() const { - return request_id_; -} - int ResourceRequestInfoImpl::GetRenderFrameID() const { return render_frame_id_; } @@ -196,10 +237,6 @@ bool ResourceRequestInfoImpl::ParentIsMainFrame() const { return parent_is_main_frame_; } -int ResourceRequestInfoImpl::GetParentRenderFrameID() const { - return parent_render_frame_id_; -} - ResourceType ResourceRequestInfoImpl::GetResourceType() const { return resource_type_; } @@ -250,6 +287,10 @@ bool ResourceRequestInfoImpl::IsDownload() const { return is_download_; } +bool ResourceRequestInfoImpl::IsUsingLoFi() const { + return is_using_lofi_; +} + bool ResourceRequestInfoImpl::ShouldReportRawHeaders() const { return report_raw_headers_; } @@ -265,6 +306,10 @@ void ResourceRequestInfoImpl::AssociateWithRequest(net::URLRequest* request) { } } +int ResourceRequestInfoImpl::GetRequestID() const { + return request_id_; +} + GlobalRequestID ResourceRequestInfoImpl::GetGlobalRequestID() const { return GlobalRequestID(child_id_, request_id_); } @@ -276,15 +321,15 @@ GlobalRoutingID ResourceRequestInfoImpl::GetGlobalRoutingID() const { void ResourceRequestInfoImpl::UpdateForTransfer( int child_id, int route_id, + int render_frame_id, int origin_pid, int request_id, - int parent_render_frame_id, base::WeakPtr<ResourceMessageFilter> filter) { child_id_ = child_id; route_id_ = route_id; + render_frame_id_ = render_frame_id; origin_pid_ = origin_pid; request_id_ = request_id; - parent_render_frame_id_ = parent_render_frame_id; filter_ = filter; } diff --git a/chromium/content/browser/loader/resource_request_info_impl.h b/chromium/content/browser/loader/resource_request_info_impl.h index f0251e51325..625e58ca890 100644 --- a/chromium/content/browser/loader/resource_request_info_impl.h +++ b/chromium/content/browser/loader/resource_request_info_impl.h @@ -7,8 +7,8 @@ #include <string> -#include "base/basictypes.h" #include "base/gtest_prod_util.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" @@ -49,7 +49,6 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, int render_frame_id, bool is_main_frame, bool parent_is_main_frame, - int parent_render_frame_id, ResourceType resource_type, ui::PageTransition transition_type, bool should_replace_current_entry, @@ -65,19 +64,20 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, ResourceContext* context, base::WeakPtr<ResourceMessageFilter> filter, bool report_raw_headers, - bool is_async); + bool is_async, + bool is_using_lofi, + const std::string& original_headers); ~ResourceRequestInfoImpl() override; // ResourceRequestInfo implementation: + WebContentsGetter GetWebContentsGetterForRequest() const override; ResourceContext* GetContext() const override; int GetChildID() const override; int GetRouteID() const override; int GetOriginPID() const override; - int GetRequestID() const override; int GetRenderFrameID() const override; bool IsMainFrame() const override; bool ParentIsMainFrame() const override; - int GetParentRenderFrameID() const override; ResourceType GetResourceType() const override; int GetProcessType() const override; blink::WebReferrerPolicy GetReferrerPolicy() const override; @@ -89,10 +89,12 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, int* render_frame_id) const override; bool IsAsync() const override; bool IsDownload() const override; + bool IsUsingLoFi() const override; bool ShouldReportRawHeaders() const; CONTENT_EXPORT void AssociateWithRequest(net::URLRequest* request); + CONTENT_EXPORT int GetRequestID() const; CONTENT_EXPORT GlobalRequestID GetGlobalRequestID() const; GlobalRoutingID GetGlobalRoutingID() const; @@ -112,9 +114,9 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, // does not need to be updated. void UpdateForTransfer(int child_id, int route_id, + int render_frame_id, int origin_pid, int request_id, - int parent_render_frame_id, base::WeakPtr<ResourceMessageFilter> filter); // CrossSiteResourceHandler for this request. May be null. @@ -180,6 +182,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, void set_do_not_prompt_for_login(bool do_not_prompt) { do_not_prompt_for_login_ = do_not_prompt; } + const std::string& original_headers() const { return original_headers_; } private: FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest, @@ -199,7 +202,6 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, int render_frame_id_; bool is_main_frame_; bool parent_is_main_frame_; - int parent_render_frame_id_; bool should_replace_current_entry_; bool is_download_; bool is_stream_; @@ -221,6 +223,8 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, base::WeakPtr<ResourceMessageFilter> filter_; bool report_raw_headers_; bool is_async_; + bool is_using_lofi_; + const std::string original_headers_; DISALLOW_COPY_AND_ASSIGN(ResourceRequestInfoImpl); }; diff --git a/chromium/content/browser/loader/resource_scheduler.cc b/chromium/content/browser/loader/resource_scheduler.cc index 2d9071612a3..690f20f0a03 100644 --- a/chromium/content/browser/loader/resource_scheduler.cc +++ b/chromium/content/browser/loader/resource_scheduler.cc @@ -5,11 +5,12 @@ #include "content/browser/loader/resource_scheduler.h" #include <stdint.h> - #include <set> #include <string> +#include <utility> #include <vector> +#include "base/macros.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" #include "base/stl_util.h" @@ -32,6 +33,11 @@ namespace content { namespace { +enum StartMode { + START_SYNC, + START_ASYNC +}; + // Field trial constants const char kThrottleCoalesceFieldTrial[] = "RequestThrottlingAndCoalescing"; const char kThrottleCoalesceFieldTrialThrottle[] = "Throttle"; @@ -162,14 +168,14 @@ class ResourceScheduler::RequestQueue { private: typedef std::map<ScheduledResourceRequest*, NetQueue::iterator> PointerMap; - uint32 MakeFifoOrderingId() { + uint32_t MakeFifoOrderingId() { fifo_ordering_ids_ += 1; return fifo_ordering_ids_; } // Used to create an ordering ID for scheduled resources so that resources // with same priority/intra_priority stay in fifo order. - uint32 fifo_ordering_ids_; + uint32_t fifo_ordering_ids_; NetQueue queue_; PointerMap pointers_; @@ -193,7 +199,8 @@ class ResourceScheduler::ScheduledResourceRequest : public ResourceThrottle { attributes_(kAttributeNone), scheduler_(scheduler), priority_(priority), - fifo_ordering_(0) { + fifo_ordering_(0), + weak_ptr_factory_(this) { DCHECK(!request_->GetUserData(kUserDataKey)); request_->SetUserData(kUserDataKey, new UnownedPointer(this)); } @@ -208,10 +215,39 @@ class ResourceScheduler::ScheduledResourceRequest : public ResourceThrottle { ->get(); } - void Start() { - ready_ = true; + // Starts the request. If |start_mode| is START_ASYNC, the request will not + // be started immediately. + void Start(StartMode start_mode) { + DCHECK(!ready_); + + // If the request was cancelled, do nothing. if (!request_->status().is_success()) return; + + bool was_deferred = deferred_; + + // If the request was deferred, need to start it. Otherwise, will just not + // defer starting it in the first place, and the value of |start_mode| + // makes no difference. + if (deferred_) { + // If can't start the request synchronously, post a task to start the + // request. + if (start_mode == START_ASYNC) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&ScheduledResourceRequest::Start, + weak_ptr_factory_.GetWeakPtr(), + START_SYNC)); + return; + } + deferred_ = false; + controller()->Resume(); + } + + ready_ = true; + + // The rest of this method is just collecting histograms. + base::TimeTicks time = base::TimeTicks::Now(); ClientState current_state = scheduler_->GetClientState(client_id_); // Note: the client state isn't perfectly accurate since it won't capture @@ -227,11 +263,8 @@ class ResourceScheduler::ScheduledResourceRequest : public ResourceThrottle { } base::TimeDelta time_was_deferred = base::TimeDelta::FromMicroseconds(0); - if (deferred_) { - deferred_ = false; - controller()->Resume(); + if (was_deferred) time_was_deferred = time - time_deferred_; - } PostHistogram("RequestTimeDeferred", client_state, NULL, time_was_deferred); PostHistogram("RequestTimeThrottled", client_state, NULL, time - request_->creation_time()); @@ -250,8 +283,8 @@ class ResourceScheduler::ScheduledResourceRequest : public ResourceThrottle { net::URLRequest* url_request() { return request_; } const net::URLRequest* url_request() const { return request_; } bool is_async() const { return is_async_; } - uint32 fifo_ordering() const { return fifo_ordering_; } - void set_fifo_ordering(uint32 fifo_ordering) { + uint32_t fifo_ordering() const { return fifo_ordering_; } + void set_fifo_ordering(uint32_t fifo_ordering) { fifo_ordering_ = fifo_ordering; } RequestAttributes attributes() const { @@ -294,9 +327,12 @@ class ResourceScheduler::ScheduledResourceRequest : public ResourceThrottle { RequestAttributes attributes_; ResourceScheduler* scheduler_; RequestPriorityParams priority_; - uint32 fifo_ordering_; + uint32_t fifo_ordering_; base::TimeTicks time_deferred_; + base::WeakPtrFactory<ResourceScheduler::ScheduledResourceRequest> + weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(ScheduledResourceRequest); }; @@ -356,10 +392,12 @@ class ResourceScheduler::Client { void ScheduleRequest(net::URLRequest* url_request, ScheduledResourceRequest* request) { SetRequestAttributes(request, DetermineRequestAttributes(request)); - if (ShouldStartRequest(request) == START_REQUEST) - StartRequest(request); - else + if (ShouldStartRequest(request) == START_REQUEST) { + // New requests can be started synchronously without issue. + StartRequest(request, START_SYNC); + } else { pending_requests_.Insert(request); + } } void RemoveRequest(ScheduledResourceRequest* request) { @@ -385,8 +423,9 @@ class ResourceScheduler::Client { ScheduledResourceRequest* request = *pending_requests_.GetNextHighestIterator(); pending_requests_.Erase(request); - // StartRequest() may modify pending_requests_. TODO(ricea): Does it? - StartRequest(request); + // Starting requests asynchronously ensures no side effects, and avoids + // starting a bunch of requests that may be about to be deleted. + StartRequest(request, START_ASYNC); } RequestSet unowned_requests; for (RequestSet::iterator it = in_flight_requests_.begin(); @@ -716,9 +755,10 @@ class ResourceScheduler::Client { return false; } - void StartRequest(ScheduledResourceRequest* request) { + void StartRequest(ScheduledResourceRequest* request, + StartMode start_mode) { InsertInFlightRequest(request); - request->Start(); + request->Start(start_mode); } // ShouldStartRequest is the main scheduling algorithm. @@ -890,7 +930,7 @@ class ResourceScheduler::Client { if (query_result == START_REQUEST) { pending_requests_.Erase(request); - StartRequest(request); + StartRequest(request, START_ASYNC); // StartRequest can modify the pending list, so we (re)start evaluation // from the currently highest priority request. Avoid copying a singular @@ -1043,13 +1083,13 @@ scoped_ptr<ResourceThrottle> ResourceScheduler::ScheduleRequest( // 2. Most unittests don't send the IPCs needed to register Clients. // 3. The tab is closed while a RequestResource IPC is in flight. unowned_requests_.insert(request.get()); - request->Start(); - return request.Pass(); + request->Start(START_SYNC); + return std::move(request); } Client* client = it->second; client->ScheduleRequest(url_request, request.get()); - return request.Pass(); + return std::move(request); } void ResourceScheduler::RemoveRequest(ScheduledResourceRequest* request) { @@ -1086,7 +1126,7 @@ void ResourceScheduler::OnClientDeleted(int child_id, int route_id) { DCHECK(CalledOnValidThread()); ClientId client_id = MakeClientId(child_id, route_id); ClientMap::iterator it = client_map_.find(client_id); - CHECK(it != client_map_.end()); + DCHECK(it != client_map_.end()); Client* client = it->second; // ResourceDispatcherHost cancels all requests except for cross-renderer diff --git a/chromium/content/browser/loader/resource_scheduler.h b/chromium/content/browser/loader/resource_scheduler.h index a50324b5a59..444d97d1693 100644 --- a/chromium/content/browser/loader/resource_scheduler.h +++ b/chromium/content/browser/loader/resource_scheduler.h @@ -5,11 +5,14 @@ #ifndef CONTENT_BROWSER_LOADER_RESOURCE_SCHEDULER_H_ #define CONTENT_BROWSER_LOADER_RESOURCE_SCHEDULER_H_ +#include <stddef.h> +#include <stdint.h> + #include <map> #include <set> -#include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/timer/timer.h" @@ -218,7 +221,7 @@ class CONTENT_EXPORT ResourceScheduler : public base::NonThreadSafe { }; class Client; - typedef int64 ClientId; + typedef int64_t ClientId; typedef std::map<ClientId, Client*> ClientMap; typedef std::set<ScheduledResourceRequest*> RequestSet; diff --git a/chromium/content/browser/loader/resource_scheduler_filter.cc b/chromium/content/browser/loader/resource_scheduler_filter.cc index 2777eaae121..0738ec188fe 100644 --- a/chromium/content/browser/loader/resource_scheduler_filter.cc +++ b/chromium/content/browser/loader/resource_scheduler_filter.cc @@ -4,6 +4,9 @@ #include "content/browser/loader/resource_scheduler_filter.h" +#include <stdint.h> + +#include "base/macros.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_scheduler.h" #include "content/common/frame_messages.h" @@ -12,9 +15,8 @@ namespace content { namespace { -const uint32 kFilteredMessageClasses[] = { - FrameMsgStart, - ViewMsgStart, +const uint32_t kFilteredMessageClasses[] = { + FrameMsgStart, ViewMsgStart, }; } // namespace diff --git a/chromium/content/browser/loader/resource_scheduler_unittest.cc b/chromium/content/browser/loader/resource_scheduler_unittest.cc index 3aaf23d5977..1399b52dde3 100644 --- a/chromium/content/browser/loader/resource_scheduler_unittest.cc +++ b/chromium/content/browser/loader/resource_scheduler_unittest.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/resource_scheduler.h" +#include <utility> + #include "base/memory/scoped_vector.h" #include "base/message_loop/message_loop.h" #include "base/metrics/field_trial.h" @@ -53,8 +55,8 @@ class TestRequest : public ResourceController { scoped_ptr<ResourceThrottle> throttle, ResourceScheduler* scheduler) : started_(false), - url_request_(url_request.Pass()), - throttle_(throttle.Pass()), + url_request_(std::move(url_request)), + throttle_(std::move(throttle)), scheduler_(scheduler) { throttle_->set_controller_for_testing(this); } @@ -102,10 +104,10 @@ class CancelingTestRequest : public TestRequest { CancelingTestRequest(scoped_ptr<net::URLRequest> url_request, scoped_ptr<ResourceThrottle> throttle, ResourceScheduler* scheduler) - : TestRequest(url_request.Pass(), throttle.Pass(), scheduler) {} + : TestRequest(std::move(url_request), std::move(throttle), scheduler) {} void set_request_to_cancel(scoped_ptr<TestRequest> request_to_cancel) { - request_to_cancel_ = request_to_cancel.Pass(); + request_to_cancel_ = std::move(request_to_cancel); } private: @@ -181,7 +183,7 @@ class ResourceSchedulerTest : public testing::Test { int route_id) { scoped_ptr<net::URLRequest> url_request( context_.CreateRequest(GURL(url), priority, NULL)); - return url_request.Pass(); + return url_request; } scoped_ptr<net::URLRequest> NewURLRequest(const char* url, @@ -238,8 +240,8 @@ class ResourceSchedulerTest : public testing::Test { NewURLRequestWithChildAndRoute(url, priority, child_id, route_id)); scoped_ptr<ResourceThrottle> throttle(scheduler_->ScheduleRequest( child_id, route_id, is_async, url_request.get())); - TestRequest* request = - new TestRequest(url_request.Pass(), throttle.Pass(), scheduler()); + TestRequest* request = new TestRequest(std::move(url_request), + std::move(throttle), scheduler()); request->Start(); return request; } @@ -282,7 +284,9 @@ TEST_F(ResourceSchedulerTest, OneLowLoadsUntilIdle) { EXPECT_TRUE(high->started()); EXPECT_TRUE(low->started()); EXPECT_FALSE(low2->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low2->started()); } @@ -293,8 +297,15 @@ TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInserted) { EXPECT_TRUE(high->started()); EXPECT_TRUE(low->started()); EXPECT_FALSE(low2->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); + // TODO(mmenke): The name of this test implies this should be false. + // Investigate if this is now expected, remove or update this test if it is. + EXPECT_TRUE(low2->started()); + scheduler()->OnWillInsertBody(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low2->started()); } @@ -305,9 +316,13 @@ TEST_F(ResourceSchedulerTest, OneLowLoadsUntilCriticalComplete) { EXPECT_TRUE(high->started()); EXPECT_TRUE(low->started()); EXPECT_FALSE(low2->started()); + scheduler()->OnWillInsertBody(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(low2->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low2->started()); } @@ -319,7 +334,9 @@ TEST_F(ResourceSchedulerTest, LowDoesNotBlockCriticalComplete) { EXPECT_TRUE(low->started()); EXPECT_TRUE(lowest->started()); EXPECT_FALSE(lowest2->started()); + scheduler()->OnWillInsertBody(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(lowest2->started()); } @@ -335,8 +352,10 @@ TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInsertedExceptSpdy) { EXPECT_TRUE(low_spdy->started()); EXPECT_TRUE(low->started()); EXPECT_FALSE(low2->started()); + scheduler()->OnWillInsertBody(kChildId, kRouteId); high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low2->started()); } @@ -367,9 +386,13 @@ TEST_F(ResourceSchedulerTest, StartMultipleLowRequestsWhenIdle) { EXPECT_TRUE(high2->started()); EXPECT_TRUE(low->started()); EXPECT_FALSE(low2->started()); + high1.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(low2->started()); + high2.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low2->started()); } @@ -382,16 +405,18 @@ TEST_F(ResourceSchedulerTest, CancelOtherRequestsWhileResuming) { scoped_ptr<ResourceThrottle> throttle(scheduler()->ScheduleRequest( kChildId, kRouteId, true, url_request.get())); scoped_ptr<CancelingTestRequest> low2(new CancelingTestRequest( - url_request.Pass(), throttle.Pass(), scheduler())); + std::move(url_request), std::move(throttle), scheduler())); low2->Start(); scoped_ptr<TestRequest> low3(NewRequest("http://host/low3", net::LOWEST)); - low2->set_request_to_cancel(low3.Pass()); + low2->set_request_to_cancel(std::move(low3)); scoped_ptr<TestRequest> low4(NewRequest("http://host/low4", net::LOWEST)); EXPECT_TRUE(high->started()); EXPECT_FALSE(low2->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low1->started()); EXPECT_TRUE(low2->started()); EXPECT_TRUE(low4->started()); @@ -421,10 +446,14 @@ TEST_F(ResourceSchedulerTest, LimitedNumberOfDelayableRequestsInFlight) { net::LOWEST)); EXPECT_FALSE(second_last_singlehost->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(second_last_singlehost->started()); EXPECT_FALSE(last_singlehost->started()); + lows_singlehost.erase(lows_singlehost.begin()); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(last_singlehost->started()); // Queue more requests from different hosts until we reach the total limit. @@ -432,6 +461,7 @@ TEST_F(ResourceSchedulerTest, LimitedNumberOfDelayableRequestsInFlight) { kMaxNumDelayableRequestsPerClient - kMaxNumDelayableRequestsPerHost; EXPECT_GT(expected_slots_left, 0); ScopedVector<TestRequest> lows_different_host; + base::RunLoop().RunUntilIdle(); for (int i = 0; i < expected_slots_left; ++i) { string url = "http://host" + base::IntToString(i) + "/low"; lows_different_host.push_back(NewRequest(url.c_str(), net::LOWEST)); @@ -439,7 +469,7 @@ TEST_F(ResourceSchedulerTest, LimitedNumberOfDelayableRequestsInFlight) { } scoped_ptr<TestRequest> last_different_host(NewRequest("http://host_new/last", - net::LOWEST)); + net::LOWEST)); EXPECT_FALSE(last_different_host->started()); } @@ -452,6 +482,7 @@ TEST_F(ResourceSchedulerTest, RaisePriorityAndStart) { EXPECT_FALSE(request->started()); ChangeRequestPriority(request.get(), net::HIGHEST); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(request->started()); } @@ -466,6 +497,7 @@ TEST_F(ResourceSchedulerTest, RaisePriorityInQueue) { EXPECT_FALSE(idle->started()); ChangeRequestPriority(request.get(), net::LOWEST); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(request->started()); EXPECT_FALSE(idle->started()); @@ -478,6 +510,7 @@ TEST_F(ResourceSchedulerTest, RaisePriorityInQueue) { scheduler()->OnWillInsertBody(kChildId, kRouteId); high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(request->started()); EXPECT_FALSE(idle->started()); @@ -494,6 +527,7 @@ TEST_F(ResourceSchedulerTest, LowerPriority) { EXPECT_FALSE(idle->started()); ChangeRequestPriority(request.get(), net::IDLE); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(request->started()); EXPECT_FALSE(idle->started()); @@ -509,6 +543,7 @@ TEST_F(ResourceSchedulerTest, LowerPriority) { scheduler()->OnWillInsertBody(kChildId, kRouteId); high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(request->started()); EXPECT_TRUE(idle->started()); @@ -532,14 +567,17 @@ TEST_F(ResourceSchedulerTest, ReprioritizedRequestGoesToBackOfQueue) { } ChangeRequestPriority(request.get(), net::IDLE); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(request->started()); EXPECT_FALSE(idle->started()); ChangeRequestPriority(request.get(), net::LOWEST); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(request->started()); EXPECT_FALSE(idle->started()); scheduler()->OnWillInsertBody(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(request->started()); EXPECT_FALSE(idle->started()); } @@ -560,10 +598,12 @@ TEST_F(ResourceSchedulerTest, HigherIntraPriorityGoesToFrontOfQueue) { EXPECT_FALSE(request->started()); ChangeRequestPriority(request.get(), net::IDLE, 1); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(request->started()); scheduler()->OnWillInsertBody(kChildId, kRouteId); high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(request->started()); } @@ -617,6 +657,7 @@ TEST_F(ResourceSchedulerTest, SpdyProxySchedulesImmediately) { EXPECT_FALSE(request->started()); scheduler()->OnReceivedSpdyProxiedHttpResponse(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(request->started()); scoped_ptr<TestRequest> after(NewRequest("http://host/after", net::IDLE)); @@ -640,9 +681,11 @@ TEST_F(ResourceSchedulerTest, NewSpdyHostInDelayableRequests) { http_server_properties_.SetSupportsSpdy( net::HostPortPair("spdyhost1", 8080), true); low1_spdy.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low1->started()); low1.reset(); + base::RunLoop().RunUntilIdle(); scoped_ptr<TestRequest> low2_spdy( NewRequest("http://spdyhost2:8080/low", net::IDLE)); // Reprioritize a request after we learn the server supports SPDY. @@ -650,6 +693,7 @@ TEST_F(ResourceSchedulerTest, NewSpdyHostInDelayableRequests) { http_server_properties_.SetSupportsSpdy( net::HostPortPair("spdyhost2", 8080), true); ChangeRequestPriority(low2_spdy.get(), net::LOWEST); + base::RunLoop().RunUntilIdle(); scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST)); EXPECT_TRUE(low2->started()); } @@ -749,9 +793,10 @@ TEST_F(ResourceSchedulerTest, UnthrottleNewlyVisibleClient) { scheduler()->OnVisibilityChanged( kBackgroundChildId, kBackgroundRouteId, true); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(ResourceScheduler::ACTIVE_AND_LOADING, scheduler()->GetClientStateForTesting(kBackgroundChildId, - kBackgroundRouteId)); + kBackgroundRouteId)); EXPECT_TRUE(request->started()); } @@ -770,6 +815,7 @@ TEST_F(ResourceSchedulerTest, UnthrottleNewlyAudibleClient) { scheduler()->OnAudibilityChanged( kBackgroundChildId, kBackgroundRouteId, true); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(ResourceScheduler::ACTIVE_AND_LOADING, scheduler()->GetClientStateForTesting(kBackgroundChildId, kBackgroundRouteId)); @@ -843,8 +889,8 @@ TEST_F(ResourceSchedulerTest, ThrottledClientStartsNextHighestPriorityRequest) { EXPECT_FALSE(low->started()); EXPECT_FALSE(high->started()); - // request->CancelRequest(); request->Cancel(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(high->started()); EXPECT_FALSE(low->started()); } @@ -864,7 +910,8 @@ TEST_F(ResourceSchedulerTest, ThrottledSpdyProxySchedulesImmediately) { EXPECT_FALSE(request->started()); scheduler()->OnReceivedSpdyProxiedHttpResponse(kBackgroundChildId, - kBackgroundRouteId); + kBackgroundRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(request->started()); scoped_ptr<TestRequest> after( @@ -890,7 +937,8 @@ TEST_F(ResourceSchedulerTest, CoalescedClientIssuesNoRequests) { EXPECT_FALSE(request->started()); scheduler()->OnReceivedSpdyProxiedHttpResponse(kBackgroundChildId, - kBackgroundRouteId); + kBackgroundRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(high->started()); scoped_ptr<TestRequest> after( @@ -915,7 +963,8 @@ TEST_F(ResourceSchedulerTest, CoalescedSpdyProxyWaits) { EXPECT_FALSE(request->started()); scheduler()->OnReceivedSpdyProxiedHttpResponse(kBackgroundChildId, - kBackgroundRouteId); + kBackgroundRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(request->started()); scoped_ptr<TestRequest> after( @@ -1687,6 +1736,7 @@ TEST_F(ResourceSchedulerTest, FullVisibleLoadedCorrectlyUnthrottle) { scheduler()->OnLoadingStateChanged( kBackgroundChildId2, kBackgroundRouteId2, true); scheduler()->OnLoadingStateChanged(kChildId2, kRouteId2, true); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(scheduler()->active_clients_loaded()); EXPECT_EQ(ResourceScheduler::THROTTLED, scheduler()->GetClientStateForTesting(kBackgroundChildId, @@ -1709,6 +1759,7 @@ TEST_F(ResourceSchedulerTest, FullVisibleLoadedCorrectlyUnthrottle) { // 2 visible loaded, 1 hidden loading, 1 hidden loaded scheduler()->OnLoadingStateChanged(kChildId, kRouteId, true); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(scheduler()->active_clients_loaded()); EXPECT_EQ(ResourceScheduler::UNTHROTTLED, scheduler()->GetClientStateForTesting(kBackgroundChildId, @@ -1725,6 +1776,7 @@ TEST_F(ResourceSchedulerTest, FullVisibleLoadedCorrectlyUnthrottle) { // 1 visible and 1 hidden loaded, 1 visible and 1 hidden loading scheduler()->OnLoadingStateChanged(kChildId, kRouteId, false); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(scheduler()->active_clients_loaded()); EXPECT_EQ(ResourceScheduler::THROTTLED, scheduler()->GetClientStateForTesting(kBackgroundChildId, @@ -1801,6 +1853,7 @@ TEST_F(ResourceSchedulerTest, CoalescedClientCreationStartsTimer) { EXPECT_FALSE(mock_timer_->IsRunning()); scheduler()->OnLoadingStateChanged( kBackgroundChildId, kBackgroundRouteId, true); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(ResourceScheduler::COALESCED, scheduler()->GetClientStateForTesting(kBackgroundChildId, kBackgroundRouteId)); @@ -2024,6 +2077,7 @@ TEST_F(ResourceSchedulerTest, CoalescedRequestsIssueOnTimer) { EXPECT_FALSE(low->started()); FireCoalescingTimer(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(high->started()); EXPECT_TRUE(low->started()); @@ -2082,6 +2136,7 @@ TEST_F(ResourceSchedulerTest, CoalescedRequestsUnthrottleCorrectlyOnTimer) { EXPECT_FALSE(low_spdy->started()); FireCoalescingTimer(); + base::RunLoop().RunUntilIdle(); // All high priority requests should issue. EXPECT_TRUE(high->started()); @@ -2115,6 +2170,7 @@ TEST_F(ResourceSchedulerTest, CoalescedRequestsWaitForNextTimer) { EXPECT_FALSE(high->started()); FireCoalescingTimer(); + base::RunLoop().RunUntilIdle(); scoped_ptr<TestRequest> high2( NewBackgroundRequest("http://host/high2", net::HIGHEST)); @@ -2126,6 +2182,7 @@ TEST_F(ResourceSchedulerTest, CoalescedRequestsWaitForNextTimer) { EXPECT_FALSE(low->started()); FireCoalescingTimer(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(high->started()); EXPECT_TRUE(high2->started()); @@ -2163,14 +2220,14 @@ TEST_F(ResourceSchedulerTest, GetVisualSignalFromRenderViewHost) { // Check initial visibility is set correctly. EXPECT_EQ(scheduler->IsClientVisibleForTesting(rvh1->GetProcess()->GetID(), rvh1->GetRoutingID()), - !rvh1->is_hidden()); + !rvh1->GetWidget()->is_hidden()); EXPECT_EQ(scheduler->IsClientVisibleForTesting(rvh2->GetProcess()->GetID(), rvh1->GetRoutingID()), - !rvh2->is_hidden()); + !rvh2->GetWidget()->is_hidden()); - // 1 visible, 1 hidden - rvh1->WasShown(ui::LatencyInfo()); - rvh2->WasHidden(); + // 1 visible, 1 hidden. + rvh1->GetWidget()->WasShown(ui::LatencyInfo()); + rvh2->GetWidget()->WasHidden(); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(scheduler->IsClientVisibleForTesting(rvh1->GetProcess()->GetID(), @@ -2179,8 +2236,8 @@ TEST_F(ResourceSchedulerTest, GetVisualSignalFromRenderViewHost) { rvh2->GetRoutingID())); // Flip the visibility and check again. - rvh1->WasHidden(); - rvh2->WasShown(ui::LatencyInfo()); + rvh1->GetWidget()->WasHidden(); + rvh2->GetWidget()->WasShown(ui::LatencyInfo()); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(scheduler->IsClientVisibleForTesting(rvh1->GetProcess()->GetID(), @@ -2257,7 +2314,9 @@ TEST_F(ResourceSchedulerTest, OutstandingRequestLimitDelays) { EXPECT_TRUE(high->started()); EXPECT_FALSE(low->started()); EXPECT_FALSE(low2->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low->started()); EXPECT_TRUE(low2->started()); } @@ -2274,9 +2333,11 @@ TEST_F(ResourceSchedulerTest, RequestStartedAfterClientDeleted) { scoped_ptr<TestRequest> lowest2(NewRequestWithChildAndRoute( "http://host/lowest", net::LOWEST, kChildId2, kRouteId2)); EXPECT_FALSE(lowest2->started()); + scheduler_->OnClientDeleted(kChildId2, kRouteId2); high.reset(); lowest1.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(lowest2->started()); } @@ -2297,9 +2358,11 @@ TEST_F(ResourceSchedulerTest, RequestStartedAfterClientDeletedManyDelayable) { scoped_ptr<TestRequest> lowest(NewRequestWithChildAndRoute( "http://host/lowest", net::LOWEST, kChildId2, kRouteId2)); EXPECT_FALSE(lowest->started()); + scheduler_->OnClientDeleted(kChildId2, kRouteId2); high.reset(); delayable_requests.clear(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(lowest->started()); } @@ -2344,7 +2407,9 @@ TEST_F(ResourceSchedulerTest, DefaultLayoutBlockingPriority) { EXPECT_TRUE(low2->started()); EXPECT_TRUE(lowest->started()); EXPECT_FALSE(lowest2->started()); + lowest.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(lowest2->started()); } @@ -2391,14 +2456,20 @@ TEST_F(ResourceSchedulerTest, IncreaseLayoutBlockingPriority) { EXPECT_FALSE(low2->started()); EXPECT_FALSE(lowest->started()); EXPECT_FALSE(lowest2->started()); + low.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low2->started()); EXPECT_FALSE(lowest->started()); EXPECT_FALSE(lowest2->started()); + low2.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(lowest->started()); EXPECT_FALSE(lowest2->started()); + lowest.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(lowest2->started()); } @@ -2432,12 +2503,18 @@ TEST_F(ResourceSchedulerTest, UseLayoutBlockingThresholdOne) { EXPECT_TRUE(high2->started()); EXPECT_FALSE(low->started()); EXPECT_FALSE(low2->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low->started()); EXPECT_FALSE(low2->started()); + high2.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(low2->started()); + scheduler()->OnWillInsertBody(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low2->started()); } @@ -2473,13 +2550,19 @@ TEST_F(ResourceSchedulerTest, UseLayoutBlockingThresholdTwo) { EXPECT_TRUE(high3->started()); EXPECT_FALSE(low->started()); EXPECT_FALSE(low2->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low->started()); EXPECT_FALSE(low2->started()); + high2.reset(); high3.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(low2->started()); + scheduler()->OnWillInsertBody(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low2->started()); } @@ -2513,8 +2596,10 @@ TEST_F(ResourceSchedulerTest, TwoDelayableLoadsUntilBodyInserted) { EXPECT_TRUE(low->started()); EXPECT_TRUE(low2->started()); EXPECT_FALSE(low3->started()); + high.reset(); scheduler()->OnWillInsertBody(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low3->started()); } @@ -2552,12 +2637,16 @@ TEST_F(ResourceSchedulerTest, EXPECT_FALSE(low->started()); EXPECT_FALSE(low2->started()); EXPECT_FALSE(low3->started()); + high.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low->started()); EXPECT_TRUE(low2->started()); EXPECT_FALSE(low3->started()); + high2.reset(); scheduler()->OnWillInsertBody(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(low3->started()); } @@ -2637,7 +2726,7 @@ TEST_F(ResourceSchedulerTest, } scoped_ptr<TestRequest> last_different_host(NewRequest("http://host_new/last", - net::LOWEST)); + net::LOWEST)); EXPECT_FALSE(last_different_host->started()); } diff --git a/chromium/content/browser/loader/stream_resource_handler.h b/chromium/content/browser/loader/stream_resource_handler.h index 37f4d0da2ea..6bc4253a2f3 100644 --- a/chromium/content/browser/loader/stream_resource_handler.h +++ b/chromium/content/browser/loader/stream_resource_handler.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_LOADER_STREAM_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_STREAM_RESOURCE_HANDLER_H_ +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "content/browser/loader/resource_handler.h" diff --git a/chromium/content/browser/loader/sync_resource_handler.h b/chromium/content/browser/loader/sync_resource_handler.h index fb028475fbb..49b0d919da0 100644 --- a/chromium/content/browser/loader/sync_resource_handler.h +++ b/chromium/content/browser/loader/sync_resource_handler.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_LOADER_SYNC_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_SYNC_RESOURCE_HANDLER_H_ +#include <stdint.h> + #include <string> #include "content/browser/loader/resource_handler.h" @@ -56,7 +58,7 @@ class SyncResourceHandler : public ResourceHandler { SyncLoadResult result_; IPC::Message* result_message_; ResourceDispatcherHostImpl* rdh_; - int64 total_transfer_size_; + int64_t total_transfer_size_; }; } // namespace content diff --git a/chromium/content/browser/loader/temporary_file_stream.cc b/chromium/content/browser/loader/temporary_file_stream.cc index fc57c8aad89..c8bb3338f5c 100644 --- a/chromium/content/browser/loader/temporary_file_stream.cc +++ b/chromium/content/browser/loader/temporary_file_stream.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/temporary_file_stream.h" +#include <utility> + #include "base/bind.h" #include "base/callback.h" #include "base/files/file_proxy.h" @@ -43,7 +45,7 @@ void DidCreateTemporaryFile( scoped_ptr<net::FileStream> file_stream( new net::FileStream(file_proxy->TakeFile(), task_runner)); - callback.Run(error_code, file_stream.Pass(), deletable_file.get()); + callback.Run(error_code, std::move(file_stream), deletable_file.get()); } } // namespace diff --git a/chromium/content/browser/loader/temporary_file_stream_unittest.cc b/chromium/content/browser/loader/temporary_file_stream_unittest.cc index af57f0824ec..a5fe1453312 100644 --- a/chromium/content/browser/loader/temporary_file_stream_unittest.cc +++ b/chromium/content/browser/loader/temporary_file_stream_unittest.cc @@ -5,13 +5,13 @@ #include "content/browser/loader/temporary_file_stream.h" #include <string.h> - #include <string> +#include <utility> -#include "base/basictypes.h" #include "base/bind.h" #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/macros.h" #include "base/run_loop.h" #include "content/public/test/test_browser_thread_bundle.h" #include "net/base/file_stream.h" @@ -42,7 +42,7 @@ class WaitForFileStream { scoped_ptr<net::FileStream> file_stream, ShareableFileReference* deletable_file) { error_ = error; - file_stream_ = file_stream.Pass(); + file_stream_ = std::move(file_stream); deletable_file_ = deletable_file; loop_.Quit(); } diff --git a/chromium/content/browser/loader/throttling_resource_handler.cc b/chromium/content/browser/loader/throttling_resource_handler.cc index 0378423ba15..93b9ad74836 100644 --- a/chromium/content/browser/loader/throttling_resource_handler.cc +++ b/chromium/content/browser/loader/throttling_resource_handler.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/throttling_resource_handler.h" +#include <utility> + #include "content/browser/loader/resource_request_info_impl.h" #include "content/public/browser/resource_throttle.h" #include "content/public/common/resource_response.h" @@ -15,9 +17,9 @@ ThrottlingResourceHandler::ThrottlingResourceHandler( scoped_ptr<ResourceHandler> next_handler, net::URLRequest* request, ScopedVector<ResourceThrottle> throttles) - : LayeredResourceHandler(request, next_handler.Pass()), + : LayeredResourceHandler(request, std::move(next_handler)), deferred_stage_(DEFERRED_NONE), - throttles_(throttles.Pass()), + throttles_(std::move(throttles)), next_index_(0), cancelled_by_resource_throttle_(false) { for (size_t i = 0; i < throttles_.size(); ++i) { diff --git a/chromium/content/browser/loader/throttling_resource_handler.h b/chromium/content/browser/loader/throttling_resource_handler.h index 0d1287d644e..6abefc46e04 100644 --- a/chromium/content/browser/loader/throttling_resource_handler.h +++ b/chromium/content/browser/loader/throttling_resource_handler.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_LOADER_THROTTLING_RESOURCE_HANDLER_H_ #define CONTENT_BROWSER_LOADER_THROTTLING_RESOURCE_HANDLER_H_ +#include <stddef.h> + #include "base/memory/ref_counted.h" #include "base/memory/scoped_vector.h" #include "content/browser/loader/layered_resource_handler.h" diff --git a/chromium/content/browser/loader/upload_data_stream_builder.cc b/chromium/content/browser/loader/upload_data_stream_builder.cc index 2f30f94a7d6..bcabe99484c 100644 --- a/chromium/content/browser/loader/upload_data_stream_builder.cc +++ b/chromium/content/browser/loader/upload_data_stream_builder.cc @@ -4,11 +4,14 @@ #include "content/browser/loader/upload_data_stream_builder.h" +#include <stdint.h> + #include <limits> #include <utility> #include <vector> #include "base/logging.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "content/browser/fileapi/upload_file_system_file_element_reader.h" #include "content/common/resource_request_body.h" @@ -77,47 +80,46 @@ scoped_ptr<net::UploadDataStream> UploadDataStreamBuilder::Build( storage::BlobStorageContext* blob_context, storage::FileSystemContext* file_system_context, base::SingleThreadTaskRunner* file_task_runner) { - ScopedVector<net::UploadElementReader> element_readers; + std::vector<scoped_ptr<net::UploadElementReader>> element_readers; for (const auto& element : *body->elements()) { switch (element.type()) { case ResourceRequestBody::Element::TYPE_BYTES: - element_readers.push_back(new BytesElementReader(body, element)); + element_readers.push_back( + make_scoped_ptr(new BytesElementReader(body, element))); break; case ResourceRequestBody::Element::TYPE_FILE: - element_readers.push_back( - new FileElementReader(body, file_task_runner, element)); + element_readers.push_back(make_scoped_ptr( + new FileElementReader(body, file_task_runner, element))); break; case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM: // If |body| contains any filesystem URLs, the caller should have // supplied a FileSystemContext. DCHECK(file_system_context); element_readers.push_back( - new content::UploadFileSystemFileElementReader( - file_system_context, - element.filesystem_url(), - element.offset(), - element.length(), - element.expected_modification_time())); + make_scoped_ptr(new content::UploadFileSystemFileElementReader( + file_system_context, element.filesystem_url(), element.offset(), + element.length(), element.expected_modification_time()))); break; case ResourceRequestBody::Element::TYPE_BLOB: { DCHECK_EQ(std::numeric_limits<uint64_t>::max(), element.length()); DCHECK_EQ(0ul, element.offset()); scoped_ptr<storage::BlobDataHandle> handle = blob_context->GetBlobDataFromUUID(element.blob_uuid()); - element_readers.push_back(new storage::UploadBlobElementReader( - handle.Pass(), file_system_context, file_task_runner)); + element_readers.push_back( + make_scoped_ptr(new storage::UploadBlobElementReader( + std::move(handle), file_system_context, file_task_runner))); break; } case ResourceRequestBody::Element::TYPE_DISK_CACHE_ENTRY: + case ResourceRequestBody::Element::TYPE_BYTES_DESCRIPTION: case ResourceRequestBody::Element::TYPE_UNKNOWN: NOTREACHED(); break; } } - return make_scoped_ptr( - new net::ElementsUploadDataStream(element_readers.Pass(), - body->identifier())); + return make_scoped_ptr(new net::ElementsUploadDataStream( + std::move(element_readers), body->identifier())); } } // namespace content diff --git a/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc b/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc index ae79dd93cf1..fc92cc185aa 100644 --- a/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc +++ b/chromium/content/browser/loader/upload_data_stream_builder_unittest.cc @@ -4,11 +4,14 @@ #include "content/browser/loader/upload_data_stream_builder.h" +#include <stdint.h> + #include <algorithm> #include <string> #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/macros.h" #include "base/run_loop.h" #include "base/thread_task_runner_handle.h" #include "base/time/time.h" @@ -41,10 +44,10 @@ TEST(UploadDataStreamBuilderTest, CreateUploadDataStream) { const std::string kBlobData = "blobdata"; const char kData[] = "123"; const base::FilePath::StringType kFilePath = FILE_PATH_LITERAL("abc"); - const uint64 kFileOffset = 10U; - const uint64 kFileLength = 100U; + const uint64_t kFileOffset = 10U; + const uint64_t kFileLength = 100U; const base::Time kFileTime = base::Time::FromDoubleT(999); - const int64 kIdentifier = 12345; + const int64_t kIdentifier = 12345; BlobStorageContext context; BlobDataBuilder builder(kBlob); @@ -81,7 +84,7 @@ TEST(UploadDataStreamBuilderTest, CreateUploadDataStream) { const storage::UploadBlobElementReader* r3 = static_cast<storage::UploadBlobElementReader*>( - (*upload->GetElementReaders())[2]); + (*upload->GetElementReaders())[2].get()); ASSERT_TRUE(r3); EXPECT_EQ("blobuuid", r3->uuid()); } @@ -158,7 +161,7 @@ TEST(UploadDataStreamBuilderTest, ResetUploadStreamWithBlob) { const std::string kBlob = "blobuuid"; const std::string kBlobData = "blobdata"; const int kBlobDataLength = 8; - const int64 kIdentifier = 12345; + const int64_t kIdentifier = 12345; BlobStorageContext blob_storage_context; BlobDataBuilder builder(kBlob); |