diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-03-08 10:28:10 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-03-20 13:40:30 +0000 |
commit | e733310db58160074f574c429d48f8308c0afe17 (patch) | |
tree | f8aef4b7e62a69928dbcf880620eece20f98c6df /chromium/content/browser/loader | |
parent | 2f583e4aec1ae3a86fa047829c96b310dc12ecdf (diff) | |
download | qtwebengine-chromium-e733310db58160074f574c429d48f8308c0afe17.tar.gz |
BASELINE: Update Chromium to 56.0.2924.122
Change-Id: I4e04de8f47e47e501c46ed934c76a431c6337ced
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/content/browser/loader')
53 files changed, 2505 insertions, 1430 deletions
diff --git a/chromium/content/browser/loader/DEPS b/chromium/content/browser/loader/DEPS index 1245eec0631..b655be17bdb 100644 --- a/chromium/content/browser/loader/DEPS +++ b/chromium/content/browser/loader/DEPS @@ -19,11 +19,11 @@ specific_include_rules = { "+content/browser/loader/resource_message_filter.h", "+content/browser/loader/resource_request_info_impl.h", "+content/common/content_export.h", + "+content/public/browser/resource_controller.h", "+content/public/browser/resource_dispatcher_host_delegate.h", "+content/public/common/resource_response.h", # TODO: these all have to be removed. - "+content/browser/host_zoom_map_impl.h", "+content/public/common/content_features.h", # TODO: To be replaced by mojo. @@ -88,6 +88,8 @@ specific_include_rules = { "+content/browser/loader/resource_request_info_impl.h", "+content/common/content_export.h", "+content/common/resource_request_completion_status.h", + "+content/public/browser/global_request_id.h", + "+content/public/browser/resource_controller.h", "+content/public/browser/resource_dispatcher_host_delegate.h", "+content/public/common/resource_response.h", "+content/common/url_loader.mojom.h", @@ -177,6 +179,7 @@ specific_include_rules = { "+content/browser/resource_context_impl.h", "+content/browser/service_worker/foreign_fetch_request_handler.h", "+content/browser/service_worker/link_header_support.h", + "+content/browser/service_worker/service_worker_navigation_handle_core.h", "+content/browser/service_worker/service_worker_request_handler.h", "+content/browser/streams/stream.h", "+content/browser/streams/stream_context.h", @@ -222,6 +225,7 @@ specific_include_rules = { "+content/common/content_export.h", "+content/public/browser/resource_controller.h", "+content/public/browser/resource_dispatcher_host_login_delegate.h", + "+content/public/common/browser_side_navigation_policy.h", "+content/public/common/resource_response.h", "+content/public/common/resource_type.h", @@ -255,6 +259,7 @@ specific_include_rules = { # TODO: these all have to be removed. "+content/browser/frame_host/frame_tree_node.h", + "+content/browser/service_worker/service_worker_context_wrapper.h", "+content/browser/web_contents/web_contents_impl.h", "+content/common/net/url_request_service_worker_data.h", "+content/common/net/url_request_user_data.h", @@ -306,6 +311,7 @@ specific_include_rules = { "+content/browser/loader/test_url_loader_client.h", "+content/common/resource_request_completion_status.h", "+content/common/url_loader.mojom.h", + "+content/common/url_loader_factory.mojom.h", "+content/public/common/resource_response.h", ], "url_loader_factory_impl\.(cc|h)": [ diff --git a/chromium/content/browser/loader/async_resource_handler.cc b/chromium/content/browser/loader/async_resource_handler.cc index 75d9289257d..69df9938434 100644 --- a/chromium/content/browser/loader/async_resource_handler.cc +++ b/chromium/content/browser/loader/async_resource_handler.cc @@ -17,7 +17,6 @@ #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" #include "base/time/time.h" -#include "content/browser/host_zoom_map_impl.h" #include "content/browser/loader/netlog_observer.h" #include "content/browser/loader/resource_buffer.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" @@ -26,6 +25,7 @@ #include "content/common/resource_messages.h" #include "content/common/resource_request_completion_status.h" #include "content/common/view_messages.h" +#include "content/public/browser/resource_controller.h" #include "content/public/browser/resource_dispatcher_host_delegate.h" #include "content/public/common/content_features.h" #include "content/public/common/resource_response.h" @@ -351,21 +351,6 @@ bool AsyncResourceHandler::OnResponseStarted(ResourceResponse* response, NetLogObserver::PopulateResponseInfo(request(), response); response->head.encoded_data_length = request()->raw_header_size(); - const HostZoomMapImpl* host_zoom_map = - static_cast<const HostZoomMapImpl*>(info->filter()->GetHostZoomMap()); - - if (info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME && host_zoom_map) { - const GURL& request_url = request()->url(); - int render_process_id = info->GetChildID(); - int render_view_id = info->GetRouteID(); - - double zoom_level = host_zoom_map->GetZoomLevelForView( - request_url, render_process_id, render_view_id); - - info->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL( - render_view_id, request_url, zoom_level)); - } - // If the parent handler downloaded the resource to a file, grant the child // read permissions on it. if (!response->head.download_file_path.empty()) { diff --git a/chromium/content/browser/loader/async_resource_handler.h b/chromium/content/browser/loader/async_resource_handler.h index 023e262842d..c02a38c34aa 100644 --- a/chromium/content/browser/loader/async_resource_handler.h +++ b/chromium/content/browser/loader/async_resource_handler.h @@ -24,10 +24,7 @@ class URLRequest; namespace content { class ResourceBuffer; -class ResourceContext; class ResourceDispatcherHostImpl; -class ResourceMessageFilter; -class SharedIOBuffer; // Used to complete an asynchronous resource request in response to resource // load events from the resource dispatcher host. diff --git a/chromium/content/browser/loader/async_resource_handler_browsertest.cc b/chromium/content/browser/loader/async_resource_handler_browsertest.cc index d0e15f0bdb5..e1bc8fe68ab 100644 --- a/chromium/content/browser/loader/async_resource_handler_browsertest.cc +++ b/chromium/content/browser/loader/async_resource_handler_browsertest.cc @@ -69,9 +69,9 @@ class AsyncResourceHandlerBrowserTest : public ContentBrowserTest { IN_PROC_BROWSER_TEST_F(AsyncResourceHandlerBrowserTest, UploadProgress) { net::EmbeddedTestServer* test_server = embedded_test_server(); - ASSERT_TRUE(test_server->Start()); test_server->RegisterRequestHandler( base::Bind(&HandlePostAndRedirectURLs, kPostPath)); + ASSERT_TRUE(test_server->Start()); NavigateToURL(shell(), test_server->GetURL("/loader/async_resource_handler.html")); @@ -87,9 +87,9 @@ IN_PROC_BROWSER_TEST_F(AsyncResourceHandlerBrowserTest, UploadProgress) { IN_PROC_BROWSER_TEST_F(AsyncResourceHandlerBrowserTest, UploadProgressRedirect) { net::EmbeddedTestServer* test_server = embedded_test_server(); - ASSERT_TRUE(test_server->Start()); test_server->RegisterRequestHandler( base::Bind(&HandlePostAndRedirectURLs, kRedirectPostPath)); + ASSERT_TRUE(test_server->Start()); NavigateToURL(shell(), test_server->GetURL("/loader/async_resource_handler.html")); diff --git a/chromium/content/browser/loader/async_resource_handler_unittest.cc b/chromium/content/browser/loader/async_resource_handler_unittest.cc index 9764dfdc9b6..9662afd54b7 100644 --- a/chromium/content/browser/loader/async_resource_handler_unittest.cc +++ b/chromium/content/browser/loader/async_resource_handler_unittest.cc @@ -14,7 +14,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/feature_list.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/memory/ptr_util.h" @@ -23,6 +22,7 @@ #include "base/process/process.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" +#include "base/test/scoped_feature_list.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_loader.h" #include "content/browser/loader/resource_loader_delegate.h" @@ -97,7 +97,6 @@ class RecordingResourceMessageFilter : public ResourceMessageFilter { nullptr, nullptr, nullptr, - nullptr, base::Bind(&RecordingResourceMessageFilter::GetContexts, base::Unretained(this))), resource_context_(resource_context), @@ -143,6 +142,8 @@ class AsyncResourceHandlerTest : public ::testing::Test, : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), context_(true) {} void TearDown() override { + if (filter_) + filter_->OnChannelClosing(); // Prevent memory leaks. filter_ = nullptr; rdh_.Shutdown(); @@ -263,11 +264,9 @@ TEST_F(AsyncResourceHandlerTest, OneChunkLengths) { TEST_F(AsyncResourceHandlerTest, InlinedChunkLengths) { // TODO(ricea): Remove this Feature-enabling code once the feature is on by // default. - auto feature_list = base::MakeUnique<base::FeatureList>(); - feature_list->InitializeFromCommandLine( - features::kOptimizeLoadingIPCForSmallResources.name, ""); - base::FeatureList::ClearInstanceForTesting(); - base::FeatureList::SetInstance(std::move(feature_list)); + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kOptimizeLoadingIPCForSmallResources); // Smaller than kInlinedLeadingChunkSize. StartRequestAndWaitWithResponseDataSize(8); diff --git a/chromium/content/browser/loader/async_revalidation_driver.h b/chromium/content/browser/loader/async_revalidation_driver.h index babd3f78e9d..a5bf1ec7c4e 100644 --- a/chromium/content/browser/loader/async_revalidation_driver.h +++ b/chromium/content/browser/loader/async_revalidation_driver.h @@ -19,10 +19,6 @@ #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 diff --git a/chromium/content/browser/loader/async_revalidation_manager.cc b/chromium/content/browser/loader/async_revalidation_manager.cc index 53279fe1320..abdf659fd22 100644 --- a/chromium/content/browser/loader/async_revalidation_manager.cc +++ b/chromium/content/browser/loader/async_revalidation_manager.cc @@ -109,7 +109,7 @@ void AsyncRevalidationManager::BeginAsyncRevalidation( // Construct the request. std::unique_ptr<net::URLRequest> new_request = request_context->CreateRequest( - for_request.url(), net::MINIMUM_PRIORITY, nullptr); + for_request.url(), net::IDLE, nullptr); new_request->set_method(for_request.method()); new_request->set_first_party_for_cookies( @@ -169,7 +169,7 @@ bool AsyncRevalidationManager::QualifiesForAsyncRevalidation( const ResourceRequest& request) { if (request.load_flags & (net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | - net::LOAD_VALIDATE_CACHE | net::LOAD_PREFERRING_CACHE | + net::LOAD_VALIDATE_CACHE | net::LOAD_SKIP_CACHE_VALIDATION | net::LOAD_ONLY_FROM_CACHE | net::LOAD_IGNORE_LIMITS | net::LOAD_PREFETCH)) { return false; diff --git a/chromium/content/browser/loader/async_revalidation_manager_browsertest.cc b/chromium/content/browser/loader/async_revalidation_manager_browsertest.cc index f02e6fca294..2646222c750 100644 --- a/chromium/content/browser/loader/async_revalidation_manager_browsertest.cc +++ b/chromium/content/browser/loader/async_revalidation_manager_browsertest.cc @@ -8,14 +8,15 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/feature_list.h" #include "base/macros.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 "base/test/scoped_feature_list.h" #include "content/public/common/browser_side_navigation_policy.h" +#include "content/public/common/content_features.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" @@ -43,20 +44,10 @@ class AsyncRevalidationManagerBrowserTest : public ContentBrowserTest { ~AsyncRevalidationManagerBrowserTest() override {} void SetUp() override { - base::FeatureList::ClearInstanceForTesting(); - std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); - feature_list->InitializeFromCommandLine( - "StaleWhileRevalidate2", std::string()); - base::FeatureList::SetInstance(std::move(feature_list)); - - ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); + scoped_feature_list_.InitAndEnableFeature(features::kStaleWhileRevalidate); ContentBrowserTest::SetUp(); } - void SetUpOnMainThread() override { - embedded_test_server()->StartAcceptingConnections(); - } - base::RunLoop* run_loop() { return &run_loop_; } int requests_counted() const { return requests_counted_; } @@ -151,6 +142,7 @@ class AsyncRevalidationManagerBrowserTest : public ContentBrowserTest { base::RunLoop run_loop_; int requests_counted_ = 0; + base::test::ScopedFeatureList scoped_feature_list_; DISALLOW_COPY_AND_ASSIGN(AsyncRevalidationManagerBrowserTest); }; @@ -159,11 +151,13 @@ class AsyncRevalidationManagerBrowserTest : public ContentBrowserTest { // triggers an async revalidation. IN_PROC_BROWSER_TEST_F(AsyncRevalidationManagerBrowserTest, StaleWhileRevalidateIsApplied) { + RegisterCountingRequestHandler(); + ASSERT_TRUE(embedded_test_server()->Start()); + // PlzNavigate: Stale while revalidate is disabled. // TODO(clamy): Re-enable the test when there is support. if (IsBrowserSideNavigationEnabled()) return; - RegisterCountingRequestHandler(); GURL url(embedded_test_server()->GetURL(kCountedHtmlPath)); EXPECT_TRUE(TitleBecomes(url, "Version 1")); @@ -186,12 +180,14 @@ IN_PROC_BROWSER_TEST_F(AsyncRevalidationManagerBrowserTest, // The fresh cache entry must become visible once the async revalidation request // has been sent. IN_PROC_BROWSER_TEST_F(AsyncRevalidationManagerBrowserTest, CacheIsUpdated) { + RegisterCountingRequestHandler(); + ASSERT_TRUE(embedded_test_server()->Start()); + // PlzNavigate: Stale while revalidate is disabled. // TODO(clamy): Re-enable the test when there is support. if (IsBrowserSideNavigationEnabled()) return; using base::ASCIIToUTF16; - RegisterCountingRequestHandler(); GURL url(embedded_test_server()->GetURL(kCountedHtmlPath)); EXPECT_TRUE(TitleBecomes(url, "Version 1")); @@ -218,11 +214,13 @@ IN_PROC_BROWSER_TEST_F(AsyncRevalidationManagerBrowserTest, CacheIsUpdated) { // applied immediately. IN_PROC_BROWSER_TEST_F(AsyncRevalidationManagerBrowserTest, CookieSetAsynchronously) { + RegisterCookieRequestHandler(); + ASSERT_TRUE(embedded_test_server()->Start()); + // PlzNavigate: Stale while revalidate is disabled. // TODO(clamy): Re-enable the test when there is support. if (IsBrowserSideNavigationEnabled()) return; - RegisterCookieRequestHandler(); GURL url(embedded_test_server()->GetURL(kCookieHtmlPath)); // Set cookie to version=1 diff --git a/chromium/content/browser/loader/async_revalidation_manager_unittest.cc b/chromium/content/browser/loader/async_revalidation_manager_unittest.cc index 392760d5690..b06f63ce168 100644 --- a/chromium/content/browser/loader/async_revalidation_manager_unittest.cc +++ b/chromium/content/browser/loader/async_revalidation_manager_unittest.cc @@ -212,7 +212,6 @@ class BlackholeFilter : public ResourceMessageFilter { nullptr, nullptr, nullptr, - nullptr, base::Bind(&BlackholeFilter::GetContexts, base::Unretained(this))), resource_context_(resource_context) { ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id()); @@ -248,6 +247,7 @@ ResourceRequest CreateResourceRequest(const char* method, request.method = std::string(method); request.url = url; request.first_party_for_cookies = url; // Bypass third-party cookie blocking. + request.request_initiator = url::Origin(url); // Ensure initiator is set. request.referrer_policy = blink::WebReferrerPolicyDefault; request.load_flags = 0; request.origin_pid = 0; @@ -289,6 +289,7 @@ class AsyncRevalidationManagerTest : public ::testing::Test { base::WrapUnique(new net::TestNetworkDelegate)) {} void TearDown() override { + filter_->OnChannelClosing(); host_.CancelRequestsForProcess(filter_->child_id()); host_.Shutdown(); host_.CancelRequestsForContext(browser_context_->GetResourceContext()); diff --git a/chromium/content/browser/loader/cross_site_resource_handler.cc b/chromium/content/browser/loader/cross_site_resource_handler.cc deleted file mode 100644 index ecffa2095fc..00000000000 --- a/chromium/content/browser/loader/cross_site_resource_handler.cc +++ /dev/null @@ -1,388 +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/cross_site_resource_handler.h" - -#include <string> -#include <utility> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "content/browser/appcache/appcache_interceptor.h" -#include "content/browser/child_process_security_policy_impl.h" -#include "content/browser/frame_host/cross_site_transferring_request.h" -#include "content/browser/frame_host/render_frame_host_impl.h" -#include "content/browser/loader/resource_dispatcher_host_impl.h" -#include "content/browser/loader/resource_request_info_impl.h" -#include "content/browser/site_instance_impl.h" -#include "content/browser/web_contents/web_contents_impl.h" -#include "content/common/site_isolation_policy.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/content_browser_client.h" -#include "content/public/browser/global_request_id.h" -#include "content/public/browser/resource_controller.h" -#include "content/public/browser/site_instance.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/resource_response.h" -#include "content/public/common/url_constants.h" -#include "net/http/http_response_headers.h" -#include "net/url_request/url_request.h" - -namespace content { - -namespace { - -bool leak_requests_for_testing_ = false; - -// The parameters to OnCrossSiteResponseHelper exceed the number of arguments -// base::Bind supports. -struct CrossSiteResponseParams { - CrossSiteResponseParams( - int render_frame_id, - const GlobalRequestID& global_request_id, - const std::vector<GURL>& transfer_url_chain, - const Referrer& referrer, - ui::PageTransition page_transition, - bool should_replace_current_entry) - : render_frame_id(render_frame_id), - global_request_id(global_request_id), - transfer_url_chain(transfer_url_chain), - referrer(referrer), - page_transition(page_transition), - should_replace_current_entry(should_replace_current_entry) { - } - - int render_frame_id; - GlobalRequestID global_request_id; - std::vector<GURL> transfer_url_chain; - Referrer referrer; - ui::PageTransition page_transition; - bool should_replace_current_entry; -}; - -void OnCrossSiteResponseHelper(const CrossSiteResponseParams& params) { - std::unique_ptr<CrossSiteTransferringRequest> cross_site_transferring_request( - new CrossSiteTransferringRequest(params.global_request_id)); - - RenderFrameHostImpl* rfh = - RenderFrameHostImpl::FromID(params.global_request_id.child_id, - params.render_frame_id); - if (rfh && rfh->is_active()) { - if (rfh->GetParent()) { - // We should only swap processes for subframes in --site-per-process mode. - // CrossSiteResourceHandler is not installed on subframe requests in - // default Chrome. - CHECK(SiteIsolationPolicy::AreCrossProcessFramesPossible()); - } - rfh->OnCrossSiteResponse( - 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_) { - // Some unit tests expect requests to be leaked in this case, so they can - // pass them along manually. - cross_site_transferring_request->ReleaseRequest(); - } -} - -// Returns whether a transfer is needed by doing a check on the UI thread. -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 || !rfh->is_active()) - return CrossSiteResourceHandler::NavigationDecision::CANCEL_REQUEST; - - 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 - -CrossSiteResourceHandler::CrossSiteResourceHandler( - std::unique_ptr<ResourceHandler> next_handler, - net::URLRequest* request) - : 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) {} - -CrossSiteResourceHandler::~CrossSiteResourceHandler() { - // Cleanup back-pointer stored on the request info. - GetRequestInfo()->set_cross_site_handler(NULL); -} - -bool CrossSiteResourceHandler::OnRequestRedirected( - const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) { - // We should not have started the transition before being redirected. - DCHECK(!in_cross_site_transition_); - return next_handler_->OnRequestRedirected(redirect_info, response, defer); -} - -bool CrossSiteResourceHandler::OnResponseStarted( - ResourceResponse* response, - bool* defer) { - response_ = response; - has_started_response_ = true; - - // Store this handler on the ExtraRequestInfo, so that RDH can call our - // ResumeResponse method when we are ready to resume. - ResourceRequestInfoImpl* info = GetRequestInfo(); - info->set_cross_site_handler(this); - - return OnNormalResponseStarted(response, defer); -} - -bool CrossSiteResourceHandler::OnNormalResponseStarted( - ResourceResponse* response, - bool* defer) { - // At this point, we know that the response is safe to send back to the - // renderer: it is not a download, and it has passed the SSL and safe - // browsing checks. - // We should not have already started the transition before now. - DCHECK(!in_cross_site_transition_); - - ResourceRequestInfoImpl* info = GetRequestInfo(); - - // The content embedder can decide that a transfer to a different process is - // required for this URL. If so, pause the response now. Other cross process - // navigations can proceed immediately, since we run the unload handler at - // commit time. Note that a process swap may no longer be necessary if we - // transferred back into the original process due to a redirect. - bool definitely_transfer = - GetContentClient()->browser()->ShouldSwapProcessesForRedirect( - info->GetContext(), request()->original_url(), request()->url()); - - // If this is a download, just pass the response through without doing a - // cross-site check. The renderer will see it is a download and abort the - // request. - // - // Similarly, HTTP 204 (No Content) responses leave us showing the previous - // page. We should allow the navigation to finish without running the unload - // handler or swapping in the pending RenderFrameHost. - // - // In both cases, any pending RenderFrameHost (if one was created for this - // navigation) will stick around until the next cross-site navigation, since - // we are unable to tell when to destroy it. - // See RenderFrameHostManager::RendererAbortedProvisionalLoad. - // - // TODO(davidben): Unify IsDownload() and is_stream(). Several places need to - // check for both and remembering about streams is error-prone. - if (info->IsDownload() || info->is_stream() || - (response->head.headers.get() && - response->head.headers->response_code() == 204)) { - return next_handler_->OnResponseStarted(response, defer); - } - - if (definitely_transfer) { - // Now that we know a transfer is needed and we have something to commit, we - // pause to let the UI thread set up the transfer. - StartCrossSiteTransition(response); - - // Defer loading until after the new renderer process has issued a - // corresponding request. - *defer = true; - OnDidDefer(); - return true; - } - - // In the site-per-process model, we may also decide (independently from the - // content embedder's ShouldSwapProcessesForRedirect decision above) that a - // process transfer is needed. For that we need to consult the navigation - // policy on the UI thread, so pause the response. Process transfers are - // skipped for WebUI processes for now, since e.g. chrome://settings has - // multiple "cross-site" chrome:// frames, and that doesn't yet work cross- - // process. - if (SiteIsolationPolicy::AreCrossProcessFramesPossible() && - !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( - info->GetChildID())) { - return DeferForNavigationPolicyCheck(info, response, defer); - } - - // No deferral needed. Pass the response through. - return next_handler_->OnResponseStarted(response, defer); -} - -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; - } -} - -bool CrossSiteResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - CHECK(!in_cross_site_transition_); - return next_handler_->OnReadCompleted(bytes_read, defer); -} - -void CrossSiteResourceHandler::OnResponseCompleted( - const net::URLRequestStatus& status, - bool* defer) { - if (!in_cross_site_transition_) { - // If we're not transferring, then we should pass this through. - next_handler_->OnResponseCompleted(status, defer); - return; - } - - // We have to buffer the call until after the transition completes. - completed_during_transition_ = true; - completed_status_ = status; - - // Defer to tell RDH not to notify the world or clean up the pending request. - // We will do so in ResumeResponse. - *defer = true; - OnDidDefer(); -} - -// We can now send the response to the new renderer, which will cause -// WebContentsImpl to swap in the new renderer and destroy the old one. -void CrossSiteResourceHandler::ResumeResponse() { - TRACE_EVENT_ASYNC_END0( - "navigation", "CrossSiteResourceHandler transition", this); - DCHECK(request()); - in_cross_site_transition_ = false; - ResourceRequestInfoImpl* info = GetRequestInfo(); - - if (has_started_response_) { - // Send OnResponseStarted to the new renderer. - DCHECK(response_.get()); - bool defer = false; - if (!next_handler_->OnResponseStarted(response_.get(), &defer)) { - controller()->Cancel(); - } else if (!defer) { - // Unpause the request to resume reading. Any further reads will be - // directed toward the new renderer. - ResumeIfDeferred(); - } - } - - // Remove ourselves from the ExtraRequestInfo. - info->set_cross_site_handler(NULL); - - // If the response completed during the transition, notify the next - // event handler. - if (completed_during_transition_) { - bool defer = false; - next_handler_->OnResponseCompleted(completed_status_, - &defer); - if (!defer) - ResumeIfDeferred(); - } -} - -// static -void CrossSiteResourceHandler::SetLeakRequestsForTesting( - bool leak_requests_for_testing) { - leak_requests_for_testing_ = leak_requests_for_testing; -} - -// Prepare to transfer the response to a new RenderFrameHost. -void CrossSiteResourceHandler::StartCrossSiteTransition( - ResourceResponse* response) { - TRACE_EVENT_ASYNC_BEGIN0( - "navigation", "CrossSiteResourceHandler transition", this); - in_cross_site_transition_ = true; - response_ = response; - - // Store this handler on the ExtraRequestInfo, so that RDH can call our - // ResumeResponse method when we are ready to resume. - ResourceRequestInfoImpl* info = GetRequestInfo(); - info->set_cross_site_handler(this); - - GlobalRequestID global_id(info->GetChildID(), info->GetRequestID()); - - // Tell the contents responsible for this request that a cross-site response - // is starting, so that it can tell its old renderer to run its onunload - // handler now. We will wait until the unload is finished and (if a transfer - // is needed) for the new renderer's request to arrive. - // The |transfer_url_chain| contains any redirect URLs that have already - // occurred, plus the destination URL at the end. - std::vector<GURL> transfer_url_chain; - Referrer referrer; - int render_frame_id = info->GetRenderFrameID(); - transfer_url_chain = request()->url_chain(); - referrer = Referrer(GURL(request()->referrer()), info->GetReferrerPolicy()); - ResourceDispatcherHostImpl::Get()->MarkAsTransferredNavigation(global_id, - response_); - - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind( - &OnCrossSiteResponseHelper, - CrossSiteResponseParams(render_frame_id, - global_id, - transfer_url_chain, - referrer, - info->GetPageTransition(), - info->should_replace_current_entry()))); -} - -bool CrossSiteResourceHandler::DeferForNavigationPolicyCheck( - ResourceRequestInfoImpl* info, - ResourceResponse* response, - bool* defer) { - // Store the response_ object internally, since the navigation is deferred - // regardless of whether it will be a transfer or not. - response_ = response; - - // Always defer the navigation to the UI thread to make a policy decision. - // It will send the result back to the IO thread to either resume or - // transfer it to a new renderer. - // TODO(nasko): If the UI thread result is that transfer is required, the - // IO thread will defer to the UI thread again through - // StartCrossSiteTransition. This is unnecessary and the policy check on the - // UI thread should be refactored to avoid the extra hop. - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::UI, - FROM_HERE, - base::Bind(&CheckNavigationPolicyOnUI, - request()->url(), - info->GetChildID(), - info->GetRenderFrameID()), - base::Bind(&CrossSiteResourceHandler::ResumeOrTransfer, - weak_ptr_factory_.GetWeakPtr())); - - // Defer loading until it is known whether the navigation will transfer - // to a new process or continue in the existing one. - *defer = true; - OnDidDefer(); - return true; -} - -void CrossSiteResourceHandler::ResumeIfDeferred() { - if (did_defer_) { - request()->LogUnblocked(); - did_defer_ = false; - controller()->Resume(); - } -} - -void CrossSiteResourceHandler::OnDidDefer() { - did_defer_ = true; - request()->LogBlockedBy("CrossSiteResourceHandler"); -} - -} // namespace content diff --git a/chromium/content/browser/loader/cross_site_resource_handler.h b/chromium/content/browser/loader/cross_site_resource_handler.h deleted file mode 100644 index 9030468841b..00000000000 --- a/chromium/content/browser/loader/cross_site_resource_handler.h +++ /dev/null @@ -1,97 +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_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" -#include "content/common/content_export.h" -#include "net/url_request/url_request_status.h" - -namespace net { -class URLRequest; -} - -namespace content { - -struct TransitionLayerData; - -// Ensures that responses are delayed for navigations that must be transferred -// to a different process. This handler wraps an AsyncEventHandler, and it sits -// inside SafeBrowsing and Buffered event handlers. This is important, so that -// it can intercept OnResponseStarted after we determine that a response is safe -// and not a download. -class CrossSiteResourceHandler : public LayeredResourceHandler { - public: - enum class NavigationDecision { - TRANSFER_REQUIRED, - USE_EXISTING_RENDERER, - CANCEL_REQUEST - }; - - CrossSiteResourceHandler(std::unique_ptr<ResourceHandler> next_handler, - net::URLRequest* request); - ~CrossSiteResourceHandler() override; - - // ResourceHandler implementation: - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override; - bool OnResponseStarted(ResourceResponse* response, bool* defer) override; - bool OnReadCompleted(int bytes_read, bool* defer) override; - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override; - - // We can now send the response to the new renderer, which will cause - // WebContentsImpl to swap in the new renderer and destroy the old one. - void ResumeResponse(); - - // When set to true, requests are leaked when they can't be passed to a - // RenderViewHost, for unit tests. - CONTENT_EXPORT static void SetLeakRequestsForTesting( - bool leak_requests_for_testing); - - private: - // Prepare to transfer the cross-site response to a new RenderFrameHost, by - // asking it to issue an identical request (on the UI thread). - void StartCrossSiteTransition(ResourceResponse* response); - - // Defer the navigation to the UI thread to check whether transfer is required - // or not. Currently only used in --site-per-process. - bool DeferForNavigationPolicyCheck(ResourceRequestInfoImpl* info, - ResourceResponse* response, - bool* defer); - - bool OnNormalResponseStarted(ResourceResponse* response, - bool* defer); - - void ResumeOrTransfer(NavigationDecision decision); - void ResumeIfDeferred(); - - // Called when about to defer a request. Sets |did_defer_| and logs the - // defferral - void OnDidDefer(); - - bool has_started_response_; - bool in_cross_site_transition_; - bool completed_during_transition_; - bool did_defer_; - net::URLRequestStatus completed_status_; - scoped_refptr<ResourceResponse> response_; - - // TODO(nasko): WeakPtr is needed in --site-per-process, since all navigations - // are deferred to the UI thread and come back to IO thread via - // PostTaskAndReplyWithResult. If a transfer is needed, it goes back to the UI - // thread. This can be removed once the code is changed to only do one hop. - base::WeakPtrFactory<CrossSiteResourceHandler> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(CrossSiteResourceHandler); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_CROSS_SITE_RESOURCE_HANDLER_H_ diff --git a/chromium/content/browser/loader/cross_site_resource_handler_browsertest.cc b/chromium/content/browser/loader/cross_site_resource_handler_browsertest.cc index d245cca7cfa..cfa809fb83a 100644 --- a/chromium/content/browser/loader/cross_site_resource_handler_browsertest.cc +++ b/chromium/content/browser/loader/cross_site_resource_handler_browsertest.cc @@ -12,6 +12,7 @@ #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_controller.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" @@ -193,8 +194,8 @@ class CrossSiteResourceHandlerTest : public ContentBrowserTest { &CrossSiteResourceHandlerTest::InjectResourceDispatcherHostDelegate, base::Unretained(this))); host_resolver()->AddRule("*", "127.0.0.1"); - ASSERT_TRUE(embedded_test_server()->Start()); content::SetupCrossSiteRedirector(embedded_test_server()); + ASSERT_TRUE(embedded_test_server()->Start()); } void TearDownOnMainThread() override { diff --git a/chromium/content/browser/loader/intercepting_resource_handler.cc b/chromium/content/browser/loader/intercepting_resource_handler.cc index 08907205397..7c8f4a99864 100644 --- a/chromium/content/browser/loader/intercepting_resource_handler.cc +++ b/chromium/content/browser/loader/intercepting_resource_handler.cc @@ -8,71 +8,58 @@ #include "base/strings/string_util.h" #include "content/public/common/resource_response.h" #include "net/base/io_buffer.h" +#include "net/url_request/url_request.h" namespace content { InterceptingResourceHandler::InterceptingResourceHandler( std::unique_ptr<ResourceHandler> next_handler, net::URLRequest* request) - : LayeredResourceHandler(request, std::move(next_handler)), - state_(State::STARTING), - first_read_buffer_size_(0) {} + : LayeredResourceHandler(request, std::move(next_handler)) { + next_handler_->SetController(this); +} InterceptingResourceHandler::~InterceptingResourceHandler() {} +void InterceptingResourceHandler::SetController( + ResourceController* controller) { + if (state_ == State::PASS_THROUGH) + return LayeredResourceHandler::SetController(controller); + ResourceHandler::SetController(controller); +} + bool InterceptingResourceHandler::OnResponseStarted(ResourceResponse* response, bool* defer) { // If there's no need to switch handlers, just start acting as a blind // pass-through ResourceHandler. if (!new_handler_) { - state_ = State::DONE; - first_read_buffer_ = nullptr; + state_ = State::PASS_THROUGH; + next_handler_->SetController(controller()); return next_handler_->OnResponseStarted(response, defer); } + DCHECK_EQ(state_, State::STARTING); // Otherwise, switch handlers. First, inform the original ResourceHandler // that this will be handled entirely by the new ResourceHandler. - // TODO(clamy): We will probably need to check the return values of these for - // PlzNavigate. bool defer_ignored = false; - next_handler_->OnResponseStarted(response, &defer_ignored); + if (!next_handler_->OnResponseStarted(response, &defer_ignored)) + return false; // Although deferring OnResponseStarted is legal, the only downstream handler // which does so is CrossSiteResourceHandler. Cross-site transitions should // not trigger when switching handlers. DCHECK(!defer_ignored); - // Make a copy of the data in the first read buffer. Despite not having been - // informed of any data being stored in first_read_buffer_, the - // MimeSniffingResourceHandler has read the data, it's just holding it back. - // This data should be passed to the alternate ResourceHandler and not to to - // the current ResourceHandler. - // TODO(clamy): see if doing the copy should be moved to the - // MimeSniffingResourceHandler. - if (first_read_buffer_) { - first_read_buffer_copy_ = new net::IOBuffer(first_read_buffer_size_); - memcpy(first_read_buffer_copy_->data(), first_read_buffer_->data(), - first_read_buffer_size_); - } - - // Send the payload to the old handler. - SendPayloadToOldHandler(); - first_read_buffer_ = nullptr; - - // The original ResourceHandler is now no longer needed, so replace it with - // the new one, before sending the response to the new one. - next_handler_ = std::move(new_handler_); - - state_ = - first_read_buffer_copy_ ? State::WAITING_FOR_BUFFER_COPY : State::DONE; - - return next_handler_->OnResponseStarted(response, defer); + // TODO(yhirano): Retaining ownership from a raw pointer is bad. + response_ = response; + state_ = State::SENDING_PAYLOAD_TO_OLD_HANDLER; + return DoLoop(defer); } bool InterceptingResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) { - if (state_ == State::DONE) + if (state_ == State::PASS_THROUGH) return next_handler_->OnWillRead(buf, buf_size, min_size); DCHECK_EQ(State::STARTING, state_); @@ -83,81 +70,213 @@ bool InterceptingResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, first_read_buffer_ = *buf; first_read_buffer_size_ = *buf_size; + first_read_buffer_double_ = new net::IOBuffer(static_cast<size_t>(*buf_size)); + *buf = first_read_buffer_double_; return true; } bool InterceptingResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { - DCHECK(bytes_read >= 0); - if (state_ == State::DONE) + DCHECK_GE(bytes_read, 0); + if (state_ == State::PASS_THROUGH) { + if (first_read_buffer_double_) { + // |first_read_buffer_double_| was allocated and the user wrote data to + // the buffer, but switching has not been done after all. + memcpy(first_read_buffer_->data(), first_read_buffer_double_->data(), + bytes_read); + first_read_buffer_ = nullptr; + first_read_buffer_double_ = nullptr; + } return next_handler_->OnReadCompleted(bytes_read, defer); + } - DCHECK_EQ(State::WAITING_FOR_BUFFER_COPY, state_); - state_ = State::DONE; + DCHECK_EQ(State::WAITING_FOR_ON_READ_COMPLETED, state_); + first_read_buffer_bytes_read_ = bytes_read; + state_ = State::SENDING_BUFFER_TO_NEW_HANDLER; + return DoLoop(defer); +} - // Copy the data from the first read to the new ResourceHandler. - scoped_refptr<net::IOBuffer> buf; - int buf_len = 0; - if (!next_handler_->OnWillRead(&buf, &buf_len, bytes_read)) - return false; +void InterceptingResourceHandler::OnResponseCompleted( + const net::URLRequestStatus& status, + bool* defer) { + if (state_ == State::PASS_THROUGH) { + LayeredResourceHandler::OnResponseCompleted(status, defer); + return; + } + if (!new_handler_) { + // Therer is only one ResourceHandler in this InterceptingResourceHandler. + state_ = State::PASS_THROUGH; + first_read_buffer_double_ = nullptr; + next_handler_->SetController(controller()); + next_handler_->OnResponseCompleted(status, defer); + return; + } - CHECK(buf_len >= bytes_read); - CHECK_GE(first_read_buffer_size_, static_cast<size_t>(bytes_read)); - memcpy(buf->data(), first_read_buffer_copy_->data(), bytes_read); + // There are two ResourceHandlers in this InterceptingResourceHandler. + // |next_handler_| is the old handler and |new_handler_| is the new handler. + // As written in the class comment, this class assumes that the old handler + // will not set |*defer| in OnResponseCompleted. + next_handler_->SetController(controller()); + next_handler_->OnResponseCompleted(status, defer); + DCHECK(!*defer); - first_read_buffer_copy_ = nullptr; + state_ = State::PASS_THROUGH; + first_read_buffer_double_ = nullptr; + new_handler_->SetController(controller()); + next_handler_ = std::move(new_handler_); + next_handler_->OnResponseCompleted(status, defer); +} - // TODO(clamy): Add a unit test to check that the deferral value is properly - // passed to the caller. - return next_handler_->OnReadCompleted(bytes_read, defer); +void InterceptingResourceHandler::Cancel() { + DCHECK_NE(State::PASS_THROUGH, state_); + controller()->Cancel(); +} + +void InterceptingResourceHandler::CancelAndIgnore() { + DCHECK_NE(State::PASS_THROUGH, state_); + controller()->CancelAndIgnore(); +} + +void InterceptingResourceHandler::CancelWithError(int error_code) { + DCHECK_NE(State::PASS_THROUGH, state_); + controller()->CancelWithError(error_code); +} + +void InterceptingResourceHandler::Resume() { + DCHECK_NE(State::PASS_THROUGH, state_); + if (state_ == State::STARTING || + state_ == State::WAITING_FOR_ON_READ_COMPLETED) { + // Uninteresting Resume: just delegate to the original resource controller. + controller()->Resume(); + return; + } + bool defer = false; + if (!DoLoop(&defer)) { + controller()->Cancel(); + return; + } + + if (!defer) + controller()->Resume(); } void InterceptingResourceHandler::UseNewHandler( std::unique_ptr<ResourceHandler> new_handler, const std::string& payload_for_old_handler) { new_handler_ = std::move(new_handler); - new_handler_->SetController(controller()); + new_handler_->SetController(this); payload_for_old_handler_ = payload_for_old_handler; } -void InterceptingResourceHandler::SendPayloadToOldHandler() { - bool defer_ignored = false; +bool InterceptingResourceHandler::DoLoop(bool* defer) { + bool result = true; + do { + switch (state_) { + case State::STARTING: + case State::WAITING_FOR_ON_READ_COMPLETED: + case State::PASS_THROUGH: + NOTREACHED(); + break; + case State::SENDING_ON_WILL_START_TO_NEW_HANDLER: + result = SendOnResponseStartedToNewHandler(defer); + break; + case State::SENDING_ON_RESPONSE_STARTED_TO_NEW_HANDLER: + if (first_read_buffer_double_) { + // OnWillRead has been called, so copying the data from + // |first_read_buffer_double_| to |first_read_buffer_| will be needed + // when OnReadCompleted is called. + state_ = State::WAITING_FOR_ON_READ_COMPLETED; + } else { + // OnWillRead has not been called, so no special handling will be + // needed from now on. + state_ = State::PASS_THROUGH; + next_handler_->SetController(controller()); + } + break; + case State::SENDING_PAYLOAD_TO_OLD_HANDLER: + result = SendPayloadToOldHandler(defer); + break; + case State::SENDING_BUFFER_TO_NEW_HANDLER: + result = SendFirstReadBufferToNewHandler(defer); + break; + } + } while (result && !*defer && + state_ != State::WAITING_FOR_ON_READ_COMPLETED && + state_ != State::PASS_THROUGH); + return result; +} + +bool InterceptingResourceHandler::SendPayloadToOldHandler(bool* defer) { + DCHECK_EQ(State::SENDING_PAYLOAD_TO_OLD_HANDLER, state_); + while (payload_bytes_written_ < payload_for_old_handler_.size()) { + scoped_refptr<net::IOBuffer> buffer; + int size = 0; + if (first_read_buffer_) { + // |first_read_buffer_| is a buffer gotten from |next_handler_| via + // OnWillRead. Use the buffer. + buffer = first_read_buffer_; + size = first_read_buffer_size_; + + first_read_buffer_ = nullptr; + first_read_buffer_size_ = 0; + } else { + if (!next_handler_->OnWillRead(&buffer, &size, -1)) + return false; + } + + size = std::min(size, static_cast<int>(payload_for_old_handler_.size() - + payload_bytes_written_)); + memcpy(buffer->data(), + payload_for_old_handler_.data() + payload_bytes_written_, size); + if (!next_handler_->OnReadCompleted(size, defer)) + return false; + payload_bytes_written_ += size; + if (*defer) + return true; + } + + net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0); if (payload_for_old_handler_.empty()) { // If there is no payload, just finalize the request on the old handler. - net::URLRequestStatus status(net::URLRequestStatus::CANCELED, - net::ERR_ABORTED); - next_handler_->OnResponseCompleted(status, &defer_ignored); - DCHECK(!defer_ignored); - return; + status = net::URLRequestStatus::FromError(net::ERR_ABORTED); } + next_handler_->OnResponseCompleted(status, defer); + DCHECK(!*defer); - // Ensure the old ResourceHandler has a buffer that can store the payload. - scoped_refptr<net::IOBuffer> buf; - int size = 0; - if (first_read_buffer_) { - // The first read buffer can be reused. The data inside it has been copied - // before calling this function, so it can safely be overriden. - buf = first_read_buffer_; - size = first_read_buffer_size_; - } + next_handler_ = std::move(new_handler_); + state_ = State::SENDING_ON_WILL_START_TO_NEW_HANDLER; + return next_handler_->OnWillStart(request()->url(), defer); +} - // If there is no first read buffer, ask the old ResourceHandler to create a - // buffer that can contain payload. - if (!buf) - next_handler_->OnWillRead(&buf, &size, -1); - - DCHECK(buf); - CHECK_GE(size, static_cast<int>(payload_for_old_handler_.length())); - memcpy(buf->data(), payload_for_old_handler_.c_str(), - payload_for_old_handler_.length()); - next_handler_->OnReadCompleted(payload_for_old_handler_.length(), - &defer_ignored); - payload_for_old_handler_ = std::string(); - DCHECK(!defer_ignored); +bool InterceptingResourceHandler::SendOnResponseStartedToNewHandler( + bool* defer) { + state_ = State::SENDING_ON_RESPONSE_STARTED_TO_NEW_HANDLER; + return next_handler_->OnResponseStarted(response_.get(), defer); +} - // Finalize the request. - net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0); - next_handler_->OnResponseCompleted(status, &defer_ignored); - DCHECK(!defer_ignored); +bool InterceptingResourceHandler::SendFirstReadBufferToNewHandler(bool* defer) { + DCHECK_EQ(state_, State::SENDING_BUFFER_TO_NEW_HANDLER); + + while (first_read_buffer_bytes_written_ < first_read_buffer_bytes_read_) { + scoped_refptr<net::IOBuffer> buf; + int size = 0; + if (!next_handler_->OnWillRead(&buf, &size, -1)) + return false; + size = std::min(size, static_cast<int>(first_read_buffer_bytes_read_ - + first_read_buffer_bytes_written_)); + memcpy(buf->data(), + first_read_buffer_double_->data() + first_read_buffer_bytes_written_, + size); + if (!next_handler_->OnReadCompleted(size, defer)) + return false; + first_read_buffer_bytes_written_ += size; + if (*defer) + return true; + } + + state_ = State::PASS_THROUGH; + first_read_buffer_double_ = nullptr; + next_handler_->SetController(controller()); + return true; } } // namespace content diff --git a/chromium/content/browser/loader/intercepting_resource_handler.h b/chromium/content/browser/loader/intercepting_resource_handler.h index 8729e5b271e..3c796a406f6 100644 --- a/chromium/content/browser/loader/intercepting_resource_handler.h +++ b/chromium/content/browser/loader/intercepting_resource_handler.h @@ -14,7 +14,9 @@ #include "content/browser/loader/layered_resource_handler.h" #include "content/browser/loader/resource_handler.h" #include "content/common/content_export.h" +#include "content/public/browser/resource_controller.h" #include "net/base/io_buffer.h" +#include "net/url_request/url_request_status.h" namespace net { class URLRequest; @@ -25,19 +27,34 @@ namespace content { // ResourceHandler that initiates special handling of the response if needed, // based on the response's MIME type (starts downloads, sends data to some // plugin types via a special channel). +// +// An InterceptingResourceHandler holds two handlers (|next_handler| and +// |new_handler|). It assumes the following: +// - OnResponseStarted on |next_handler| never sets |*defer|. +// - OnResponseCompleted on |next_handler| never sets |*defer|. class CONTENT_EXPORT InterceptingResourceHandler - : public LayeredResourceHandler { + : public LayeredResourceHandler, + public ResourceController { public: InterceptingResourceHandler(std::unique_ptr<ResourceHandler> next_handler, net::URLRequest* request); ~InterceptingResourceHandler() override; // ResourceHandler implementation: + void SetController(ResourceController* controller) override; bool OnResponseStarted(ResourceResponse* response, bool* defer) override; bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size, int min_size) override; bool OnReadCompleted(int bytes_read, bool* defer) override; + void OnResponseCompleted(const net::URLRequestStatus& status, + bool* defer) override; + + // ResourceController implementation: + void Cancel() override; + void CancelAndIgnore() override; + void CancelWithError(int error_code) override; + void Resume() override; // Replaces the next handler with |new_handler|, sending // |payload_for_old_handler| to the old handler. Must be called after @@ -54,38 +71,70 @@ class CONTENT_EXPORT InterceptingResourceHandler private: enum class State { - // In this state, the InterceptingResourceHandler is waiting for the mime - // type of the response to be identified, to check if the next handler - // should be replaced with an appropriate one. + // The InterceptingResourceHandler is waiting for the mime type of the + // response to be identified, to check if the next handler should be + // replaced with an appropriate one. STARTING, - // In this state, the InterceptingResourceHandler is waiting to copy the - // read buffer to the next handler if it was replaced. This is needed - // because MimeTypeResourceHandler may call OnWillRead before calling - // OnResponseStarted, that is before the InterceptingResourceHandler - // replaces the original ResourceHandler of the request. Therefore, the - // data read at that time should be copied to the new ResourceHandler. - WAITING_FOR_BUFFER_COPY, - - // In this state, the InterceptingResourceHandler has replaced its next - // ResourceHandler if needed, and has ensured the buffered read data was - // properly transmitted to the new ResourceHandler. The - // InterceptingResourceHandler now acts as a pass-through ResourceHandler. - DONE, + // The InterceptingResourceHandler is sending the payload given via + // UseNewHandler to the old handler and waiting for its completion via + // Resume(). + SENDING_PAYLOAD_TO_OLD_HANDLER, + + // The InterceptingResourcHandler is calling the new handler's + // OnResponseStarted method and waiting for its completion via Resume(). + // After completion, the InterceptingResourceHandler will transition to + // SENDING_ON_RESPONSE_STARTED_TO_NEW_HANDLER on success. + SENDING_ON_WILL_START_TO_NEW_HANDLER, + + // The InterceptingResourcHandler is calling the new handler's + // OnResponseStarted method and waiting for its completion via Resume(). + // After completion, the InterceptingResourceHandler will transition to + // WAITING_FOR_ON_READ_COMPLETED on success. + SENDING_ON_RESPONSE_STARTED_TO_NEW_HANDLER, + + // The InterceptingResourcHandler is waiting for OnReadCompleted to be + // called. + WAITING_FOR_ON_READ_COMPLETED, + + // The InterceptingResourceHandler is sending the old handler's contents to + // the new handler and waiting for its completion via Resume(). + SENDING_BUFFER_TO_NEW_HANDLER, + + // The InterceptingResourceHandler has replaced its next ResourceHandler if + // needed, and has ensured the buffered read data was properly transmitted + // to the new ResourceHandler. The InterceptingResourceHandler now acts as + // a pass-through ResourceHandler. + PASS_THROUGH, }; - void SendPayloadToOldHandler(); + // Runs necessary operations depending on |state_|. Returns false when an + // error happens, and set |*defer| to true if the operation continues upon + // return. + bool DoLoop(bool* defer); + + // The return value and |defer| has the same meaning as DoLoop. + bool SendPayloadToOldHandler(bool* defer); + bool SendFirstReadBufferToNewHandler(bool* defer); + bool SendOnResponseStartedToNewHandler(bool* defer); - State state_; + State state_ = State::STARTING; std::unique_ptr<ResourceHandler> new_handler_; std::string payload_for_old_handler_; + size_t payload_bytes_written_ = 0; // Result of the first read, that may have to be passed to an alternate // ResourceHandler instead of the original ResourceHandler. scoped_refptr<net::IOBuffer> first_read_buffer_; - size_t first_read_buffer_size_; - scoped_refptr<net::IOBuffer> first_read_buffer_copy_; + // Instead of |first_read_buffer_|, this handler creates a new IOBuffer with + // the same size and return it to the client. + scoped_refptr<net::IOBuffer> first_read_buffer_double_; + size_t first_read_buffer_size_ = 0; + size_t first_read_buffer_bytes_read_ = 0; + size_t first_read_buffer_bytes_written_ = 0; + + scoped_refptr<ResourceResponse> response_; DISALLOW_COPY_AND_ASSIGN(InterceptingResourceHandler); }; diff --git a/chromium/content/browser/loader/intercepting_resource_handler_unittest.cc b/chromium/content/browser/loader/intercepting_resource_handler_unittest.cc index 3ae9dead69e..9f2096ce76c 100644 --- a/chromium/content/browser/loader/intercepting_resource_handler_unittest.cc +++ b/chromium/content/browser/loader/intercepting_resource_handler_unittest.cc @@ -7,13 +7,16 @@ #include <stdint.h> #include <memory> +#include <string> #include "base/files/file_path.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" +#include "content/browser/loader/test_resource_handler.h" #include "content/public/browser/resource_controller.h" #include "content/public/browser/resource_request_info.h" #include "content/public/common/resource_response.h" @@ -30,102 +33,20 @@ namespace content { namespace { -class TestResourceHandler : public ResourceHandler { +class TestResourceController : public ResourceController { public: - // A test version of a ResourceHandler. |request_status| and - // |final_bytes_read| will be updated when the response is complete with the - // final status of the request received by the handler, and the total bytes - // the handler saw. - explicit TestResourceHandler(net::URLRequestStatus* request_status, - size_t* final_bytes_read) - : TestResourceHandler(request_status, - final_bytes_read, - true /* on_response_started_result */, - true /* on_will_read_result */, - true /* on_read_completed_result */) {} - - // This constructor allows to specify return values for OnResponseStarted, - // OnWillRead and OnReadCompleted. - TestResourceHandler(net::URLRequestStatus* request_status, - size_t* final_bytes_read, - bool on_response_started_result, - bool on_will_read_result, - bool on_read_completed_result) - : ResourceHandler(nullptr), - buffer_(new net::IOBuffer(2048)), - request_status_(request_status), - final_bytes_read_(final_bytes_read), - on_response_started_result_(on_response_started_result), - on_will_read_result_(on_will_read_result), - on_read_completed_result_(on_read_completed_result), - bytes_read_(0), - is_completed_(false) { - memset(buffer_->data(), '\0', 2048); - } - - ~TestResourceHandler() override {} - - void SetController(ResourceController* controller) override {} - - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override { - NOTREACHED(); - return false; - } - - bool OnResponseStarted(ResourceResponse* response, bool* defer) override { - EXPECT_FALSE(is_completed_); - return on_response_started_result_; - } - - bool OnWillStart(const GURL& url, bool* defer) override { - EXPECT_FALSE(is_completed_); - return true; - } - - bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, - int* buf_size, - int min_size) override { - EXPECT_FALSE(is_completed_); - *buf = buffer_; - *buf_size = 2048; - memset(buffer_->data(), '\0', 2048); - return on_will_read_result_; - } - - bool OnReadCompleted(int bytes_read, bool* defer) override { - EXPECT_FALSE(is_completed_); - EXPECT_LT(bytes_read, 2048); - bytes_read_ += bytes_read; - return on_read_completed_result_; - } - - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override { - EXPECT_FALSE(is_completed_); - is_completed_ = true; - *request_status_ = status; - *final_bytes_read_ = bytes_read_; - } - - void OnDataDownloaded(int bytes_downloaded) override { NOTREACHED(); } - - scoped_refptr<net::IOBuffer> buffer() const { return buffer_; } - - size_t bytes_read() const { return bytes_read_; } + TestResourceController() = default; + void Cancel() override {} + void CancelAndIgnore() override {} + void CancelWithError(int error_code) override {} + void Resume() override { ++resume_calls_; } + + int resume_calls() const { return resume_calls_; } private: - scoped_refptr<net::IOBuffer> buffer_; - net::URLRequestStatus* request_status_; - size_t* final_bytes_read_; - bool on_response_started_result_; - bool on_will_read_result_; - bool on_read_completed_result_; - size_t bytes_read_; - bool is_completed_; - - DISALLOW_COPY_AND_ASSIGN(TestResourceHandler); + int resume_calls_ = 0; + + DISALLOW_COPY_AND_ASSIGN(TestResourceController); }; class InterceptingResourceHandlerTest : public testing::Test { @@ -155,11 +76,10 @@ TEST_F(InterceptingResourceHandlerTest, NoSwitching) { false); // is_using_lofi net::URLRequestStatus old_handler_status; - size_t old_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> old_handler(new TestResourceHandler( - &old_handler_status, &old_handler_final_bytes_read)); + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); TestResourceHandler* old_test_handler = old_handler.get(); - scoped_refptr<net::IOBuffer> old_buffer = old_handler.get()->buffer(); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( new InterceptingResourceHandler(std::move(old_handler), request.get())); @@ -176,7 +96,7 @@ TEST_F(InterceptingResourceHandlerTest, NoSwitching) { const std::string kData = "The data"; EXPECT_NE(kData, std::string(old_test_handler->buffer()->data())); - ASSERT_EQ(read_buffer.get(), old_test_handler->buffer()); + ASSERT_NE(read_buffer.get(), old_test_handler->buffer()); ASSERT_GT(static_cast<size_t>(buf_size), kData.length()); memcpy(read_buffer->data(), kData.c_str(), kData.length()); @@ -203,7 +123,7 @@ TEST_F(InterceptingResourceHandlerTest, NoSwitching) { EXPECT_TRUE(intercepting_handler->OnReadCompleted(kData2.length(), &defer)); EXPECT_FALSE(defer); EXPECT_EQ(kData2, std::string(old_test_handler->buffer()->data())); - EXPECT_EQ(kData.length() + kData2.length(), old_test_handler->bytes_read()); + EXPECT_EQ(kData + kData2, old_handler_body); } // Tests that the data received is transmitted to the newly created @@ -225,9 +145,9 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchNoPayload) { false); // is_using_lofi net::URLRequestStatus old_handler_status; - size_t old_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> old_handler(new TestResourceHandler( - &old_handler_status, &old_handler_final_bytes_read)); + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); scoped_refptr<net::IOBuffer> old_buffer = old_handler.get()->buffer(); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( new InterceptingResourceHandler(std::move(old_handler), request.get())); @@ -243,17 +163,16 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchNoPayload) { EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); const std::string kData = "The data"; - ASSERT_EQ(read_buffer.get(), old_buffer.get()); + ASSERT_NE(read_buffer.get(), old_buffer.get()); ASSERT_GT(static_cast<size_t>(buf_size), kData.length()); memcpy(read_buffer->data(), kData.c_str(), kData.length()); // Simulate the MimeSniffingResourceHandler asking the // InterceptingResourceHandler to switch to a new handler. net::URLRequestStatus new_handler_status; - size_t new_handler_final_bytes_read = 0; + std::string new_handler_body; std::unique_ptr<TestResourceHandler> new_handler_scoped( - new TestResourceHandler(&new_handler_status, - &new_handler_final_bytes_read)); + new TestResourceHandler(&new_handler_status, &new_handler_body)); TestResourceHandler* new_test_handler = new_handler_scoped.get(); intercepting_handler->UseNewHandler(std::move(new_handler_scoped), std::string()); @@ -265,7 +184,7 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchNoPayload) { EXPECT_FALSE(old_handler_status.is_success()); EXPECT_EQ(net::ERR_ABORTED, old_handler_status.error()); - EXPECT_EQ(0ul, old_handler_final_bytes_read); + EXPECT_EQ(std::string(), old_handler_body); // It should not have received the download data yet. EXPECT_NE(kData, std::string(new_test_handler->buffer()->data())); @@ -289,7 +208,7 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchNoPayload) { EXPECT_TRUE(intercepting_handler->OnReadCompleted(kData2.length(), &defer)); EXPECT_FALSE(defer); EXPECT_EQ(kData2, std::string(new_test_handler->buffer()->data())); - EXPECT_EQ(kData.length() + kData2.length(), new_test_handler->bytes_read()); + EXPECT_EQ(kData + kData2, new_handler_body); } // Tests that the data received is transmitted to the newly created @@ -311,10 +230,9 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchWithPayload) { false); // is_using_lofi net::URLRequestStatus old_handler_status; - size_t old_handler_final_bytes_read = 0; + std::string old_handler_body; std::unique_ptr<TestResourceHandler> old_handler_scoped( - new TestResourceHandler(&old_handler_status, - &old_handler_final_bytes_read)); + new TestResourceHandler(&old_handler_status, &old_handler_body)); TestResourceHandler* old_handler = old_handler_scoped.get(); scoped_refptr<net::IOBuffer> old_buffer = old_handler->buffer(); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( @@ -332,7 +250,7 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchWithPayload) { EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); const std::string kData = "The data"; - ASSERT_EQ(read_buffer.get(), old_buffer.get()); + ASSERT_NE(read_buffer.get(), old_buffer.get()); ASSERT_GT(static_cast<size_t>(buf_size), kData.length()); memcpy(read_buffer->data(), kData.c_str(), kData.length()); @@ -340,16 +258,14 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchWithPayload) { // InterceptingResourceHandler to switch to a new handler. const std::string kPayload = "The payload"; net::URLRequestStatus new_handler_status; - size_t new_handler_final_bytes_read = 0; + std::string new_handler_body; std::unique_ptr<TestResourceHandler> new_handler_scoped( - new TestResourceHandler(&new_handler_status, - &new_handler_final_bytes_read)); + new TestResourceHandler(&new_handler_status, &new_handler_body)); TestResourceHandler* new_test_handler = new_handler_scoped.get(); intercepting_handler->UseNewHandler(std::move(new_handler_scoped), kPayload); // The old handler should not have received the payload yet. - ASSERT_FALSE(old_handler->bytes_read()); - EXPECT_NE(kPayload, std::string(old_buffer->data())); + ASSERT_EQ(std::string(), old_handler_body); // The response is received. The new ResourceHandler should be used to handle // the download. @@ -357,8 +273,7 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchWithPayload) { EXPECT_FALSE(defer); // The old handler should have received the payload. - ASSERT_TRUE(old_handler_final_bytes_read == kPayload.size()); - EXPECT_EQ(kPayload, std::string(old_buffer->data())); + EXPECT_EQ(kPayload, old_handler_body); EXPECT_TRUE(old_handler_status.is_success()); EXPECT_EQ(net::OK, old_handler_status.error()); @@ -385,7 +300,7 @@ TEST_F(InterceptingResourceHandlerTest, HandlerSwitchWithPayload) { EXPECT_TRUE(intercepting_handler->OnReadCompleted(kData2.length(), &defer)); EXPECT_FALSE(defer); EXPECT_EQ(kData2, std::string(new_test_handler->buffer()->data())); - EXPECT_EQ(kData.length() + kData2.length(), new_test_handler->bytes_read()); + EXPECT_EQ(kData + kData2, new_handler_body); } // Tests that the handler behaves properly if the old handler fails will read. @@ -406,13 +321,10 @@ TEST_F(InterceptingResourceHandlerTest, OldHandlerFailsWillRead) { false); // is_using_lofi net::URLRequestStatus old_handler_status; - size_t old_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> old_handler(new TestResourceHandler( - &old_handler_status, &old_handler_final_bytes_read, - true, // on_response_started - false, // on_will_read - true)); // on_read_completed - scoped_refptr<net::IOBuffer> old_buffer = old_handler.get()->buffer(); + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); + old_handler->set_on_will_read_result(false); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( new InterceptingResourceHandler(std::move(old_handler), request.get())); @@ -428,6 +340,61 @@ TEST_F(InterceptingResourceHandlerTest, OldHandlerFailsWillRead) { EXPECT_FALSE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); } +// Tests that the handler behaves properly if the new handler fails in +// OnWillStart. +TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsOnWillStart) { + net::URLRequestContext context; + std::unique_ptr<net::URLRequest> request(context.CreateRequest( + GURL("http://www.google.com"), net::DEFAULT_PRIORITY, nullptr)); + ResourceRequestInfo::AllocateForTesting(request.get(), + RESOURCE_TYPE_MAIN_FRAME, + nullptr, // context + 0, // render_process_id + 0, // render_view_id + 0, // render_frame_id + true, // is_main_frame + false, // parent_is_main_frame + true, // allow_download + true, // is_async + false); // is_using_lofi + + net::URLRequestStatus old_handler_status; + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); + scoped_refptr<net::IOBuffer> old_buffer = old_handler.get()->buffer(); + std::unique_ptr<InterceptingResourceHandler> intercepting_handler( + new InterceptingResourceHandler(std::move(old_handler), request.get())); + + scoped_refptr<ResourceResponse> response(new ResourceResponse); + + // Simulate the MimeSniffingResourceHandler buffering the data. + scoped_refptr<net::IOBuffer> read_buffer; + int buf_size = 0; + bool defer = false; + EXPECT_TRUE(intercepting_handler->OnWillStart(GURL(), &defer)); + EXPECT_FALSE(defer); + EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); + + const char kData[] = "The data"; + ASSERT_NE(read_buffer.get(), old_buffer.get()); + ASSERT_GT(static_cast<size_t>(buf_size), sizeof(kData)); + memcpy(read_buffer->data(), kData, sizeof(kData)); + + // Simulate the MimeSniffingResourceHandler asking the + // InterceptingResourceHandler to switch to a new handler. + net::URLRequestStatus new_handler_status; + std::string new_handler_body; + std::unique_ptr<TestResourceHandler> new_handler( + new TestResourceHandler(&new_handler_status, &new_handler_body)); + new_handler->set_on_will_start_result(false); + intercepting_handler->UseNewHandler(std::move(new_handler), std::string()); + + // The response is received. The new ResourceHandler should tell us to fail. + EXPECT_FALSE(intercepting_handler->OnResponseStarted(response.get(), &defer)); + EXPECT_FALSE(defer); +} + // Tests that the handler behaves properly if the new handler fails response // started. TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsResponseStarted) { @@ -447,9 +414,9 @@ TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsResponseStarted) { false); // is_using_lofi net::URLRequestStatus old_handler_status; - size_t old_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> old_handler(new TestResourceHandler( - &old_handler_status, &old_handler_final_bytes_read)); + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); scoped_refptr<net::IOBuffer> old_buffer = old_handler.get()->buffer(); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( new InterceptingResourceHandler(std::move(old_handler), request.get())); @@ -465,19 +432,17 @@ TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsResponseStarted) { EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); const char kData[] = "The data"; - ASSERT_EQ(read_buffer.get(), old_buffer.get()); + ASSERT_NE(read_buffer.get(), old_buffer.get()); ASSERT_GT(static_cast<size_t>(buf_size), sizeof(kData)); memcpy(read_buffer->data(), kData, sizeof(kData)); // Simulate the MimeSniffingResourceHandler asking the // InterceptingResourceHandler to switch to a new handler. net::URLRequestStatus new_handler_status; - size_t new_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> new_handler(new TestResourceHandler( - &new_handler_status, &new_handler_final_bytes_read, - false, // on_response_started - true, // on_will_read - true)); // on_read_completed + std::string new_handler_body; + std::unique_ptr<TestResourceHandler> new_handler( + new TestResourceHandler(&new_handler_status, &new_handler_body)); + new_handler->set_on_response_started_result(false); intercepting_handler->UseNewHandler(std::move(new_handler), std::string()); // The response is received. The new ResourceHandler should tell us to fail. @@ -503,9 +468,9 @@ TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsWillRead) { false); // is_using_lofi net::URLRequestStatus old_handler_status; - size_t old_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> old_handler(new TestResourceHandler( - &old_handler_status, &old_handler_final_bytes_read)); + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); scoped_refptr<net::IOBuffer> old_buffer = old_handler.get()->buffer(); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( new InterceptingResourceHandler(std::move(old_handler), request.get())); @@ -521,19 +486,17 @@ TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsWillRead) { EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); const char kData[] = "The data"; - ASSERT_EQ(read_buffer.get(), old_buffer.get()); + ASSERT_NE(read_buffer.get(), old_buffer.get()); ASSERT_GT(static_cast<size_t>(buf_size), sizeof(kData)); memcpy(read_buffer->data(), kData, sizeof(kData)); // Simulate the MimeSniffingResourceHandler asking the // InterceptingResourceHandler to switch to a new handler. net::URLRequestStatus new_handler_status; - size_t new_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> new_handler(new TestResourceHandler( - &new_handler_status, &new_handler_final_bytes_read, - true, // on_response_started - false, // on_will_read - true)); // on_read_completed + std::string new_handler_body; + std::unique_ptr<TestResourceHandler> new_handler( + new TestResourceHandler(&new_handler_status, &new_handler_body)); + new_handler->set_on_will_read_result(false); intercepting_handler->UseNewHandler(std::move(new_handler), std::string()); // The response is received. The new handler should not have been asked to @@ -568,9 +531,9 @@ TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsReadCompleted) { false); // is_using_lofi net::URLRequestStatus old_handler_status; - size_t old_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> old_handler(new TestResourceHandler( - &old_handler_status, &old_handler_final_bytes_read)); + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); scoped_refptr<net::IOBuffer> old_buffer = old_handler.get()->buffer(); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( new InterceptingResourceHandler(std::move(old_handler), request.get())); @@ -586,19 +549,17 @@ TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsReadCompleted) { EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); const char kData[] = "The data"; - ASSERT_EQ(read_buffer.get(), old_buffer.get()); + ASSERT_NE(read_buffer.get(), old_buffer.get()); ASSERT_GT(static_cast<size_t>(buf_size), sizeof(kData)); memcpy(read_buffer->data(), kData, sizeof(kData)); // Simulate the MimeSniffingResourceHandler asking the // InterceptingResourceHandler to switch to a new handler. net::URLRequestStatus new_handler_status; - size_t new_handler_final_bytes_read = 0; - std::unique_ptr<TestResourceHandler> new_handler(new TestResourceHandler( - &new_handler_status, &new_handler_final_bytes_read, - true, // on_response_started - true, // on_will_read - false)); // on_read_completed + std::string new_handler_body; + std::unique_ptr<TestResourceHandler> new_handler( + new TestResourceHandler(&new_handler_status, &new_handler_body)); + new_handler->set_on_read_completed_result(false); intercepting_handler->UseNewHandler(std::move(new_handler), std::string()); // The response is received. @@ -613,6 +574,315 @@ TEST_F(InterceptingResourceHandlerTest, NewHandlerFailsReadCompleted) { EXPECT_FALSE(defer); } +// The old handler sets |defer| to true in OnReadCompleted and +// OnResponseCompleted. The new handler sets |defer| to true in +// OnResponseStarted and OnReadCompleted. +TEST_F(InterceptingResourceHandlerTest, DeferredOperations) { + net::URLRequestContext context; + std::unique_ptr<net::URLRequest> request(context.CreateRequest( + GURL("http://www.google.com"), net::DEFAULT_PRIORITY, nullptr)); + ResourceRequestInfo::AllocateForTesting(request.get(), + RESOURCE_TYPE_MAIN_FRAME, + nullptr, // context + 0, // render_process_id + 0, // render_view_id + 0, // render_frame_id + true, // is_main_frame + false, // parent_is_main_frame + true, // allow_download + true, // is_async + false); // is_using_lofi + + std::unique_ptr<TestResourceController> resource_controller = + base::MakeUnique<TestResourceController>(); + net::URLRequestStatus old_handler_status = {net::URLRequestStatus::IO_PENDING, + 0}; + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); + old_handler->SetBufferSize(10); + old_handler->set_defer_on_read_completed(true); + + scoped_refptr<net::IOBuffer> old_buffer = old_handler.get()->buffer(); + std::unique_ptr<InterceptingResourceHandler> intercepting_handler( + new InterceptingResourceHandler(std::move(old_handler), request.get())); + intercepting_handler->SetController(resource_controller.get()); + + scoped_refptr<ResourceResponse> response(new ResourceResponse); + + // Simulate the MimeSniffingResourceHandler buffering the data. + scoped_refptr<net::IOBuffer> read_buffer; + int buf_size = 0; + bool defer = false; + EXPECT_TRUE(intercepting_handler->OnWillStart(GURL(), &defer)); + EXPECT_FALSE(defer); + EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); + + const char kData[] = "The data"; + ASSERT_NE(read_buffer.get(), old_buffer.get()); + ASSERT_GT(static_cast<size_t>(buf_size), strlen(kData)); + memcpy(read_buffer->data(), kData, strlen(kData)); + + // Simulate the MimeSniffingResourceHandler asking the + // InterceptingResourceHandler to switch to a new handler. + net::URLRequestStatus new_handler_status = {net::URLRequestStatus::IO_PENDING, + 0}; + + std::string new_handler_body; + const std::string kPayload = "The long long long long long payload"; + ASSERT_GT(kPayload.size(), static_cast<size_t>(buf_size)); + std::unique_ptr<TestResourceHandler> new_handler( + new TestResourceHandler(&new_handler_status, &new_handler_body)); + new_handler->SetBufferSize(1); + new_handler->set_defer_on_will_start(true); + new_handler->set_defer_on_response_started(true); + new_handler->set_defer_on_read_completed(true); + new_handler->set_defer_on_response_completed(true); + intercepting_handler->UseNewHandler(std::move(new_handler), kPayload); + + // The response is received, and then deferred by the old handler's + // OnReadCompleted method. + ASSERT_TRUE(intercepting_handler->OnResponseStarted(response.get(), &defer)); + ASSERT_TRUE(defer); + + // The old handler has received the first N bytes of the payload synchronously + // where N is the size of the buffer exposed via OnWillRead. + EXPECT_EQ("The long l", old_handler_body); + EXPECT_EQ(std::string(), new_handler_body); + EXPECT_EQ(old_handler_status.status(), net::URLRequestStatus::IO_PENDING); + EXPECT_EQ(new_handler_status.status(), net::URLRequestStatus::IO_PENDING); + + // Run until the new handler's OnWillStart method defers the request. + intercepting_handler->Resume(); + EXPECT_EQ(0, resource_controller->resume_calls()); + EXPECT_EQ(kPayload, old_handler_body); + EXPECT_EQ(std::string(), new_handler_body); + EXPECT_EQ(old_handler_status.status(), net::URLRequestStatus::SUCCESS); + EXPECT_EQ(new_handler_status.status(), net::URLRequestStatus::IO_PENDING); + + // Run until the new handler's OnResponseStarted method defers the request. + intercepting_handler->Resume(); + EXPECT_EQ(0, resource_controller->resume_calls()); + EXPECT_EQ(std::string(), new_handler_body); + EXPECT_EQ(old_handler_status.status(), net::URLRequestStatus::SUCCESS); + EXPECT_EQ(new_handler_status.status(), net::URLRequestStatus::IO_PENDING); + + // Resuming should finally call back into the ResourceController. + intercepting_handler->Resume(); + EXPECT_EQ(1, resource_controller->resume_calls()); + + // Data is read, the new handler defers completion of the read. + defer = false; + ASSERT_TRUE(intercepting_handler->OnReadCompleted(strlen(kData), &defer)); + ASSERT_TRUE(defer); + + EXPECT_EQ(kPayload, old_handler_body); + EXPECT_EQ("T", new_handler_body); + + intercepting_handler->Resume(); + EXPECT_EQ(2, resource_controller->resume_calls()); + EXPECT_EQ(kPayload, old_handler_body); + EXPECT_EQ(kData, new_handler_body); + + EXPECT_EQ(old_handler_status.status(), net::URLRequestStatus::SUCCESS); + EXPECT_EQ(new_handler_status.status(), net::URLRequestStatus::IO_PENDING); + + defer = false; + intercepting_handler->OnResponseCompleted({net::URLRequestStatus::SUCCESS, 0}, + &defer); + ASSERT_TRUE(defer); + EXPECT_EQ(old_handler_status.status(), net::URLRequestStatus::SUCCESS); + EXPECT_EQ(new_handler_status.status(), net::URLRequestStatus::SUCCESS); +} + +// Test cancellation where there is only the old handler in an +// InterceptingResourceHandler. +TEST_F(InterceptingResourceHandlerTest, CancelOldHandler) { + net::URLRequestContext context; + std::unique_ptr<net::URLRequest> request(context.CreateRequest( + GURL("http://www.google.com"), net::DEFAULT_PRIORITY, nullptr)); + ResourceRequestInfo::AllocateForTesting(request.get(), + RESOURCE_TYPE_MAIN_FRAME, + nullptr, // context + 0, // render_process_id + 0, // render_view_id + 0, // render_frame_id + true, // is_main_frame + false, // parent_is_main_frame + true, // allow_download + true, // is_async + false); // is_using_lofi + + std::unique_ptr<TestResourceController> resource_controller = + base::MakeUnique<TestResourceController>(); + net::URLRequestStatus old_handler_status = {net::URLRequestStatus::IO_PENDING, + 0}; + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); + + std::unique_ptr<InterceptingResourceHandler> intercepting_handler( + new InterceptingResourceHandler(std::move(old_handler), request.get())); + intercepting_handler->SetController(resource_controller.get()); + + EXPECT_EQ(net::URLRequestStatus::IO_PENDING, old_handler_status.status()); + + bool defer = false; + intercepting_handler->OnResponseCompleted( + {net::URLRequestStatus::CANCELED, net::ERR_FAILED}, &defer); + ASSERT_FALSE(defer); + EXPECT_EQ(0, resource_controller->resume_calls()); + EXPECT_EQ(net::URLRequestStatus::CANCELED, old_handler_status.status()); +} + +// Test cancellation where there is only the new handler in an +// InterceptingResourceHandler. +TEST_F(InterceptingResourceHandlerTest, CancelNewHandler) { + net::URLRequestContext context; + std::unique_ptr<net::URLRequest> request(context.CreateRequest( + GURL("http://www.google.com"), net::DEFAULT_PRIORITY, nullptr)); + ResourceRequestInfo::AllocateForTesting(request.get(), + RESOURCE_TYPE_MAIN_FRAME, + nullptr, // context + 0, // render_process_id + 0, // render_view_id + 0, // render_frame_id + true, // is_main_frame + false, // parent_is_main_frame + true, // allow_download + true, // is_async + false); // is_using_lofi + + std::unique_ptr<TestResourceController> resource_controller = + base::MakeUnique<TestResourceController>(); + net::URLRequestStatus old_handler_status = {net::URLRequestStatus::IO_PENDING, + 0}; + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); + + std::unique_ptr<InterceptingResourceHandler> intercepting_handler( + new InterceptingResourceHandler(std::move(old_handler), request.get())); + intercepting_handler->SetController(resource_controller.get()); + + scoped_refptr<ResourceResponse> response(new ResourceResponse); + + // Simulate the MimeSniffingResourceHandler buffering the data. + scoped_refptr<net::IOBuffer> read_buffer; + int buf_size = 0; + bool defer = false; + EXPECT_TRUE(intercepting_handler->OnWillStart(GURL(), &defer)); + EXPECT_FALSE(defer); + EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); + + const char kData[] = "The data"; + ASSERT_GT(static_cast<size_t>(buf_size), strlen(kData)); + memcpy(read_buffer->data(), kData, strlen(kData)); + + // Simulate the MimeSniffingResourceHandler asking the + // InterceptingResourceHandler to switch to a new handler. + net::URLRequestStatus new_handler_status = {net::URLRequestStatus::IO_PENDING, + 0}; + + std::string new_handler_body; + const std::string kPayload = "The payload"; + std::unique_ptr<TestResourceHandler> new_handler( + new TestResourceHandler(&new_handler_status, &new_handler_body)); + new_handler->SetBufferSize(1); + new_handler->set_defer_on_response_started(true); + new_handler->set_defer_on_response_completed(true); + intercepting_handler->UseNewHandler(std::move(new_handler), kPayload); + + // The response is received. + ASSERT_TRUE(intercepting_handler->OnResponseStarted(response.get(), &defer)); + ASSERT_TRUE(defer); + + EXPECT_EQ(net::URLRequestStatus::SUCCESS, old_handler_status.status()); + EXPECT_EQ(net::URLRequestStatus::IO_PENDING, new_handler_status.status()); + + defer = false; + intercepting_handler->OnResponseCompleted( + {net::URLRequestStatus::CANCELED, net::ERR_FAILED}, &defer); + ASSERT_TRUE(defer); + EXPECT_EQ(0, resource_controller->resume_calls()); + EXPECT_EQ(net::URLRequestStatus::SUCCESS, old_handler_status.status()); + EXPECT_EQ(net::URLRequestStatus::CANCELED, new_handler_status.status()); +} + +// Test cancellation where there are both the old and the new handlers in an +// InterceptingResourceHandler. +TEST_F(InterceptingResourceHandlerTest, CancelBothHandlers) { + net::URLRequestContext context; + std::unique_ptr<net::URLRequest> request(context.CreateRequest( + GURL("http://www.google.com"), net::DEFAULT_PRIORITY, nullptr)); + ResourceRequestInfo::AllocateForTesting(request.get(), + RESOURCE_TYPE_MAIN_FRAME, + nullptr, // context + 0, // render_process_id + 0, // render_view_id + 0, // render_frame_id + true, // is_main_frame + false, // parent_is_main_frame + true, // allow_download + true, // is_async + false); // is_using_lofi + + std::unique_ptr<TestResourceController> resource_controller = + base::MakeUnique<TestResourceController>(); + net::URLRequestStatus old_handler_status = {net::URLRequestStatus::IO_PENDING, + 0}; + std::string old_handler_body; + std::unique_ptr<TestResourceHandler> old_handler( + new TestResourceHandler(&old_handler_status, &old_handler_body)); + old_handler->set_defer_on_read_completed(true); + + std::unique_ptr<InterceptingResourceHandler> intercepting_handler( + new InterceptingResourceHandler(std::move(old_handler), request.get())); + intercepting_handler->SetController(resource_controller.get()); + + scoped_refptr<ResourceResponse> response(new ResourceResponse); + + // Simulate the MimeSniffingResourceHandler buffering the data. + scoped_refptr<net::IOBuffer> read_buffer; + int buf_size = 0; + bool defer = false; + EXPECT_TRUE(intercepting_handler->OnWillStart(GURL(), &defer)); + EXPECT_FALSE(defer); + EXPECT_TRUE(intercepting_handler->OnWillRead(&read_buffer, &buf_size, -1)); + + const char kData[] = "The data"; + ASSERT_GT(static_cast<size_t>(buf_size), strlen(kData)); + memcpy(read_buffer->data(), kData, strlen(kData)); + + // Simulate the MimeSniffingResourceHandler asking the + // InterceptingResourceHandler to switch to a new handler. + net::URLRequestStatus new_handler_status = {net::URLRequestStatus::IO_PENDING, + 0}; + + std::string new_handler_body; + const std::string kPayload = "The payload"; + std::unique_ptr<TestResourceHandler> new_handler( + new TestResourceHandler(&new_handler_status, &new_handler_body)); + new_handler->SetBufferSize(1); + new_handler->set_defer_on_response_completed(true); + intercepting_handler->UseNewHandler(std::move(new_handler), kPayload); + + // The response is received. + ASSERT_TRUE(intercepting_handler->OnResponseStarted(response.get(), &defer)); + ASSERT_TRUE(defer); + + EXPECT_EQ(net::URLRequestStatus::IO_PENDING, old_handler_status.status()); + EXPECT_EQ(net::URLRequestStatus::IO_PENDING, new_handler_status.status()); + + defer = false; + intercepting_handler->OnResponseCompleted( + {net::URLRequestStatus::CANCELED, net::ERR_FAILED}, &defer); + ASSERT_TRUE(defer); + EXPECT_EQ(0, resource_controller->resume_calls()); + EXPECT_EQ(net::URLRequestStatus::CANCELED, old_handler_status.status()); + EXPECT_EQ(net::URLRequestStatus::CANCELED, new_handler_status.status()); +} + } // namespace } // namespace content diff --git a/chromium/content/browser/loader/mime_sniffing_resource_handler.cc b/chromium/content/browser/loader/mime_sniffing_resource_handler.cc index bba9d01aa6a..3e5e414ff1c 100644 --- a/chromium/content/browser/loader/mime_sniffing_resource_handler.cc +++ b/chromium/content/browser/loader/mime_sniffing_resource_handler.cc @@ -406,7 +406,7 @@ bool MimeSniffingResourceHandler::MaybeStartInterception(bool* defer) { return true; } - // This is request is a download, + // This request is a download. if (!CheckResponseIsNotProvisional()) return false; diff --git a/chromium/content/browser/loader/mime_sniffing_resource_handler_unittest.cc b/chromium/content/browser/loader/mime_sniffing_resource_handler_unittest.cc index 4e8e912e4b1..92a8a1fca76 100644 --- a/chromium/content/browser/loader/mime_sniffing_resource_handler_unittest.cc +++ b/chromium/content/browser/loader/mime_sniffing_resource_handler_unittest.cc @@ -12,10 +12,12 @@ #include "base/location.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "content/browser/loader/intercepting_resource_handler.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/browser/loader/test_resource_handler.h" #include "content/public/browser/resource_controller.h" #include "content/public/browser/resource_dispatcher_host_delegate.h" #include "content/public/browser/resource_request_info.h" @@ -33,97 +35,6 @@ namespace content { namespace { -class TestResourceHandler : public ResourceHandler { - public: - TestResourceHandler(bool response_started_succeeds, - bool defer_on_response_started, - bool will_read_succeeds, - bool read_completed_succeeds, - bool defer_on_read_completed) - : ResourceHandler(nullptr), - buffer_(new net::IOBuffer(2048)), - response_started_succeeds_(response_started_succeeds), - defer_on_response_started_(defer_on_response_started), - will_read_succeeds_(will_read_succeeds), - read_completed_succeeds_(read_completed_succeeds), - defer_on_read_completed_(defer_on_read_completed), - on_will_start_called_(0), - on_request_redirected_called_(0), - on_response_started_called_(0), - on_will_read_called_(0), - on_read_completed_called_(0) {} - - void SetController(ResourceController* controller) override {} - - bool OnRequestRedirected(const net::RedirectInfo& redirect_info, - ResourceResponse* response, - bool* defer) override { - on_request_redirected_called_++; - NOTREACHED(); - return false; - } - - bool OnResponseStarted(ResourceResponse* response, bool* defer) override { - on_response_started_called_++; - if (defer_on_response_started_) - *defer = true; - return response_started_succeeds_; - } - - bool OnWillStart(const GURL& url, bool* defer) override { - on_will_start_called_++; - return false; - } - - bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, - int* buf_size, - int min_size) override { - on_will_read_called_++; - *buf = buffer_; - *buf_size = 2048; - return will_read_succeeds_; - } - - bool OnReadCompleted(int bytes_read, bool* defer) override { - DCHECK_LT(bytes_read, 2048); - on_read_completed_called_++; - if (defer_on_read_completed_) - *defer = true; - return read_completed_succeeds_; - } - - void OnResponseCompleted(const net::URLRequestStatus& status, - bool* defer) override {} - - void OnDataDownloaded(int bytes_downloaded) override { NOTREACHED(); } - - scoped_refptr<net::IOBuffer> buffer() { return buffer_; } - - int on_will_start_called() const { return on_will_start_called_; } - int on_request_redirected_called() const { - return on_request_redirected_called_; - } - int on_response_started_called() const { return on_response_started_called_; } - int on_will_read_called() const { return on_will_read_called_; } - int on_read_completed_called() const { return on_read_completed_called_; } - - private: - scoped_refptr<net::IOBuffer> buffer_; - bool response_started_succeeds_; - bool defer_on_response_started_; - bool will_read_succeeds_; - bool read_completed_succeeds_; - bool defer_on_read_completed_; - - int on_will_start_called_; - int on_request_redirected_called_; - int on_response_started_called_; - int on_will_read_called_; - int on_read_completed_called_; - - DISALLOW_COPY_AND_ASSIGN(TestResourceHandler); -}; - class TestResourceDispatcherHostDelegate : public ResourceDispatcherHostDelegate { public: @@ -179,7 +90,8 @@ class TestResourceDispatcherHost : public ResourceDispatcherHostImpl { private: std::unique_ptr<ResourceHandler> CreateNewResourceHandler() { std::unique_ptr<TestResourceHandler> new_resource_handler( - new TestResourceHandler(false, false, true, true, false)); + new TestResourceHandler()); + new_resource_handler->set_on_response_started_result(false); new_resource_handler_ = new_resource_handler.get(); return std::move(new_resource_handler); } @@ -340,12 +252,14 @@ MimeSniffingResourceHandlerTest::TestAcceptHeaderSettingWithURLRequest( true, // is_async false); // is_using_lofi + std::unique_ptr<TestResourceHandler> scoped_test_handler( + new TestResourceHandler()); + scoped_test_handler->set_on_response_started_result(false); + std::unique_ptr<ResourceHandler> mime_sniffing_handler( - new MimeSniffingResourceHandler( - std::unique_ptr<ResourceHandler>( - new TestResourceHandler(false, false, false, false, false)), - nullptr, nullptr, nullptr, request, - REQUEST_CONTEXT_TYPE_UNSPECIFIED)); + new MimeSniffingResourceHandler(std::move(scoped_test_handler), nullptr, + nullptr, nullptr, request, + REQUEST_CONTEXT_TYPE_UNSPECIFIED)); bool defer = false; mime_sniffing_handler->OnWillStart(request->url(), &defer); @@ -380,13 +294,16 @@ bool MimeSniffingResourceHandlerTest::TestStreamIsIntercepted( host.SetDelegate(&host_delegate); TestFakePluginService plugin_service(plugin_available_, plugin_stale_); + std::unique_ptr<InterceptingResourceHandler> intercepting_handler( - new InterceptingResourceHandler(std::unique_ptr<ResourceHandler>(), + new InterceptingResourceHandler(base::MakeUnique<TestResourceHandler>(), nullptr)); + std::unique_ptr<TestResourceHandler> scoped_test_handler( + new TestResourceHandler()); + scoped_test_handler->set_on_response_started_result(false); std::unique_ptr<ResourceHandler> mime_handler(new MimeSniffingResourceHandler( - std::unique_ptr<ResourceHandler>( - new TestResourceHandler(false, false, false, false, false)), - &host, &plugin_service, intercepting_handler.get(), request.get(), + std::unique_ptr<ResourceHandler>(std::move(scoped_test_handler)), &host, + &plugin_service, intercepting_handler.get(), request.get(), REQUEST_CONTEXT_TYPE_UNSPECIFIED)); TestResourceController resource_controller; @@ -397,6 +314,9 @@ bool MimeSniffingResourceHandlerTest::TestStreamIsIntercepted( response->head.mime_type = "application/pdf"; bool defer = false; + mime_handler->OnWillStart(request->url(), &defer); + EXPECT_FALSE(defer); + mime_handler->OnResponseStarted(response.get(), &defer); content::RunAllPendingInMessageLoop(); @@ -433,12 +353,16 @@ void MimeSniffingResourceHandlerTest::TestHandlerSniffing( TestFakePluginService plugin_service(plugin_available_, plugin_stale_); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( - new InterceptingResourceHandler(std::unique_ptr<ResourceHandler>(), + new InterceptingResourceHandler(base::MakeUnique<TestResourceHandler>(), nullptr)); - std::unique_ptr<TestResourceHandler> scoped_test_handler = - std::unique_ptr<TestResourceHandler>(new TestResourceHandler( - response_started, defer_response_started, will_read, read_completed, - defer_read_completed)); + + std::unique_ptr<TestResourceHandler> scoped_test_handler( + new TestResourceHandler()); + scoped_test_handler->set_on_response_started_result(response_started); + scoped_test_handler->set_defer_on_response_started(defer_response_started); + scoped_test_handler->set_on_will_read_result(will_read); + scoped_test_handler->set_on_read_completed_result(read_completed); + scoped_test_handler->set_defer_on_read_completed(defer_read_completed); TestResourceHandler* test_handler = scoped_test_handler.get(); std::unique_ptr<MimeSniffingResourceHandler> mime_sniffing_handler( new MimeSniffingResourceHandler(std::move(scoped_test_handler), &host, @@ -594,13 +518,16 @@ void MimeSniffingResourceHandlerTest::TestHandlerNoSniffing( TestFakePluginService plugin_service(plugin_available_, plugin_stale_); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( - new InterceptingResourceHandler(std::unique_ptr<ResourceHandler>(), + new InterceptingResourceHandler(base::MakeUnique<TestResourceHandler>(), nullptr)); - std::unique_ptr<TestResourceHandler> scoped_test_handler = - std::unique_ptr<TestResourceHandler>(new TestResourceHandler( - response_started, defer_response_started, will_read, read_completed, - defer_read_completed)); + std::unique_ptr<TestResourceHandler> scoped_test_handler( + new TestResourceHandler()); + scoped_test_handler->set_on_response_started_result(response_started); + scoped_test_handler->set_defer_on_response_started(defer_response_started); + scoped_test_handler->set_on_will_read_result(will_read); + scoped_test_handler->set_on_read_completed_result(read_completed); + scoped_test_handler->set_defer_on_read_completed(defer_read_completed); TestResourceHandler* test_handler = scoped_test_handler.get(); std::unique_ptr<MimeSniffingResourceHandler> mime_sniffing_handler( new MimeSniffingResourceHandler(std::move(scoped_test_handler), &host, @@ -988,18 +915,22 @@ TEST_F(MimeSniffingResourceHandlerTest, 304Handling) { TestFakePluginService plugin_service(false, false); std::unique_ptr<ResourceHandler> intercepting_handler( - new InterceptingResourceHandler(std::unique_ptr<ResourceHandler>(), + new InterceptingResourceHandler(base::MakeUnique<TestResourceHandler>(), nullptr)); std::unique_ptr<ResourceHandler> mime_handler(new MimeSniffingResourceHandler( - std::unique_ptr<ResourceHandler>( - new TestResourceHandler(true, false, true, true, false)), - &host, &plugin_service, + std::unique_ptr<ResourceHandler>(new TestResourceHandler()), &host, + &plugin_service, static_cast<InterceptingResourceHandler*>(intercepting_handler.get()), request.get(), REQUEST_CONTEXT_TYPE_UNSPECIFIED)); TestResourceController resource_controller; mime_handler->SetController(&resource_controller); + // Request starts. + bool defer = false; + mime_handler->OnWillStart(request->url(), &defer); + EXPECT_FALSE(defer); + // Simulate a 304 response. scoped_refptr<ResourceResponse> response(new ResourceResponse); // The MIME type isn't important but it shouldn't be empty. @@ -1008,7 +939,6 @@ TEST_F(MimeSniffingResourceHandlerTest, 304Handling) { // The response is received. No new ResourceHandler should be created to // handle the download. - bool defer = false; mime_handler->OnResponseStarted(response.get(), &defer); EXPECT_FALSE(defer); EXPECT_FALSE(host.new_resource_handler()); @@ -1036,14 +966,12 @@ TEST_F(MimeSniffingResourceHandlerTest, FetchShouldDisableMimeSniffing) { TestFakePluginService plugin_service(false, false); std::unique_ptr<InterceptingResourceHandler> intercepting_handler( - new InterceptingResourceHandler(nullptr, nullptr)); + new InterceptingResourceHandler(base::MakeUnique<TestResourceHandler>(), + nullptr)); std::unique_ptr<TestResourceHandler> scoped_test_handler( - new TestResourceHandler(false, // response_started - false, // defer_response_started - true, // will_read, - true, // read_completed, - false)); // defer_read_completed + new TestResourceHandler()); + scoped_test_handler->set_on_response_started_result(false); std::unique_ptr<ResourceHandler> mime_sniffing_handler( new MimeSniffingResourceHandler(std::move(scoped_test_handler), &host, &plugin_service, diff --git a/chromium/content/browser/loader/mojo_async_resource_handler.cc b/chromium/content/browser/loader/mojo_async_resource_handler.cc index b66c389d3ff..1288fe45136 100644 --- a/chromium/content/browser/loader/mojo_async_resource_handler.cc +++ b/chromium/content/browser/loader/mojo_async_resource_handler.cc @@ -16,9 +16,12 @@ #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/common/resource_request_completion_status.h" +#include "content/public/browser/global_request_id.h" +#include "content/public/browser/resource_controller.h" #include "content/public/browser/resource_dispatcher_host_delegate.h" #include "content/public/common/resource_response.h" #include "mojo/public/c/system/data_pipe.h" +#include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/system/data_pipe.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" @@ -103,14 +106,18 @@ class MojoAsyncResourceHandler::WriterIOBuffer final MojoAsyncResourceHandler::MojoAsyncResourceHandler( net::URLRequest* request, ResourceDispatcherHostImpl* rdh, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client) + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client) : ResourceHandler(request), rdh_(rdh), binding_(this, std::move(mojo_request)), url_loader_client_(std::move(url_loader_client)) { DCHECK(url_loader_client_); InitializeResourceBufferConstants(); + // This unretained pointer is safe, because |binding_| is owned by |this| and + // the callback will never be called after |this| is destroyed. + binding_.set_connection_error_handler( + base::Bind(&MojoAsyncResourceHandler::Cancel, base::Unretained(this))); } MojoAsyncResourceHandler::~MojoAsyncResourceHandler() { @@ -122,8 +129,24 @@ bool MojoAsyncResourceHandler::OnRequestRedirected( const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) { - // Not implemented. - return false; + // Unlike OnResponseStarted, OnRequestRedirected will NOT be preceded by + // OnWillRead. + DCHECK(!shared_writer_); + + *defer = true; + request()->LogBlockedBy("MojoAsyncResourceHandler"); + did_defer_on_redirect_ = true; + + NetLogObserver::PopulateResponseInfo(request(), response); + response->head.encoded_data_length = request()->GetTotalReceivedBytes(); + response->head.request_start = request()->creation_time(); + response->head.response_start = base::TimeTicks::Now(); + // TODO(davidben): Is it necessary to pass the new first party URL for + // cookies? The only case where it can change is top-level navigation requests + // and hopefully those will eventually all be owned by the browser. It's + // possible this is still needed while renderer-owned ones exist. + url_loader_client_->OnReceiveRedirect(redirect_info, response->head); + return true; } bool MojoAsyncResourceHandler::OnResponseStarted(ResourceResponse* response, @@ -213,8 +236,10 @@ bool MojoAsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { buffer_bytes_read_ = bytes_read; if (!CopyReadDataToDataPipe(defer)) return false; - if (*defer) - OnDefer(); + if (*defer) { + request()->LogBlockedBy("MojoAsyncResourceHandler"); + did_defer_on_writing_ = true; + } return true; } @@ -224,25 +249,42 @@ bool MojoAsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { // doesn't have |defer| parameter. if (!AllocateWriterIOBuffer(&buffer_, defer)) return false; - if (*defer) - OnDefer(); + if (*defer) { + request()->LogBlockedBy("MojoAsyncResourceHandler"); + did_defer_on_writing_ = true; + } return true; } void MojoAsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) { - // Not implemented. + int64_t total_received_bytes = request()->GetTotalReceivedBytes(); + int64_t bytes_to_report = + total_received_bytes - reported_total_received_bytes_; + reported_total_received_bytes_ = total_received_bytes; + DCHECK_LE(0, bytes_to_report); + + url_loader_client_->OnDataDownloaded(bytes_downloaded, bytes_to_report); } void MojoAsyncResourceHandler::FollowRedirect() { - NOTIMPLEMENTED(); -} + if (!request()->status().is_success()) { + DVLOG(1) << "FollowRedirect for invalid request"; + return; + } + if (!did_defer_on_redirect_) { + DVLOG(1) << "Malformed FollowRedirect request"; + ReportBadMessage("Malformed FollowRedirect request"); + return; + } -void MojoAsyncResourceHandler::Cancel() { - NOTIMPLEMENTED(); + DCHECK(!did_defer_on_writing_); + did_defer_on_redirect_ = false; + request()->LogUnblocked(); + controller()->Resume(); } -void MojoAsyncResourceHandler::ResumeForTesting() { - Resume(); +void MojoAsyncResourceHandler::OnWritableForTesting() { + OnWritable(MOJO_RESULT_OK); } void MojoAsyncResourceHandler::SetAllocationSizeForTesting(size_t size) { @@ -344,54 +386,56 @@ bool MojoAsyncResourceHandler::AllocateWriterIOBuffer( return true; } -void MojoAsyncResourceHandler::Resume() { - if (!did_defer_) +bool MojoAsyncResourceHandler::CheckForSufficientResource() { + if (has_checked_for_sufficient_resources_) + return true; + has_checked_for_sufficient_resources_ = true; + + if (rdh_->HasSufficientResourcesForRequest(request())) + return true; + + controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); + return false; +} + +void MojoAsyncResourceHandler::OnWritable(MojoResult result) { + if (!did_defer_on_writing_) return; - bool defer = false; + DCHECK(!did_defer_on_redirect_); + did_defer_on_writing_ = false; + if (is_using_io_buffer_not_from_writer_) { // |buffer_| is set to a net::IOBufferWithSize. Write the buffer contents // to the data pipe. DCHECK_GT(buffer_bytes_read_, 0u); - if (!CopyReadDataToDataPipe(&defer)) { + if (!CopyReadDataToDataPipe(&did_defer_on_writing_)) { controller()->CancelWithError(net::ERR_FAILED); return; } } else { // Allocate a buffer for the next OnWillRead call here. - if (!AllocateWriterIOBuffer(&buffer_, &defer)) { + if (!AllocateWriterIOBuffer(&buffer_, &did_defer_on_writing_)) { controller()->CancelWithError(net::ERR_FAILED); return; } } - if (defer) { + if (did_defer_on_writing_) { // Continue waiting. return; } - did_defer_ = false; request()->LogUnblocked(); controller()->Resume(); } -void MojoAsyncResourceHandler::OnDefer() { - request()->LogBlockedBy("MojoAsyncResourceHandler"); - did_defer_ = true; -} - -bool MojoAsyncResourceHandler::CheckForSufficientResource() { - if (has_checked_for_sufficient_resources_) - return true; - has_checked_for_sufficient_resources_ = true; - - if (rdh_->HasSufficientResourcesForRequest(request())) - return true; - - controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); - return false; +void MojoAsyncResourceHandler::Cancel() { + const ResourceRequestInfoImpl* info = GetRequestInfo(); + ResourceDispatcherHostImpl::Get()->CancelRequestFromRenderer( + GlobalRequestID(info->GetChildID(), info->GetRequestID())); } -void MojoAsyncResourceHandler::OnWritable(MojoResult unused) { - Resume(); +void MojoAsyncResourceHandler::ReportBadMessage(const std::string& error) { + mojo::ReportBadMessage(error); } } // namespace content diff --git a/chromium/content/browser/loader/mojo_async_resource_handler.h b/chromium/content/browser/loader/mojo_async_resource_handler.h index 5df87fd090e..ae47419a956 100644 --- a/chromium/content/browser/loader/mojo_async_resource_handler.h +++ b/chromium/content/browser/loader/mojo_async_resource_handler.h @@ -16,7 +16,7 @@ #include "content/browser/loader/resource_handler.h" #include "content/common/content_export.h" #include "content/common/url_loader.mojom.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/system/watcher.h" #include "net/base/io_buffer.h" #include "url/gurl.h" @@ -27,17 +27,13 @@ class URLRequest; namespace content { class ResourceDispatcherHostImpl; -class ResourceRequestInfoImpl; struct ResourceResponse; // Used to complete an asynchronous resource request in response to resource // load events from the resource dispatcher host. This class is used only // when LoadingWithMojo runtime flag is enabled. // -// TODO(yhirano): Implement redirects. -// TODO(yhirano): Implement downloading to file. // TODO(yhirano): Add histograms. -// TODO(yhirano): Set zoom level. // TODO(yhirano): Send cached metadata. // // This class can be inherited only for tests. @@ -48,8 +44,8 @@ class CONTENT_EXPORT MojoAsyncResourceHandler MojoAsyncResourceHandler( net::URLRequest* request, ResourceDispatcherHostImpl* rdh, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client); + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client); ~MojoAsyncResourceHandler() override; // ResourceHandler implementation: @@ -68,9 +64,8 @@ class CONTENT_EXPORT MojoAsyncResourceHandler // mojom::URLLoader implementation void FollowRedirect() override; - void Cancel() override; - void ResumeForTesting(); + void OnWritableForTesting(); static void SetAllocationSizeForTesting(size_t size); static constexpr size_t kDefaultAllocationSize = 512 * 1024; @@ -92,23 +87,26 @@ class CONTENT_EXPORT MojoAsyncResourceHandler bool AllocateWriterIOBuffer(scoped_refptr<net::IOBufferWithSize>* buf, bool* defer); - void Resume(); - void OnDefer(); bool CheckForSufficientResource(); void OnWritable(MojoResult result); + void Cancel(); + // This function can be overriden only for tests. + virtual void ReportBadMessage(const std::string& error); ResourceDispatcherHostImpl* rdh_; - mojo::Binding<mojom::URLLoader> binding_; + mojo::AssociatedBinding<mojom::URLLoader> binding_; bool has_checked_for_sufficient_resources_ = false; bool sent_received_response_message_ = false; bool is_using_io_buffer_not_from_writer_ = false; - bool did_defer_ = false; + bool did_defer_on_writing_ = false; + bool did_defer_on_redirect_ = false; base::TimeTicks response_started_ticks_; + int64_t reported_total_received_bytes_ = 0; mojo::Watcher handle_watcher_; std::unique_ptr<mojom::URLLoader> url_loader_; - mojom::URLLoaderClientPtr url_loader_client_; + mojom::URLLoaderClientAssociatedPtr url_loader_client_; scoped_refptr<net::IOBufferWithSize> buffer_; size_t buffer_offset_ = 0; size_t buffer_bytes_read_ = 0; diff --git a/chromium/content/browser/loader/mojo_async_resource_handler_unittest.cc b/chromium/content/browser/loader/mojo_async_resource_handler_unittest.cc index 5386d6e906f..4ad1ba638fe 100644 --- a/chromium/content/browser/loader/mojo_async_resource_handler_unittest.cc +++ b/chromium/content/browser/loader/mojo_async_resource_handler_unittest.cc @@ -33,6 +33,7 @@ #include "content/public/test/test_browser_thread_bundle.h" #include "mojo/public/c/system/data_pipe.h" #include "mojo/public/c/system/types.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/system/data_pipe.h" #include "net/base/auth.h" #include "net/base/net_errors.h" @@ -98,12 +99,7 @@ class TestResourceDispatcherHostDelegate final bool HandleExternalProtocol( const GURL& url, - int child_id, - const ResourceRequestInfo::WebContentsGetter& web_contents_getter, - bool is_main_frame, - ui::PageTransition page_transition, - bool has_user_gesture, - ResourceContext* resource_context) override { + ResourceRequestInfo* resource_request_info) override { ADD_FAILURE() << "HandleExternalProtocol should not be called."; return false; } @@ -189,6 +185,10 @@ class TestResourceController : public ResourceController { } void CancelWithError(int error_code) override { + // While cancelling more than once is legal, none of these tests should do + // it. + EXPECT_FALSE(is_cancel_with_error_called_); + is_cancel_with_error_called_ = true; error_ = error_code; if (quit_closure_) @@ -228,8 +228,8 @@ class MojoAsyncResourceHandlerWithCustomDataPipeOperations MojoAsyncResourceHandlerWithCustomDataPipeOperations( net::URLRequest* request, ResourceDispatcherHostImpl* rdh, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client) + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client) : MojoAsyncResourceHandler(request, rdh, std::move(mojo_request), @@ -246,6 +246,7 @@ class MojoAsyncResourceHandlerWithCustomDataPipeOperations is_end_write_expectation_set_ = true; end_write_expectation_ = end_write_expectation; } + bool has_received_bad_message() const { return has_received_bad_message_; } private: MojoResult BeginWrite(void** data, uint32_t* available) override { @@ -258,9 +259,13 @@ class MojoAsyncResourceHandlerWithCustomDataPipeOperations return end_write_expectation_; return MojoAsyncResourceHandler::EndWrite(written); } + void ReportBadMessage(const std::string& error) override { + has_received_bad_message_ = true; + } bool is_begin_write_expectation_set_ = false; bool is_end_write_expectation_set_ = false; + bool has_received_bad_message_ = false; MojoResult begin_write_expectation_ = MOJO_RESULT_UNKNOWN; MojoResult end_write_expectation_ = MOJO_RESULT_UNKNOWN; @@ -268,6 +273,43 @@ class MojoAsyncResourceHandlerWithCustomDataPipeOperations MojoAsyncResourceHandlerWithCustomDataPipeOperations); }; +class TestURLLoaderFactory final : public mojom::URLLoaderFactory { + public: + TestURLLoaderFactory() {} + ~TestURLLoaderFactory() override {} + + void CreateLoaderAndStart( + mojom::URLLoaderAssociatedRequest request, + int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info) override { + loader_request_ = std::move(request); + client_ptr_info_ = std::move(client_ptr_info); + } + + mojom::URLLoaderAssociatedRequest PassLoaderRequest() { + return std::move(loader_request_); + } + + mojom::URLLoaderClientAssociatedPtrInfo PassClientPtrInfo() { + return std::move(client_ptr_info_); + } + + void SyncLoad(int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + const SyncLoadCallback& callback) override { + NOTREACHED(); + } + + private: + mojom::URLLoaderAssociatedRequest loader_request_; + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info_; + + DISALLOW_COPY_AND_ASSIGN(TestURLLoaderFactory); +}; + class MojoAsyncResourceHandlerTestBase { public: MojoAsyncResourceHandlerTestBase() @@ -295,9 +337,28 @@ class MojoAsyncResourceHandlerTestBase { true, // is_async false // is_using_lofi ); + + ResourceRequest request; + base::WeakPtr<mojo::StrongBinding<mojom::URLLoaderFactory>> weak_binding = + mojo::MakeStrongBinding(base::MakeUnique<TestURLLoaderFactory>(), + mojo::GetProxy(&url_loader_factory_)); + + url_loader_factory_->CreateLoaderAndStart( + mojo::GetProxy(&url_loader_proxy_, + url_loader_factory_.associated_group()), + 0, 0, request, url_loader_client_.CreateRemoteAssociatedPtrInfo( + url_loader_factory_.associated_group())); + + url_loader_factory_.FlushForTesting(); + DCHECK(weak_binding); + TestURLLoaderFactory* factory_impl = + static_cast<TestURLLoaderFactory*>(weak_binding->impl()); + + mojom::URLLoaderClientAssociatedPtr client_ptr; + client_ptr.Bind(factory_impl->PassClientPtrInfo()); handler_.reset(new MojoAsyncResourceHandlerWithCustomDataPipeOperations( - request_.get(), &rdh_, nullptr, - url_loader_client_.CreateInterfacePtrAndBind())); + request_.get(), &rdh_, factory_impl->PassLoaderRequest(), + std::move(client_ptr))); handler_->SetController(&resource_controller_); } @@ -349,6 +410,8 @@ class MojoAsyncResourceHandlerTestBase { TestBrowserThreadBundle thread_bundle_; TestResourceDispatcherHostDelegate rdh_delegate_; ResourceDispatcherHostImpl rdh_; + mojom::URLLoaderFactoryPtr url_loader_factory_; + mojom::URLLoaderAssociatedPtr url_loader_proxy_; TestURLLoaderClient url_loader_client_; TestResourceController resource_controller_; std::unique_ptr<TestBrowserContext> browser_context_; @@ -781,7 +844,7 @@ TEST_F(MojoAsyncResourceHandlerTest, url_loader_client_.RunUntilResponseBodyArrived(); ASSERT_TRUE(url_loader_client_.response_body().is_valid()); handler_->ResetBeginWriteExpectation(); - handler_->ResumeForTesting(); + handler_->OnWritableForTesting(); std::string actual; while (actual.size() < written) { @@ -1020,6 +1083,76 @@ TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, CancelWhileWaiting) { EXPECT_EQ(0, resource_controller_.num_resume_calls()); } +TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, RedirectHandling) { + rdh_delegate_.set_num_on_response_started_calls_expectation(1); + bool defer = false; + + ASSERT_TRUE(handler_->OnWillStart(request_->url(), &defer)); + ASSERT_FALSE(defer); + scoped_refptr<ResourceResponse> response = new ResourceResponse(); + net::RedirectInfo redirect_info; + redirect_info.status_code = 301; + ASSERT_TRUE( + handler_->OnRequestRedirected(redirect_info, response.get(), &defer)); + ASSERT_TRUE(defer); + + ASSERT_FALSE(url_loader_client_.has_received_response()); + ASSERT_FALSE(url_loader_client_.has_received_redirect()); + url_loader_client_.RunUntilRedirectReceived(); + + ASSERT_FALSE(url_loader_client_.has_received_response()); + ASSERT_TRUE(url_loader_client_.has_received_redirect()); + EXPECT_EQ(301, url_loader_client_.redirect_info().status_code); + + EXPECT_EQ(0, resource_controller_.num_resume_calls()); + handler_->FollowRedirect(); + EXPECT_EQ(1, resource_controller_.num_resume_calls()); + + url_loader_client_.ClearHasReceivedRedirect(); + // Redirect once more. + defer = false; + redirect_info.status_code = 302; + ASSERT_TRUE( + handler_->OnRequestRedirected(redirect_info, response.get(), &defer)); + ASSERT_TRUE(defer); + + ASSERT_FALSE(url_loader_client_.has_received_response()); + ASSERT_FALSE(url_loader_client_.has_received_redirect()); + url_loader_client_.RunUntilRedirectReceived(); + + ASSERT_FALSE(url_loader_client_.has_received_response()); + ASSERT_TRUE(url_loader_client_.has_received_redirect()); + EXPECT_EQ(302, url_loader_client_.redirect_info().status_code); + + EXPECT_EQ(1, resource_controller_.num_resume_calls()); + handler_->FollowRedirect(); + EXPECT_EQ(2, resource_controller_.num_resume_calls()); + + // Give the final response. + defer = false; + ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); + ASSERT_FALSE(defer); + + net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); + handler_->OnResponseCompleted(status, &defer); + ASSERT_FALSE(defer); + + ASSERT_FALSE(url_loader_client_.has_received_completion()); + url_loader_client_.RunUntilComplete(); + + ASSERT_TRUE(url_loader_client_.has_received_response()); + ASSERT_TRUE(url_loader_client_.has_received_completion()); + EXPECT_EQ(net::OK, url_loader_client_.completion_status().error_code); +} + +TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, + MalformedFollowRedirectRequest) { + handler_->FollowRedirect(); + + EXPECT_TRUE(handler_->has_received_bad_message()); + EXPECT_EQ(0, resource_controller_.num_resume_calls()); +} + // Typically ResourceHandler methods are called in this order. TEST_P( MojoAsyncResourceHandlerWithAllocationSizeTest, diff --git a/chromium/content/browser/loader/navigation_resource_handler.cc b/chromium/content/browser/loader/navigation_resource_handler.cc index 76a5d8898db..dc73b854bea 100644 --- a/chromium/content/browser/loader/navigation_resource_handler.cc +++ b/chromium/content/browser/loader/navigation_resource_handler.cc @@ -14,7 +14,6 @@ #include "content/browser/resource_context_impl.h" #include "content/browser/streams/stream.h" #include "content/browser/streams/stream_context.h" -#include "content/common/security_style_util.h" #include "content/public/browser/navigation_data.h" #include "content/public/browser/resource_controller.h" #include "content/public/browser/resource_dispatcher_host_delegate.h" @@ -32,9 +31,7 @@ void NavigationResourceHandler::GetSSLStatusForRequest( int child_id, SSLStatus* ssl_status) { DCHECK(ssl_info.cert); - *ssl_status = SSLStatus(GetSecurityStyleForResource( - url, !!ssl_info.cert, ssl_info.cert_status), - ssl_info.cert, ssl_info); + *ssl_status = SSLStatus(ssl_info); } NavigationResourceHandler::NavigationResourceHandler( diff --git a/chromium/content/browser/loader/navigation_resource_throttle.cc b/chromium/content/browser/loader/navigation_resource_throttle.cc index 46e901fd694..e8f9e6ed25b 100644 --- a/chromium/content/browser/loader/navigation_resource_throttle.cc +++ b/chromium/content/browser/loader/navigation_resource_throttle.cc @@ -15,6 +15,8 @@ #include "content/browser/frame_host/navigator.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/loader/navigation_resource_handler.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/browser/loader/resource_loader.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_data.h" @@ -38,6 +40,9 @@ namespace { // NavigationHandle. bool g_ui_checks_always_succeed = false; +// Used in unit tests to transfer all navigations. +bool g_force_transfer = false; + typedef base::Callback<void(NavigationThrottle::ThrottleCheckResult)> UIChecksPerformedCallback; @@ -76,11 +81,9 @@ NavigationHandleImpl* FindNavigationHandle( } NavigationHandleImpl* navigation_handle = - render_frame_host->frame_tree_node() - ->navigator() - ->GetNavigationHandleForFrameHost(render_frame_host); + render_frame_host->navigation_handle(); if (!navigation_handle) { - SendCheckResultToIOThread(callback, NavigationThrottle::PROCEED); + SendCheckResultToIOThread(callback, NavigationThrottle::CANCEL); return nullptr; } return navigation_handle; @@ -118,7 +121,8 @@ void CheckWillRedirectRequestOnUIThread( const std::string& new_method, const GURL& new_referrer_url, bool new_is_external_protocol, - scoped_refptr<net::HttpResponseHeaders> headers) { + scoped_refptr<net::HttpResponseHeaders> headers, + net::HttpResponseInfo::ConnectionInfo connection_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); NavigationHandleImpl* navigation_handle = FindNavigationHandle(render_process_id, render_frame_host_id, callback); @@ -130,7 +134,8 @@ void CheckWillRedirectRequestOnUIThread( ->FilterURL(false, &new_validated_url); navigation_handle->WillRedirectRequest( new_validated_url, new_method, new_referrer_url, new_is_external_protocol, - headers, base::Bind(&SendCheckResultToIOThread, callback)); + headers, connection_info, + base::Bind(&SendCheckResultToIOThread, callback)); } void WillProcessResponseOnUIThread( @@ -138,9 +143,20 @@ void WillProcessResponseOnUIThread( int render_process_id, int render_frame_host_id, scoped_refptr<net::HttpResponseHeaders> headers, + net::HttpResponseInfo::ConnectionInfo connection_info, const SSLStatus& ssl_status, + const GlobalRequestID& request_id, + bool should_replace_current_entry, + bool is_download, + bool is_stream, + const base::Closure& transfer_callback, std::unique_ptr<NavigationData> navigation_data) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + + if (g_force_transfer) { + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, transfer_callback); + } + NavigationHandleImpl* navigation_handle = FindNavigationHandle(render_process_id, render_frame_host_id, callback); if (!navigation_handle) @@ -153,7 +169,8 @@ void WillProcessResponseOnUIThread( RenderFrameHostImpl::FromID(render_process_id, render_frame_host_id); DCHECK(render_frame_host); navigation_handle->WillProcessResponse( - render_frame_host, headers, ssl_status, + render_frame_host, headers, connection_info, ssl_status, request_id, + should_replace_current_entry, is_download, is_stream, transfer_callback, base::Bind(&SendCheckResultToIOThread, callback)); } @@ -166,6 +183,8 @@ NavigationResourceThrottle::NavigationResourceThrottle( : request_(request), resource_dispatcher_host_delegate_(resource_dispatcher_host_delegate), request_context_type_(request_context_type), + in_cross_site_transition_(false), + on_transfer_done_result_(NavigationThrottle::DEFER), weak_ptr_factory_(this) {} NavigationResourceThrottle::~NavigationResourceThrottle() {} @@ -238,13 +257,15 @@ void NavigationResourceThrottle::WillRedirectRequest( base::Bind(&CheckWillRedirectRequestOnUIThread, callback, render_process_id, render_frame_id, redirect_info.new_url, redirect_info.new_method, GURL(redirect_info.new_referrer), - new_is_external_protocol, response_headers)); + new_is_external_protocol, response_headers, + request_->response_info().connection_info)); *defer = true; } void NavigationResourceThrottle::WillProcessResponse(bool* defer) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); + const ResourceRequestInfoImpl* info = + ResourceRequestInfoImpl::ForRequest(request_); if (!info) return; @@ -274,6 +295,9 @@ void NavigationResourceThrottle::WillProcessResponse(bool* defer) { UIChecksPerformedCallback callback = base::Bind(&NavigationResourceThrottle::OnUIChecksPerformed, weak_ptr_factory_.GetWeakPtr()); + base::Closure transfer_callback = + base::Bind(&NavigationResourceThrottle::InitiateTransfer, + weak_ptr_factory_.GetWeakPtr()); SSLStatus ssl_status; if (request_->ssl_info().cert.get()) { @@ -284,7 +308,11 @@ void NavigationResourceThrottle::WillProcessResponse(bool* defer) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&WillProcessResponseOnUIThread, callback, render_process_id, - render_frame_id, response_headers, ssl_status, + render_frame_id, response_headers, + request_->response_info().connection_info, ssl_status, + info->GetGlobalRequestID(), + info->should_replace_current_entry(), info->IsDownload(), + info->is_stream(), transfer_callback, base::Passed(&cloned_data))); *defer = true; } @@ -298,9 +326,20 @@ void NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing( g_ui_checks_always_succeed = ui_checks_always_succeed; } +void NavigationResourceThrottle::set_force_transfer_for_testing( + bool force_transfer) { + g_force_transfer = force_transfer; +} + void NavigationResourceThrottle::OnUIChecksPerformed( NavigationThrottle::ThrottleCheckResult result) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK_NE(NavigationThrottle::DEFER, result); + if (in_cross_site_transition_) { + on_transfer_done_result_ = result; + return; + } + if (result == NavigationThrottle::CANCEL_AND_IGNORE) { controller()->CancelAndIgnore(); } else if (result == NavigationThrottle::CANCEL) { @@ -312,4 +351,28 @@ void NavigationResourceThrottle::OnUIChecksPerformed( } } +void NavigationResourceThrottle::InitiateTransfer() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + in_cross_site_transition_ = true; + ResourceRequestInfoImpl* info = + ResourceRequestInfoImpl::ForRequest(request_); + ResourceDispatcherHostImpl::Get()->MarkAsTransferredNavigation( + info->GetGlobalRequestID(), + base::Bind(&NavigationResourceThrottle::OnTransferComplete, + weak_ptr_factory_.GetWeakPtr())); +} + +void NavigationResourceThrottle::OnTransferComplete() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(in_cross_site_transition_); + in_cross_site_transition_ = false; + + // If the results of the checks on the UI thread are known, unblock the + // navigation. Otherwise, wait until the callback has executed. + if (on_transfer_done_result_ != NavigationThrottle::DEFER) { + OnUIChecksPerformed(on_transfer_done_result_); + on_transfer_done_result_ = NavigationThrottle::DEFER; + } +} + } // namespace content diff --git a/chromium/content/browser/loader/navigation_resource_throttle.h b/chromium/content/browser/loader/navigation_resource_throttle.h index e85a1fb9a09..a9685e347c7 100644 --- a/chromium/content/browser/loader/navigation_resource_throttle.h +++ b/chromium/content/browser/loader/navigation_resource_throttle.h @@ -42,12 +42,23 @@ class NavigationResourceThrottle : public ResourceThrottle { CONTENT_EXPORT static void set_ui_checks_always_succeed_for_testing( bool ui_checks_always_succeed); + // Used in unit tests to make all navigations transfer. + CONTENT_EXPORT static void set_force_transfer_for_testing( + bool force_transfer); + private: void OnUIChecksPerformed(NavigationThrottle::ThrottleCheckResult result); + // Used in transfer navigations. + void InitiateTransfer(); + void OnTransferComplete(); + net::URLRequest* request_; ResourceDispatcherHostDelegate* resource_dispatcher_host_delegate_; RequestContextType request_context_type_; + bool in_cross_site_transition_; + NavigationThrottle::ThrottleCheckResult on_transfer_done_result_; + base::WeakPtrFactory<NavigationResourceThrottle> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(NavigationResourceThrottle); diff --git a/chromium/content/browser/loader/navigation_url_loader.h b/chromium/content/browser/loader/navigation_url_loader.h index d4494aadca7..a97e0d728b6 100644 --- a/chromium/content/browser/loader/navigation_url_loader.h +++ b/chromium/content/browser/loader/navigation_url_loader.h @@ -17,7 +17,6 @@ class NavigationUIData; class NavigationURLLoaderDelegate; class NavigationURLLoaderFactory; class ServiceWorkerNavigationHandle; -struct CommonNavigationParams; struct NavigationRequestInfo; // PlzNavigate: The navigation logic's UI thread entry point into the resource 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 786634c349e..dd55af60123 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl_core.h +++ b/chromium/content/browser/loader/navigation_url_loader_impl_core.h @@ -12,18 +12,15 @@ #include "content/browser/loader/navigation_url_loader_impl.h" namespace net { -class URLRequest; struct RedirectInfo; } namespace content { -class FrameTreeNode; class NavigationResourceHandler; class NavigationData; class ResourceContext; class ResourceHandler; -class ResourceRequestBody; class ServiceWorkerNavigationHandleCore; class StreamHandle; struct ResourceResponse; diff --git a/chromium/content/browser/loader/navigation_url_loader_unittest.cc b/chromium/content/browser/loader/navigation_url_loader_unittest.cc index 89604bb33ba..a0b61b98c6b 100644 --- a/chromium/content/browser/loader/navigation_url_loader_unittest.cc +++ b/chromium/content/browser/loader/navigation_url_loader_unittest.cc @@ -112,7 +112,8 @@ class NavigationURLLoaderTest : public testing::Test { common_params.url = url; std::unique_ptr<NavigationRequestInfo> request_info( new NavigationRequestInfo(common_params, begin_params, url, - url::Origin(url), true, false, false, -1)); + url::Origin(url), true, false, false, -1, + false, false)); return NavigationURLLoader::Create(browser_context_.get(), std::move(request_info), nullptr, diff --git a/chromium/content/browser/loader/reload_cache_control_browsertest.cc b/chromium/content/browser/loader/reload_cache_control_browsertest.cc index 362bff46039..8999d37b199 100644 --- a/chromium/content/browser/loader/reload_cache_control_browsertest.cc +++ b/chromium/content/browser/loader/reload_cache_control_browsertest.cc @@ -6,8 +6,8 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/feature_list.h" #include "base/files/file_path.h" +#include "base/test/scoped_feature_list.h" #include "content/public/common/content_features.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" @@ -45,11 +45,8 @@ class ReloadCacheControlBrowserTest : public ContentBrowserTest { void SetUpOnMainThread() override { // TODO(toyoshim): Tests in this file depend on current reload behavior, // and should be modified when we enable the new reload behavior. - base::FeatureList::ClearInstanceForTesting(); - std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); - feature_list->InitializeFromCommandLine( - std::string(), features::kNonValidatingReloadOnNormalReload.name); - base::FeatureList::SetInstance(std::move(feature_list)); + scoped_feature_list_.InitAndDisableFeature( + features::kNonValidatingReloadOnNormalReload); SetUpTestServerOnMainThread(); } @@ -80,6 +77,8 @@ class ReloadCacheControlBrowserTest : public ContentBrowserTest { request_log_.push_back(log); } + base::test::ScopedFeatureList scoped_feature_list_; + DISALLOW_COPY_AND_ASSIGN(ReloadCacheControlBrowserTest); }; @@ -90,15 +89,14 @@ class ReloadCacheControlWithAnExperimentBrowserTest ~ReloadCacheControlWithAnExperimentBrowserTest() override = default; void SetUpOnMainThread() override { - base::FeatureList::ClearInstanceForTesting(); - std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); - feature_list->InitializeFromCommandLine( - features::kNonValidatingReloadOnNormalReload.name, std::string()); - base::FeatureList::SetInstance(std::move(feature_list)); + scoped_feature_list_.InitAndEnableFeature( + features::kNonValidatingReloadOnNormalReload); SetUpTestServerOnMainThread(); } + base::test::ScopedFeatureList scoped_feature_list_; + DISALLOW_COPY_AND_ASSIGN(ReloadCacheControlWithAnExperimentBrowserTest); }; diff --git a/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc b/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc index e69f948ee47..b47f3572a81 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/run_loop.h" #include "base/strings/string_util.h" @@ -16,6 +17,7 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "content/browser/download/download_manager_impl.h" +#include "content/browser/loader/resource_dispatcher_host_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" @@ -238,6 +240,52 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, ASSERT_FALSE(got_downloads()); } +namespace { + +// Responses with a HungResponse for the specified URL to hang on the request, +// and cancells all requests from specifield |child_id|. +std::unique_ptr<net::test_server::HttpResponse> CancelOnRequest( + const std::string& relative_url, + int child_id, + const net::test_server::HttpRequest& request) { + if (request.relative_url != relative_url) + return nullptr; + + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(&ResourceDispatcherHostImpl::CancelRequestsForProcess, + base::Unretained(ResourceDispatcherHostImpl::Get()), + child_id)); + + return base::MakeUnique<net::test_server::HungResponse>(); +} + +} // namespace + +// Tests the case where the request is cancelled by a layer above the +// URLRequest, which passes the error on ResourceLoader teardown, rather than in +// response to call to AsyncResourceHandler::OnResponseComplete. +IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, + SyncXMLHttpRequest_Cancelled) { + ASSERT_TRUE(embedded_test_server()->Start()); + WaitForLoadStop(shell()->web_contents()); + + embedded_test_server()->RegisterRequestHandler( + base::Bind(&CancelOnRequest, "/hung", + shell()->web_contents()->GetRenderProcessHost()->GetID())); + NavigateToURL(shell(), embedded_test_server()->GetURL( + "/sync_xmlhttprequest_cancelled.html")); + + int status_code = -1; + EXPECT_TRUE(ExecuteScriptAndExtractInt( + shell(), "window.domAutomationController.send(getErrorCode());", + &status_code)); + + // 19 is the value of NETWORK_ERROR on DOMException. + EXPECT_EQ(19, status_code); +} + // Flaky everywhere. http://crbug.com/130404 // Tests that onunload is run for cross-site requests. (Bug 1114994) IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, @@ -297,6 +345,10 @@ std::unique_ptr<net::test_server::HttpResponse> NoContentResponseHandler( // If this flakes use http://crbug.com/80596. IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CrossSiteNoUnloadOn204) { + const char kNoContentPath[] = "/nocontent"; + embedded_test_server()->RegisterRequestHandler( + base::Bind(&NoContentResponseHandler, kNoContentPath)); + ASSERT_TRUE(embedded_test_server()->Start()); // Start with a URL that sets a cookie in its unload handler. @@ -304,9 +356,6 @@ IN_PROC_BROWSER_TEST_F(ResourceDispatcherHostBrowserTest, CheckTitleTest(url, "set cookie on unload"); // Navigate to a cross-site URL that returns a 204 No Content response. - const char kNoContentPath[] = "/nocontent"; - embedded_test_server()->RegisterRequestHandler( - base::Bind(&NoContentResponseHandler, kNoContentPath)); NavigateToURL(shell(), embedded_test_server()->GetURL(kNoContentPath)); // Check that the unload cookie was not set. @@ -480,9 +529,9 @@ std::unique_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()->Start()); embedded_test_server()->RegisterRequestHandler( base::Bind(&HandleRedirectRequest, "/redirect?")); + ASSERT_TRUE(embedded_test_server()->Start()); std::string set_cookie_url(base::StringPrintf( "http://localhost:%u/set_cookie.html", embedded_test_server()->port())); @@ -755,11 +804,11 @@ namespace { struct RequestDataForDelegate { const GURL url; const GURL first_party; - const url::Origin initiator; + const base::Optional<url::Origin> initiator; RequestDataForDelegate(const GURL& url, const GURL& first_party, - const url::Origin initiator) + const base::Optional<url::Origin>& initiator) : url(url), first_party(first_party), initiator(initiator) {} }; diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc index 8cc50c6f058..4117eae4cdc 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc @@ -32,7 +32,7 @@ #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 "base/timer/timer.h" #include "content/browser/appcache/appcache_interceptor.h" #include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/bad_message.h" @@ -44,7 +44,6 @@ #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/intercepting_resource_handler.h" #include "content/browser/loader/loader_delegate.h" @@ -55,8 +54,10 @@ #include "content/browser/loader/navigation_url_loader_impl_core.h" #include "content/browser/loader/power_save_block_resource_throttle.h" #include "content/browser/loader/redirect_to_file_resource_handler.h" +#include "content/browser/loader/resource_loader.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/browser/loader/stream_resource_handler.h" #include "content/browser/loader/sync_resource_handler.h" #include "content/browser/loader/throttling_resource_handler.h" @@ -64,6 +65,7 @@ #include "content/browser/resource_context_impl.h" #include "content/browser/service_worker/foreign_fetch_request_handler.h" #include "content/browser/service_worker/link_header_support.h" +#include "content/browser/service_worker/service_worker_navigation_handle_core.h" #include "content/browser/service_worker/service_worker_request_handler.h" #include "content/browser/streams/stream.h" #include "content/browser/streams/stream_context.h" @@ -96,10 +98,10 @@ #include "net/base/mime_util.h" #include "net/base/net_errors.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" -#include "net/base/request_priority.h" #include "net/base/upload_data_stream.h" #include "net/cert/cert_status_flags.h" #include "net/cookies/cookie_monster.h" +#include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/ssl/client_cert_store.h" @@ -113,12 +115,15 @@ #include "storage/browser/blob/shareable_file_reference.h" #include "storage/browser/fileapi/file_permission_policy.h" #include "storage/browser/fileapi/file_system_context.h" +#include "url/third_party/mozilla/url_parse.h" #include "url/url_constants.h" using base::Time; using base::TimeDelta; using base::TimeTicks; using storage::ShareableFileReference; +using SyncLoadResultCallback = + content::ResourceDispatcherHostImpl::SyncLoadResultCallback; // ---------------------------------------------------------------------------- @@ -221,15 +226,15 @@ bool IsDetachableResourceType(ResourceType type) { } // Aborts a request before an URLRequest has actually been created. -void AbortRequestBeforeItStarts(ResourceMessageFilter* filter, - IPC::Message* sync_result, - int request_id, - mojom::URLLoaderClientPtr url_loader_client) { - if (sync_result) { +void AbortRequestBeforeItStarts( + ResourceMessageFilter* filter, + const SyncLoadResultCallback& sync_result_handler, + int request_id, + mojom::URLLoaderClientAssociatedPtr url_loader_client) { + if (sync_result_handler) { SyncLoadResult result; result.error_code = net::ERR_ABORTED; - ResourceHostMsg_SyncLoad::WriteReplyParams(sync_result, result); - filter->Send(sync_result); + sync_result_handler.Run(&result); } else { // Tell the renderer that this request was disallowed. ResourceRequestCompletionStatus request_complete_data; @@ -261,9 +266,13 @@ void SetReferrerForRequest(net::URLRequest* request, const Referrer& referrer) { net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE; switch (referrer.policy) { case blink::WebReferrerPolicyAlways: + net_referrer_policy = net::URLRequest::NEVER_CLEAR_REFERRER; + break; case blink::WebReferrerPolicyNever: + net_referrer_policy = net::URLRequest::NO_REFERRER; + break; case blink::WebReferrerPolicyOrigin: - net_referrer_policy = net::URLRequest::NEVER_CLEAR_REFERRER; + net_referrer_policy = net::URLRequest::ORIGIN; break; case blink::WebReferrerPolicyNoReferrerWhenDowngrade: net_referrer_policy = @@ -414,6 +423,22 @@ void NotifyForEachFrameFromUI( base::Passed(std::move(routing_ids)))); } +// Sends back the result of a synchronous loading result to the renderer through +// Chrome IPC. +void HandleSyncLoadResult(base::WeakPtr<ResourceMessageFilter> filter, + std::unique_ptr<IPC::Message> sync_result, + const SyncLoadResult* result) { + if (!filter) + return; + + if (result) { + ResourceHostMsg_SyncLoad::WriteReplyParams(sync_result.get(), *result); + } else { + sync_result->set_reply_error(); + } + filter->Send(sync_result.release()); +} + } // namespace ResourceDispatcherHostImpl::LoadInfo::LoadInfo() {} @@ -728,10 +753,7 @@ bool ResourceDispatcherHostImpl::HandleExternalProtocol(ResourceLoader* loader, if (job_factory->IsHandledURL(url)) return false; - return delegate_->HandleExternalProtocol( - url, info->GetChildID(), info->GetWebContentsGetterForRequest(), - info->IsMainFrame(), info->GetPageTransition(), info->HasUserGesture(), - info->GetContext()); + return delegate_->HandleExternalProtocol(url, info); } void ResourceDispatcherHostImpl::DidStartRequest(ResourceLoader* loader) { @@ -909,7 +931,7 @@ void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) { } if (loader->request()->url().SchemeIsCryptographic()) { - if (loader->request()->url().host() == "www.google.com") { + if (loader->request()->url().host_piece() == "www.google.com") { UMA_HISTOGRAM_SPARSE_SLOWLY("Net.ErrorCodesForHTTPSGoogleMainFrame2", -loader->request()->status().error()); } @@ -995,7 +1017,6 @@ bool ResourceDispatcherHostImpl::OnMessageReceived( IPC_MESSAGE_HANDLER_DELAY_REPLY(ResourceHostMsg_SyncLoad, OnSyncLoad) IPC_MESSAGE_HANDLER(ResourceHostMsg_ReleaseDownloadedFile, OnReleaseDownloadedFile) - IPC_MESSAGE_HANDLER(ResourceHostMsg_DataDownloaded_ACK, OnDataDownloadedACK) IPC_MESSAGE_HANDLER(ResourceHostMsg_CancelRequest, OnCancelRequest) IPC_MESSAGE_HANDLER(ResourceHostMsg_DidChangePriority, OnDidChangePriority) IPC_MESSAGE_UNHANDLED(handled = false) @@ -1009,10 +1030,11 @@ bool ResourceDispatcherHostImpl::OnMessageReceived( GlobalRequestID id(filter_->child_id(), request_id); DelegateMap::iterator it = delegate_map_.find(id); if (it != delegate_map_.end()) { - base::ObserverList<ResourceMessageDelegate>::Iterator del_it(it->second); - ResourceMessageDelegate* delegate; - while (!handled && (delegate = del_it.GetNext()) != NULL) { - handled = delegate->OnMessageReceived(message); + for (auto& delegate : *it->second) { + if (delegate.OnMessageReceived(message)) { + handled = true; + break; + } } } @@ -1037,8 +1059,8 @@ void ResourceDispatcherHostImpl::OnRequestResourceInternal( int routing_id, int request_id, const ResourceRequest& request_data, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client) { + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client) { // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( @@ -1059,7 +1081,7 @@ void ResourceDispatcherHostImpl::OnRequestResourceInternal( request_data.render_frame_id, request_data.url)); } - BeginRequest(request_id, request_data, NULL, routing_id, + BeginRequest(request_id, request_data, SyncLoadResultCallback(), routing_id, std::move(mojo_request), std::move(url_loader_client)); } @@ -1074,7 +1096,10 @@ void ResourceDispatcherHostImpl::OnRequestResourceInternal( void ResourceDispatcherHostImpl::OnSyncLoad(int request_id, const ResourceRequest& request_data, IPC::Message* sync_result) { - BeginRequest(request_id, request_data, sync_result, sync_result->routing_id(), + SyncLoadResultCallback callback = base::Bind( + &HandleSyncLoadResult, filter_->GetWeakPtr(), + base::Passed(WrapUnique(sync_result))); + BeginRequest(request_id, request_data, callback, sync_result->routing_id(), nullptr, nullptr); } @@ -1145,11 +1170,8 @@ void ResourceDispatcherHostImpl::UpdateRequestForTransfer( DelegateMap::iterator it = delegate_map_.find(old_request_id); if (it != delegate_map_.end()) { // Tell each delegate that the request ID has changed. - base::ObserverList<ResourceMessageDelegate>::Iterator del_it(it->second); - ResourceMessageDelegate* delegate; - while ((delegate = del_it.GetNext()) != NULL) { - delegate->set_request_id(new_request_id); - } + for (auto& delegate : *it->second) + delegate.set_request_id(new_request_id); // Now store the observer list under the new request ID. delegate_map_[new_request_id] = delegate_map_[old_request_id]; delegate_map_.erase(old_request_id); @@ -1173,18 +1195,66 @@ void ResourceDispatcherHostImpl::UpdateRequestForTransfer( child_id, request_data.service_worker_provider_id); } } +} + +void ResourceDispatcherHostImpl::CompleteTransfer( + int request_id, + const ResourceRequest& request_data, + int route_id) { + // Caller should ensure that |request_data| is associated with a transfer. + DCHECK(request_data.transferred_request_child_id != -1 || + request_data.transferred_request_request_id != -1); + + bool is_navigational_request = + request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME || + request_data.resource_type == RESOURCE_TYPE_SUB_FRAME; + if (!is_navigational_request) { + // Transfers apply only to navigational requests - the renderer seems to + // have sent bogus IPC data. + bad_message::ReceivedBadMessage( + filter_, bad_message::RDH_TRANSFERRING_NONNAVIGATIONAL_REQUEST); + return; + } - // We should have a CrossSiteResourceHandler to finish the transfer. - DCHECK(info->cross_site_handler()); + // Attempt to find a loader associated with the deferred transfer request. + LoaderMap::iterator it = pending_loaders_.find( + GlobalRequestID(request_data.transferred_request_child_id, + request_data.transferred_request_request_id)); + if (it == pending_loaders_.end()) { + // Renderer sent transferred_request_request_id and/or + // transferred_request_child_id that doesn't have a corresponding entry on + // the browser side. + // TODO(lukasza): https://crbug.com/659613: Need to understand the scenario + // that can lead here (and then attempt to reintroduce a renderer kill + // below). + return; + } + ResourceLoader* pending_loader = it->second.get(); + + if (!pending_loader->is_transferring()) { + // Renderer sent transferred_request_request_id and/or + // transferred_request_child_id that doesn't correspond to an actually + // transferring loader on the browser side. + base::debug::Alias(pending_loader); + bad_message::ReceivedBadMessage(filter_, + bad_message::RDH_REQUEST_NOT_TRANSFERRING); + return; + } + + // If the request is transferring to a new process, we can update our + // state and let it resume with its existing ResourceHandlers. + UpdateRequestForTransfer(filter_->child_id(), route_id, request_id, + request_data, it); + pending_loader->CompleteTransfer(); } void ResourceDispatcherHostImpl::BeginRequest( int request_id, const ResourceRequest& request_data, - IPC::Message* sync_result, // only valid for sync + const SyncLoadResultCallback& sync_result_handler, // only valid for sync int route_id, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client) { + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client) { int process_type = filter_->process_type(); int child_id = filter_->child_id(); @@ -1222,24 +1292,12 @@ void ResourceDispatcherHostImpl::BeginRequest( // If the request that's coming in is being transferred from another process, // we want to reuse and resume the old loader rather than start a new one. - LoaderMap::iterator it = pending_loaders_.find( - GlobalRequestID(request_data.transferred_request_child_id, - request_data.transferred_request_request_id)); - if (it != pending_loaders_.end()) { + if (request_data.transferred_request_child_id != -1 || + request_data.transferred_request_request_id != -1) { // TODO(yhirano): Make mojo work for this case. DCHECK(!url_loader_client); - // If the request is transferring to a new process, we can update our - // state and let it resume with its existing ResourceHandlers. - if (it->second->is_transferring()) { - ResourceLoader* deferred_loader = it->second.get(); - UpdateRequestForTransfer(child_id, route_id, request_id, - request_data, it); - deferred_loader->CompleteTransfer(); - } else { - bad_message::ReceivedBadMessage( - filter_, bad_message::RDH_REQUEST_NOT_TRANSFERRING); - } + CompleteTransfer(request_id, request_data, route_id); return; } @@ -1256,7 +1314,7 @@ void ResourceDispatcherHostImpl::BeginRequest( if (is_shutdown_ || !ShouldServiceRequest(process_type, child_id, request_data, headers, filter_, resource_context)) { - AbortRequestBeforeItStarts(filter_, sync_result, request_id, + AbortRequestBeforeItStarts(filter_, sync_result_handler, request_id, std::move(url_loader_client)); return; } @@ -1281,33 +1339,33 @@ void ResourceDispatcherHostImpl::BeginRequest( it.name(), it.value(), child_id, resource_context, base::Bind(&ResourceDispatcherHostImpl::ContinuePendingBeginRequest, base::Unretained(this), request_id, request_data, - sync_result, route_id, headers, + sync_result_handler, route_id, headers, base::Passed(std::move(mojo_request)), base::Passed(std::move(url_loader_client)))); return; } } } - ContinuePendingBeginRequest(request_id, request_data, sync_result, route_id, - headers, std::move(mojo_request), + ContinuePendingBeginRequest(request_id, request_data, sync_result_handler, + route_id, headers, std::move(mojo_request), std::move(url_loader_client), true, 0); } void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( int request_id, const ResourceRequest& request_data, - IPC::Message* sync_result, // only valid for sync + const SyncLoadResultCallback& sync_result_handler, // only valid for sync int route_id, const net::HttpRequestHeaders& headers, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client, + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client, bool continue_request, int error_code) { if (!continue_request) { // TODO(ananta): Find a way to specify the right error code here. Passing // in a non-content error code is not safe. bad_message::ReceivedBadMessage(filter_, bad_message::RDH_ILLEGAL_ORIGIN); - AbortRequestBeforeItStarts(filter_, sync_result, request_id, + AbortRequestBeforeItStarts(filter_, sync_result_handler, request_id, std::move(url_loader_client)); return; } @@ -1330,7 +1388,7 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( request_data.url, request_data.resource_type, resource_context)) { - AbortRequestBeforeItStarts(filter_, sync_result, request_id, + AbortRequestBeforeItStarts(filter_, sync_result_handler, request_id, std::move(url_loader_client)); return; } @@ -1352,6 +1410,12 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( new_request->set_first_party_for_cookies( request_data.first_party_for_cookies); + + // The initiator should normally be present, unless this is a navigation in a + // top-level frame. It may be null for some top-level navigations (eg: + // browser-initiated ones). + DCHECK(request_data.request_initiator.has_value() || + request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME); new_request->set_initiator(request_data.request_initiator); if (request_data.originated_from_service_worker) { @@ -1403,7 +1467,7 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( bool allow_download = request_data.allow_download && IsResourceTypeFrame(request_data.resource_type); bool do_not_prompt_for_login = request_data.do_not_prompt_for_login; - bool is_sync_load = sync_result != NULL; + bool is_sync_load = !!sync_result_handler; // Raw headers are sensitive, as they include Cookie/Set-Cookie, so only // allow requesting them if requester has ReadRawCookies permission. @@ -1515,8 +1579,8 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( request_data.should_reset_appcache); std::unique_ptr<ResourceHandler> handler(CreateResourceHandler( - new_request.get(), request_data, sync_result, route_id, process_type, - child_id, resource_context, std::move(mojo_request), + new_request.get(), request_data, sync_result_handler, route_id, + process_type, child_id, resource_context, std::move(mojo_request), std::move(url_loader_client))); if (handler) @@ -1527,20 +1591,20 @@ std::unique_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler( net::URLRequest* request, const ResourceRequest& request_data, - IPC::Message* sync_result, + const SyncLoadResultCallback& sync_result_handler, int route_id, int process_type, int child_id, ResourceContext* resource_context, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client) { + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client) { // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed. tracked_objects::ScopedTracker tracking_profile( FROM_HERE_WITH_EXPLICIT_FUNCTION( "456331 ResourceDispatcherHostImpl::CreateResourceHandler")); // Construct the IPC resource handler. std::unique_ptr<ResourceHandler> handler; - if (sync_result) { + if (sync_result_handler) { // download_to_file is not supported for synchronous requests. if (request_data.download_to_file) { bad_message::ReceivedBadMessage(filter_, bad_message::RDH_BAD_DOWNLOAD); @@ -1549,7 +1613,7 @@ ResourceDispatcherHostImpl::CreateResourceHandler( DCHECK(!mojo_request.is_pending()); DCHECK(!url_loader_client); - handler.reset(new SyncResourceHandler(request, sync_result, this)); + handler.reset(new SyncResourceHandler(request, sync_result_handler, this)); } else { if (mojo_request.is_pending()) { handler.reset(new MojoAsyncResourceHandler(request, this, @@ -1569,8 +1633,9 @@ ResourceDispatcherHostImpl::CreateResourceHandler( bool start_detached = request_data.download_to_network_cache_only; // Prefetches and <a ping> requests outlive their child process. - if (!sync_result && (start_detached || - IsDetachableResourceType(request_data.resource_type))) { + if (!sync_result_handler && + (start_detached || + IsDetachableResourceType(request_data.resource_type))) { std::unique_ptr<DetachableResourceHandler> detachable_handler = base::MakeUnique<DetachableResourceHandler>( request, @@ -1581,28 +1646,6 @@ ResourceDispatcherHostImpl::CreateResourceHandler( handler = std::move(detachable_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 (!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. - bool is_swappable_navigation = - request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME; - // If out-of-process iframes are possible, then all subframe requests need - // to go through the CrossSiteResourceHandler to enforce the site isolation - // policy. - if (!is_swappable_navigation && - SiteIsolationPolicy::AreCrossProcessFramesPossible()) { - is_swappable_navigation = - request_data.resource_type == RESOURCE_TYPE_SUB_FRAME; - } - if (is_swappable_navigation && process_type == PROCESS_TYPE_RENDERER) - handler.reset(new CrossSiteResourceHandler(std::move(handler), request)); - } - return AddStandardHandlers( request, request_data.resource_type, resource_context, request_data.fetch_request_context_type, filter_->appcache_service(), @@ -1725,10 +1768,6 @@ void ResourceDispatcherHostImpl::OnDidChangePriority( intra_priority_value); } -void ResourceDispatcherHostImpl::OnDataDownloadedACK(int request_id) { - // TODO(michaeln): maybe throttle DataDownloaded messages -} - void ResourceDispatcherHostImpl::RegisterDownloadedTempFile( int child_id, int request_id, const base::FilePath& file_path) { scoped_refptr<ShareableFileReference> reference = @@ -1770,26 +1809,8 @@ bool ResourceDispatcherHostImpl::Send(IPC::Message* message) { return false; } -// Note that this cancel is subtly different from the other -// CancelRequest methods in this file, which also tear down the loader. void ResourceDispatcherHostImpl::OnCancelRequest(int request_id) { - int child_id = filter_->child_id(); - - // When the old renderer dies, it sends a message to us to cancel its - // requests. - if (IsTransferredNavigation(GlobalRequestID(child_id, request_id))) - return; - - ResourceLoader* loader = GetLoader(child_id, request_id); - - // It is possible that the request has been completed and removed from the - // loader queue but the client has not processed the request completed message - // before issuing a cancel. This happens frequently for beacons which are - // canceled in the response received handler. - if (!loader) - return; - - loader->CancelRequest(true); + CancelRequestFromRenderer(GlobalRequestID(filter_->child_id(), request_id)); } ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( @@ -1799,17 +1820,12 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( bool download, ResourceContext* context) { return new ResourceRequestInfoImpl( - PROCESS_TYPE_RENDERER, - child_id, - render_view_route_id, + PROCESS_TYPE_RENDERER, child_id, render_view_route_id, -1, // frame_tree_node_id - 0, - request_id_, - render_frame_route_id, - false, // is_main_frame - false, // parent_is_main_frame - RESOURCE_TYPE_SUB_RESOURCE, - ui::PAGE_TRANSITION_LINK, + 0, MakeRequestID(), render_frame_route_id, + false, // is_main_frame + false, // parent_is_main_frame + RESOURCE_TYPE_SUB_RESOURCE, ui::PAGE_TRANSITION_LINK, false, // should_replace_current_entry download, // is_download false, // is_stream @@ -1818,8 +1834,7 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( false, // enable_load_timing false, // enable_upload_progress false, // do_not_prompt_for_login - blink::WebReferrerPolicyDefault, - blink::WebPageVisibilityStateVisible, + blink::WebReferrerPolicyDefault, blink::WebPageVisibilityStateVisible, context, base::WeakPtr<ResourceMessageFilter>(), // filter false, // report_raw_headers @@ -1848,8 +1863,8 @@ void ResourceDispatcherHostImpl::OnRenderViewHostSetIsLoading(int child_id, void ResourceDispatcherHostImpl::MarkAsTransferredNavigation( const GlobalRequestID& id, - const scoped_refptr<ResourceResponse>& response) { - GetLoader(id)->MarkAsTransferring(response); + const base::Closure& on_transfer_complete_callback) { + GetLoader(id)->MarkAsTransferring(on_transfer_complete_callback); } void ResourceDispatcherHostImpl::CancelTransferringNavigation( @@ -2083,7 +2098,21 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( ResourceType resource_type = info.is_main_frame ? RESOURCE_TYPE_MAIN_FRAME : RESOURCE_TYPE_SUB_FRAME; - if (is_shutdown_ || + // Do not allow browser plugin guests to navigate to non-web URLs, since they + // cannot swap processes or grant bindings. Do not check external protocols + // here because they're checked in + // ChromeResourceDispatcherHostDelegate::HandleExternalProtocol. + ChildProcessSecurityPolicyImpl* policy = + ChildProcessSecurityPolicyImpl::GetInstance(); + bool is_external_protocol = + !resource_context->GetRequestContext()->job_factory()->IsHandledURL( + info.common_params.url); + bool non_web_url_in_guest = + info.is_for_guests_only && + !policy->IsWebSafeScheme(info.common_params.url.scheme()) && + !is_external_protocol; + + if (is_shutdown_ || non_web_url_in_guest || // TODO(davidben): Check ShouldServiceRequest here. This is important; it // needs to be checked relative to the child that /requested/ the // navigation. It's where file upload checks, etc., come in. @@ -2148,8 +2177,6 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get())); } - request_id_--; - // Make extra info and read footer (contains request ID). // // TODO(davidben): Associate the request with the FrameTreeNode and/or tab so @@ -2160,7 +2187,7 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( -1, // route_id info.frame_tree_node_id, -1, // request_data.origin_pid, - request_id_, + MakeRequestID(), -1, // request_data.render_frame_id, info.is_main_frame, info.parent_is_main_frame, resource_type, info.common_params.transition, @@ -2179,7 +2206,7 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( // same mechanism as the cookie one. blink::WebPageVisibilityStateVisible, resource_context, base::WeakPtr<ResourceMessageFilter>(), // filter - false, // request_data.report_raw_headers + info.report_raw_headers, true, // is_async IsUsingLoFi(info.common_params.lofi_state, delegate_, *new_request, resource_context, info.is_main_frame), @@ -2197,6 +2224,11 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( false); // initiated_in_secure_context extra_info->set_navigation_ui_data(std::move(navigation_ui_data)); + if (service_worker_handle_core) { + extra_info->set_service_worker_context( + service_worker_handle_core->context_wrapper()); + } + // Request takes ownership. extra_info->AssociateWithRequest(new_request.get()); @@ -2255,8 +2287,8 @@ void ResourceDispatcherHostImpl::OnRequestResourceWithMojo( int routing_id, int request_id, const ResourceRequest& request, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client, + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client, ResourceMessageFilter* filter) { filter_ = filter; OnRequestResourceInternal(routing_id, request_id, request, @@ -2265,16 +2297,32 @@ void ResourceDispatcherHostImpl::OnRequestResourceWithMojo( filter_ = nullptr; } +void ResourceDispatcherHostImpl::OnSyncLoadWithMojo( + int routing_id, + int request_id, + const ResourceRequest& request_data, + ResourceMessageFilter* filter, + const SyncLoadResultCallback& result_handler) { + filter_ = filter; + BeginRequest(request_id, request_data, result_handler, routing_id, + nullptr, nullptr); + filter_ = nullptr; +} + // static int ResourceDispatcherHostImpl::CalculateApproximateMemoryCost( net::URLRequest* request) { // The following fields should be a minor size contribution (experimentally // on the order of 100). However since they are variable length, it could // in theory be a sizeable contribution. - int strings_cost = request->extra_request_headers().ToString().size() + - request->original_url().spec().size() + - request->referrer().size() + - request->method().size(); + int strings_cost = 0; + for (net::HttpRequestHeaders::Iterator it(request->extra_request_headers()); + it.GetNext();) { + strings_cost += it.name().length() + it.value().length(); + } + strings_cost += + request->original_url().parsed_for_possibly_invalid_spec().Length() + + request->referrer().size() + request->method().size(); // Note that this expression will typically be dominated by: // |kAvgBytesPerOutstandingRequest|. @@ -2345,8 +2393,6 @@ void ResourceDispatcherHostImpl::InitializeURLRequest( SetReferrerForRequest(request, referrer); - request_id_--; - ResourceRequestInfoImpl* info = CreateRequestInfo(render_process_host_id, render_view_routing_id, render_frame_routing_id, is_download, context); @@ -2392,6 +2438,30 @@ void ResourceDispatcherHostImpl::BeginURLRequest( BeginRequestInternal(std::move(request), std::move(handler)); } +int ResourceDispatcherHostImpl::MakeRequestID() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + return --request_id_; +} + +void ResourceDispatcherHostImpl::CancelRequestFromRenderer( + GlobalRequestID request_id) { + // When the old renderer dies, it sends a message to us to cancel its + // requests. + if (IsTransferredNavigation(request_id)) + return; + + ResourceLoader* loader = GetLoader(request_id); + + // It is possible that the request has been completed and removed from the + // loader queue but the client has not processed the request completed message + // before issuing a cancel. This happens frequently for beacons which are + // canceled in the response received handler. + if (!loader) + return; + + loader->CancelRequest(true); +} + void ResourceDispatcherHostImpl::StartLoading( ResourceRequestInfoImpl* info, std::unique_ptr<ResourceLoader> loader) { diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.h b/chromium/content/browser/loader/resource_dispatcher_host_impl.h index 0ee50dfa24c..655d83fe80a 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.h +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.h @@ -19,16 +19,14 @@ #include <string> #include <vector> +#include "base/callback_forward.h" #include "base/gtest_prod_util.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/observer_list.h" #include "base/time/time.h" -#include "base/timer/timer.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" -#include "content/browser/loader/resource_scheduler.h" #include "content/common/content_export.h" #include "content/common/url_loader.mojom.h" #include "content/public/browser/global_request_id.h" @@ -37,17 +35,18 @@ #include "content/public/common/request_context_type.h" #include "content/public/common/resource_type.h" #include "ipc/ipc_message.h" -#include "mojo/public/cpp/system/data_pipe.h" +#include "net/base/load_states.h" #include "net/base/request_priority.h" -#include "net/cookies/canonical_cookie.h" -#include "net/url_request/url_request.h" +#include "url/gurl.h" namespace base { class FilePath; +class RepeatingTimer; } namespace net { -class URLRequestJobFactory; +class URLRequest; +class HttpRequestHeaders; } namespace storage { @@ -63,20 +62,17 @@ class NavigationUIData; class RenderFrameHostImpl; class ResourceContext; class ResourceDispatcherHostDelegate; +class ResourceLoader; class ResourceHandler; class ResourceMessageDelegate; class ResourceMessageFilter; class ResourceRequestInfoImpl; +class ResourceScheduler; class ServiceWorkerNavigationHandleCore; -struct CommonNavigationParams; struct NavigationRequestInfo; struct Referrer; struct ResourceRequest; -namespace mojom { -class URLLoader; -} // namespace mojom - using CreateDownloadHandlerIntercept = base::Callback<std::unique_ptr<ResourceHandler>(net::URLRequest*)>; @@ -84,6 +80,11 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl : public ResourceDispatcherHost, public ResourceLoaderDelegate { public: + // Used to handle the result of SyncLoad IPC. |result| is null if it's + // unavailable due to an error. + using SyncLoadResultCallback = + base::Callback<void(const SyncLoadResult* result)>; + // This constructor should be used if we want downloads to work correctly. // TODO(ananta) // Work on moving creation of download handlers out of @@ -144,7 +145,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl // resumed by a new process. void MarkAsTransferredNavigation( const GlobalRequestID& id, - const scoped_refptr<ResourceResponse>& response); + const base::Closure& on_transfer_complete_callback); // Cancels a request previously marked as being transferred, for use when a // navigation was cancelled. @@ -297,10 +298,16 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl int routing_id, int request_id, const ResourceRequest& request, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client, + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client, ResourceMessageFilter* filter); + void OnSyncLoadWithMojo(int routing_id, + int request_id, + const ResourceRequest& request_data, + ResourceMessageFilter* filter, + const SyncLoadResultCallback& result_handler); + // Helper function for initializing the |request| passed in. By initializing // we mean setting the |referrer| on the |request|, associating the // ResourceRequestInfoImpl structure with the |request|, etc. @@ -330,6 +337,16 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl bool is_shutdown() const { return is_shutdown_; } + // Creates a new request ID for browser initiated requests. See the comments + // of |request_id_| for the details. Must be called on the IO thread. + int MakeRequestID(); + + // Cancels a request as requested by a renderer. This function is called when + // a mojo connection is lost. + // Note that this cancel is subtly different from the other CancelRequest + // methods in this file, which also tear down the loader. + void CancelRequestFromRenderer(GlobalRequestID request_id); + private: friend class ResourceDispatcherHostTest; @@ -361,6 +378,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl LoadInfo(); LoadInfo(const LoadInfo& other); ~LoadInfo(); + ResourceRequestInfo::WebContentsGetter web_contents_getter; GURL url; net::LoadStateWithParam load_state; @@ -515,8 +533,8 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl int routing_id, int request_id, const ResourceRequest& request_data, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client); + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client); void OnSyncLoad(int request_id, const ResourceRequest& request_data, @@ -532,12 +550,19 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl const ResourceRequest& request_data, LoaderMap::iterator iter); - void BeginRequest(int request_id, - const ResourceRequest& request_data, - IPC::Message* sync_result, // only valid for sync - int route_id, // only valid for async - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client); + // If |request_data| is for a request being transferred from another process, + // then CompleteTransfer method can be used to complete the transfer. + void CompleteTransfer(int request_id, + const ResourceRequest& request_data, + int route_id); + + void BeginRequest( + int request_id, + const ResourceRequest& request_data, + const SyncLoadResultCallback& sync_result_handler, // only valid for sync + int route_id, + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client); // There are requests which need decisions to be made like the following: // Whether the presence of certain HTTP headers like the Origin header are @@ -551,11 +576,11 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl void ContinuePendingBeginRequest( int request_id, const ResourceRequest& request_data, - IPC::Message* sync_result, // only valid for sync + const SyncLoadResultCallback& sync_result_handler, // only valid for sync int route_id, const net::HttpRequestHeaders& headers, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client, + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client, bool continue_request, int error_code); @@ -564,13 +589,13 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl std::unique_ptr<ResourceHandler> CreateResourceHandler( net::URLRequest* request, const ResourceRequest& request_data, - IPC::Message* sync_result, + const SyncLoadResultCallback& sync_result_handler, int route_id, int process_type, int child_id, ResourceContext* resource_context, - mojo::InterfaceRequest<mojom::URLLoader> mojo_request, - mojom::URLLoaderClientPtr url_loader_client); + mojom::URLLoaderAssociatedRequest mojo_request, + mojom::URLLoaderClientAssociatedPtr url_loader_client); // Wraps |handler| in the standard resource handlers for normal resource // loading and navigation requests. This adds MimeTypeResourceHandler and @@ -585,7 +610,6 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl int route_id, std::unique_ptr<ResourceHandler> handler); - void OnDataDownloadedACK(int request_id); void OnCancelRequest(int request_id); void OnReleaseDownloadedFile(int request_id); void OnDidChangePriority(int request_id, diff --git a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc index a4bcf9b425a..8dfae97d5e5 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc @@ -9,7 +9,6 @@ #include <vector> #include "base/bind.h" -#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/location.h" @@ -21,13 +20,13 @@ #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" +#include "base/test/scoped_feature_list.h" #include "base/threading/thread_task_runner_handle.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_resource_handler.h" #include "content/browser/frame_host/navigation_request_info.h" -#include "content/browser/loader/cross_site_resource_handler.h" #include "content/browser/loader/detachable_resource_handler.h" #include "content/browser/loader/navigation_resource_throttle.h" #include "content/browser/loader/navigation_url_loader.h" @@ -160,6 +159,7 @@ static ResourceRequest CreateResourceRequest(const char* method, request.method = std::string(method); request.url = url; request.first_party_for_cookies = url; // bypass third-party cookie blocking + request.request_initiator = url::Origin(url); // ensure initiator is set request.referrer_policy = blink::WebReferrerPolicyDefault; request.load_flags = 0; request.origin_pid = 0; @@ -203,6 +203,9 @@ class ResourceIPCAccumulator { typedef std::vector< std::vector<IPC::Message> > ClassifiedMessages; void GetClassifiedMessages(ClassifiedMessages* msgs); + // Returns the reply of |msg|, which is a synchronous IPC message. + const IPC::Message* GetReply(const IPC::Message& msg); + private: std::vector<IPC::Message> messages_; }; @@ -232,6 +235,17 @@ void ResourceIPCAccumulator::GetClassifiedMessages(ClassifiedMessages* msgs) { } } +const IPC::Message* ResourceIPCAccumulator::GetReply(const IPC::Message& msg) { + for (auto& reply : messages_) { + if (!reply.is_reply()) + continue; + if (IPC::SyncMessage::GetMessageId(reply) == + IPC::SyncMessage::GetMessageId(msg)) + return &reply; + } + return nullptr; +} + // This is used to create a filter matching a specified child id. class TestFilterSpecifyingChild : public ResourceMessageFilter { public: @@ -244,7 +258,6 @@ class TestFilterSpecifyingChild : public ResourceMessageFilter { NULL, NULL, NULL, - NULL, base::Bind(&TestFilterSpecifyingChild::GetContexts, base::Unretained(this))), resource_context_(resource_context), @@ -619,16 +632,6 @@ class TestUserData : public base::SupportsUserData::Data { bool* was_deleted_; }; -class TransfersAllNavigationsContentBrowserClient - : public TestContentBrowserClient { - public: - bool ShouldSwapProcessesForRedirect(ResourceContext* resource_context, - const GURL& current_url, - const GURL& new_url) override { - return true; - } -}; - enum GenericResourceThrottleFlags { NONE = 0, DEFER_STARTING_REQUEST = 1 << 0, @@ -872,6 +875,10 @@ class ResourceDispatcherHostTest : public testing::TestWithParam<TestConfig>, request_context->set_network_delegate(&network_delegate_); } + ~ResourceDispatcherHostTest() override { + filter_->OnChannelClosing(); + } + // IPC::Sender implementation bool Send(IPC::Message* msg) override { accum_.AddMessage(*msg); @@ -909,16 +916,12 @@ class ResourceDispatcherHostTest : public testing::TestWithParam<TestConfig>, web_contents_->GetRenderProcessHost()->GetID()); child_ids_.insert(web_contents_->GetRenderProcessHost()->GetID()); - base::FeatureList::ClearInstanceForTesting(); switch (GetParam()) { case TestConfig::kDefault: - base::FeatureList::InitializeInstance(std::string(), std::string()); break; case TestConfig::kOptimizeIPCForSmallResourceEnabled: { - std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); - feature_list->InitializeFromCommandLine( - features::kOptimizeLoadingIPCForSmallResources.name, std::string()); - base::FeatureList::SetInstance(std::move(feature_list)); + scoped_feature_list_.InitAndEnableFeature( + features::kOptimizeLoadingIPCForSmallResources); ASSERT_TRUE(base::FeatureList::IsEnabled( features::kOptimizeLoadingIPCForSmallResources)); break; @@ -927,6 +930,7 @@ class ResourceDispatcherHostTest : public testing::TestWithParam<TestConfig>, } void TearDown() override { + web_contents_filter_->OnChannelClosing(); web_contents_observer_.reset(); web_contents_.reset(); @@ -1090,7 +1094,8 @@ class ResourceDispatcherHostTest : public testing::TestWithParam<TestConfig>, common_params.url = url; std::unique_ptr<NavigationRequestInfo> request_info( new NavigationRequestInfo(common_params, begin_params, url, - url::Origin(url), true, false, false, -1)); + url::Origin(url), true, false, false, -1, + false, false)); std::unique_ptr<NavigationURLLoader> test_loader = NavigationURLLoader::Create(browser_context_.get(), std::move(request_info), nullptr, nullptr, @@ -1143,6 +1148,7 @@ class ResourceDispatcherHostTest : public testing::TestWithParam<TestConfig>, std::unique_ptr<base::RunLoop> wait_for_request_complete_loop_; RenderViewHostTestEnabler render_view_host_test_enabler_; bool auto_advance_; + base::test::ScopedFeatureList scoped_feature_list_; }; void ResourceDispatcherHostTest::MakeTestRequest(int render_view_id, @@ -1564,6 +1570,60 @@ TEST_P(ResourceDispatcherHostTest, DetachedResourceTimesOut) { EXPECT_EQ(0, network_delegate()->error_count()); } +TEST_P(ResourceDispatcherHostTest, SyncLoadSuccess) { + ResourceRequest request = CreateResourceRequest( + "GET", RESOURCE_TYPE_XHR, net::URLRequestTestJob::test_url_1()); + request.priority = net::MAXIMUM_PRIORITY; + + // Successful sync load. + std::tuple<SyncLoadResult> result; + ResourceHostMsg_SyncLoad sync_load_msg(0, 1, request, &std::get<0>(result)); + host_.OnMessageReceived(sync_load_msg, filter_.get()); + base::RunLoop().RunUntilIdle(); + + const IPC::Message* reply = accum_.GetReply(sync_load_msg); + ASSERT_TRUE(reply); + + ASSERT_TRUE(ResourceHostMsg_SyncLoad::ReadReplyParam(reply, &result)); + EXPECT_EQ(net::OK, std::get<0>(result).error_code); +} + +TEST_P(ResourceDispatcherHostTest, SyncLoadError) { + ResourceRequest request = CreateResourceRequest( + "GET", RESOURCE_TYPE_XHR, net::URLRequestTestJob::test_url_error()); + request.priority = net::MAXIMUM_PRIORITY; + + // Failued sync load. + std::tuple<SyncLoadResult> result; + ResourceHostMsg_SyncLoad sync_load_msg(0, 1, request, &std::get<0>(result)); + host_.OnMessageReceived(sync_load_msg, filter_.get()); + base::RunLoop().RunUntilIdle(); + + const IPC::Message* reply = accum_.GetReply(sync_load_msg); + ASSERT_TRUE(reply); + + ASSERT_TRUE(ResourceHostMsg_SyncLoad::ReadReplyParam(reply, &result)); + EXPECT_EQ(net::ERR_INVALID_URL, std::get<0>(result).error_code); +} + +TEST_P(ResourceDispatcherHostTest, SyncLoadCancel) { + ResourceRequest request = CreateResourceRequest( + "GET", RESOURCE_TYPE_XHR, net::URLRequestTestJob::test_url_1()); + request.priority = net::MAXIMUM_PRIORITY; + + // Cancelled sync load. + SyncLoadResult result; + ResourceHostMsg_SyncLoad sync_load_msg(0, 1, request, &result); + + host_.OnMessageReceived(sync_load_msg, filter_.get()); + host_.CancelRequestsForProcess(filter_->child_id()); + base::RunLoop().RunUntilIdle(); + + const IPC::Message* reply = accum_.GetReply(sync_load_msg); + ASSERT_TRUE(reply); + ASSERT_TRUE(reply->is_reply_error()); +} + // If the filter has disappeared then detachable resources should continue to // load. TEST_P(ResourceDispatcherHostTest, DeletedFilterDetached) { @@ -1905,6 +1965,8 @@ TEST_P(ResourceDispatcherHostTest, TestProcessCancel) { EXPECT_EQ(4, network_delegate()->completed_requests()); EXPECT_EQ(0, network_delegate()->canceled_requests()); EXPECT_EQ(0, network_delegate()->error_count()); + + test_filter->OnChannelClosing(); } // Tests whether the correct requests get canceled when a RenderViewHost is @@ -2155,6 +2217,7 @@ TEST_P(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) { CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_3()); EXPECT_TRUE(host_.blocked_loaders_map_.empty()); + second_filter->OnChannelClosing(); } // Tests that blocked requests don't leak when the ResourceDispatcherHost goes @@ -2200,6 +2263,8 @@ TEST_P(ResourceDispatcherHostTest, TestBlockedRequestsDontLeak) { // Flush all the pending requests. while (net::URLRequestTestJob::ProcessOnePendingMessage()) {} + + second_filter->OnChannelClosing(); } // Test the private helper method "CalculateApproximateMemoryCost()". @@ -2207,15 +2272,13 @@ TEST_P(ResourceDispatcherHostTest, CalculateApproximateMemoryCost) { net::URLRequestContext context; std::unique_ptr<net::URLRequest> req(context.CreateRequest( GURL("http://www.google.com"), net::DEFAULT_PRIORITY, NULL)); - EXPECT_EQ( - 4427, - ResourceDispatcherHostImpl::CalculateApproximateMemoryCost(req.get())); + EXPECT_EQ(4425, ResourceDispatcherHostImpl::CalculateApproximateMemoryCost( + req.get())); // Add 9 bytes of referrer. req->SetReferrer("123456789"); - EXPECT_EQ( - 4436, - ResourceDispatcherHostImpl::CalculateApproximateMemoryCost(req.get())); + EXPECT_EQ(4434, ResourceDispatcherHostImpl::CalculateApproximateMemoryCost( + req.get())); // Add 33 bytes of upload content. std::string upload_content; @@ -2228,9 +2291,8 @@ TEST_P(ResourceDispatcherHostTest, CalculateApproximateMemoryCost) { net::ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); // Since the upload throttling is disabled, this has no effect on the cost. - EXPECT_EQ( - 4436, - ResourceDispatcherHostImpl::CalculateApproximateMemoryCost(req.get())); + EXPECT_EQ(4434, ResourceDispatcherHostImpl::CalculateApproximateMemoryCost( + req.get())); } // Test that too much memory for outstanding requests for a particular @@ -2306,6 +2368,8 @@ TEST_P(ResourceDispatcherHostTest, TooMuchOutstandingRequestsMemory) { net::URLRequestTestJob::test_data_2()); CheckSuccessfulRequest(msgs[kMaxRequests + 3], net::URLRequestTestJob::test_data_2()); + + second_filter->OnChannelClosing(); } // Test that when too many requests are outstanding for a particular @@ -2371,6 +2435,9 @@ TEST_P(ResourceDispatcherHostTest, TooManyOutstandingRequests) { CheckFailedRequest(msgs[kMaxRequestsPerProcess + 2], net::URLRequestTestJob::test_data_2(), net::ERR_INSUFFICIENT_RESOURCES); + + second_filter->OnChannelClosing(); + third_filter->OnChannelClosing(); } // Tests that we sniff the mime type for a simple request. @@ -2578,7 +2645,7 @@ TEST_P(ResourceDispatcherHostTest, CancelRequestsForContext) { std::unique_ptr<NavigationRequestInfo> request_info( new NavigationRequestInfo(common_params, begin_params, download_url, url::Origin(download_url), true, false, false, - -1)); + -1, false, false)); std::unique_ptr<NavigationURLLoader> loader = NavigationURLLoader::Create( browser_context_.get(), std::move(request_info), nullptr, nullptr, &delegate); @@ -2687,7 +2754,7 @@ TEST_P(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) { GlobalRequestID global_request_id(web_contents_filter_->child_id(), request_id); - host_.MarkAsTransferredNavigation(global_request_id, nullptr); + host_.MarkAsTransferredNavigation(global_request_id, base::Closure()); // And now simulate a cancellation coming from the renderer. ResourceHostMsg_CancelRequest msg(request_id); @@ -2714,13 +2781,11 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationHtml) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } - // This test expects the cross site request to be leaked, so it can transfer - // the request directly. - CrossSiteResourceHandler::SetLeakRequestsForTesting(true); EXPECT_EQ(0, host_.pending_requests()); NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing(true); + NavigationResourceThrottle::set_force_transfer_for_testing(true); int render_view_id = 0; int request_id = 1; @@ -2731,11 +2796,6 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationHtml) { HandleScheme("http"); - // Temporarily replace ContentBrowserClient with one that will trigger the - // transfer navigation code paths. - TransfersAllNavigationsContentBrowserClient new_client; - ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client); - MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id, GURL("http://example.com/blah"), RESOURCE_TYPE_MAIN_FRAME); @@ -2754,9 +2814,6 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationHtml) { // MimeTypeResourceHandler. while (net::URLRequestTestJob::ProcessOnePendingMessage()) {} - // Restore, now that we've set up a transfer. - SetBrowserClientForTesting(old_client); - // This second filter is used to emulate a second process. scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter(); @@ -2780,6 +2837,8 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationHtml) { ASSERT_EQ(2U, msgs.size()); EXPECT_EQ(ResourceMsg_ReceivedRedirect::ID, msgs[0][0].type()); CheckSuccessfulRequest(msgs[1], kResponseBody); + + second_filter->OnChannelClosing(); } // Test transferring two navigations with text/html, to ensure the resource @@ -2789,10 +2848,8 @@ TEST_P(ResourceDispatcherHostTest, TransferTwoNavigationsHtml) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } - // This test expects the cross site request to be leaked, so it can transfer - // the request directly. - CrossSiteResourceHandler::SetLeakRequestsForTesting(true); + NavigationResourceThrottle::set_force_transfer_for_testing(true); NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing(true); EXPECT_EQ(0, host_.pending_requests()); @@ -2808,11 +2865,6 @@ TEST_P(ResourceDispatcherHostTest, TransferTwoNavigationsHtml) { HandleScheme("http"); - // Temporarily replace ContentBrowserClient with one that will trigger the - // transfer navigation code paths. - TransfersAllNavigationsContentBrowserClient new_client; - ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client); - // Make the first request. MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id, GURL("http://example.com/blah"), @@ -2829,8 +2881,7 @@ TEST_P(ResourceDispatcherHostTest, TransferTwoNavigationsHtml) { // MimeTypeResourceHandler. while (net::URLRequestTestJob::ProcessOnePendingMessage()) {} - // Restore, now that we've set up a transfer. - SetBrowserClientForTesting(old_client); + NavigationResourceThrottle::set_force_transfer_for_testing(false); // This second filter is used to emulate a second process. scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter(); @@ -2866,6 +2917,8 @@ TEST_P(ResourceDispatcherHostTest, TransferTwoNavigationsHtml) { ASSERT_EQ(2U, msgs.size()); CheckSuccessfulRequest(msgs[0], kResponseBody); + + second_filter->OnChannelClosing(); } // Test transferred navigations with text/plain, which causes @@ -2876,10 +2929,8 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationText) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } - // This test expects the cross site request to be leaked, so it can transfer - // the request directly. - CrossSiteResourceHandler::SetLeakRequestsForTesting(true); + NavigationResourceThrottle::set_force_transfer_for_testing(true); NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing(true); EXPECT_EQ(0, host_.pending_requests()); @@ -2893,11 +2944,6 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationText) { HandleScheme("http"); - // Temporarily replace ContentBrowserClient with one that will trigger the - // transfer navigation code paths. - TransfersAllNavigationsContentBrowserClient new_client; - ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client); - MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id, GURL("http://example.com/blah"), RESOURCE_TYPE_MAIN_FRAME); @@ -2919,9 +2965,6 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationText) { while (net::URLRequestTestJob::ProcessOnePendingMessage()) {} base::RunLoop().RunUntilIdle(); - // Restore, now that we've set up a transfer. - SetBrowserClientForTesting(old_client); - // This second filter is used to emulate a second process. scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter(); @@ -2945,6 +2988,8 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationText) { ASSERT_EQ(2U, msgs.size()); EXPECT_EQ(ResourceMsg_ReceivedRedirect::ID, msgs[0][0].type()); CheckSuccessfulRequest(msgs[1], kResponseBody); + + second_filter->OnChannelClosing(); } TEST_P(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) { @@ -2952,10 +2997,8 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } - // This test expects the cross site request to be leaked, so it can transfer - // the request directly. - CrossSiteResourceHandler::SetLeakRequestsForTesting(true); + NavigationResourceThrottle::set_force_transfer_for_testing(true); NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing(true); EXPECT_EQ(0, host_.pending_requests()); @@ -2971,11 +3014,6 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) { HandleScheme("http"); - // Temporarily replace ContentBrowserClient with one that will trigger the - // transfer navigation code paths. - TransfersAllNavigationsContentBrowserClient new_client; - ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client); - // Create a first filter that can be deleted before the second one starts. { scoped_refptr<ForwardingFilter> first_filter = MakeForwardingFilter(); @@ -3001,12 +3039,11 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) { // Flush all the pending requests to get the response through the // MimeTypeResourceHandler. while (net::URLRequestTestJob::ProcessOnePendingMessage()) {} + + first_filter->OnChannelClosing(); } // The first filter is now deleted, as if the child process died. - // Restore. - SetBrowserClientForTesting(old_client); - // Make sure we don't hold onto the ResourceMessageFilter after it is deleted. GlobalRequestID first_global_request_id(first_child_id, request_id); @@ -3035,6 +3072,8 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) { ASSERT_EQ(2U, msgs.size()); EXPECT_EQ(ResourceMsg_ReceivedRedirect::ID, msgs[0][0].type()); CheckSuccessfulRequest(msgs[1], kResponseBody); + + second_filter->OnChannelClosing(); } TEST_P(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) { @@ -3042,10 +3081,8 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) { SUCCEED() << "Test is not applicable with browser side navigation enabled"; return; } - // This test expects the cross site request to be leaked, so it can transfer - // the request directly. - CrossSiteResourceHandler::SetLeakRequestsForTesting(true); + NavigationResourceThrottle::set_force_transfer_for_testing(true); NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing(true); EXPECT_EQ(0, host_.pending_requests()); @@ -3059,11 +3096,6 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) { HandleScheme("http"); - // Temporarily replace ContentBrowserClient with one that will trigger the - // transfer navigation code paths. - TransfersAllNavigationsContentBrowserClient new_client; - ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client); - MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id, GURL("http://example.com/blah"), RESOURCE_TYPE_MAIN_FRAME); @@ -3092,9 +3124,6 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) { while (net::URLRequestTestJob::ProcessOnePendingMessage()) {} base::RunLoop().RunUntilIdle(); - // Restore. - SetBrowserClientForTesting(old_client); - // This second filter is used to emulate a second process. scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter(); @@ -3131,6 +3160,8 @@ TEST_P(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) { ASSERT_EQ(2U, msgs.size()); EXPECT_EQ(ResourceMsg_ReceivedRedirect::ID, msgs[0][0].type()); CheckSuccessfulRequest(msgs[1], kResponseBody); + + second_filter->OnChannelClosing(); } TEST_P(ResourceDispatcherHostTest, UnknownURLScheme) { @@ -3755,6 +3786,72 @@ TEST_P(ResourceDispatcherHostTest, ThrottleMustProcessResponseBeforeRead) { base::RunLoop().RunUntilIdle(); } +namespace { + +void StoreSyncLoadResult(bool* called, + bool* was_null, + SyncLoadResult* result_out, + const SyncLoadResult* result) { + *called = true; + *was_null = !result; + + if (result) + *result_out = *result; +} + +} // namespace + +TEST_P(ResourceDispatcherHostTest, SyncLoadWithMojoSuccess) { + ResourceRequest request = CreateResourceRequest( + "GET", RESOURCE_TYPE_XHR, net::URLRequestTestJob::test_url_1()); + request.priority = net::MAXIMUM_PRIORITY; + + bool called = false; + bool was_null = false; + SyncLoadResult result; + host_.OnSyncLoadWithMojo(0, 1, request, filter_.get(), + base::Bind(&StoreSyncLoadResult, + &called, &was_null, &result)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(called); + EXPECT_FALSE(was_null); + EXPECT_EQ(net::OK, result.error_code); +} + +TEST_P(ResourceDispatcherHostTest, SyncLoadWithMojoError) { + ResourceRequest request = CreateResourceRequest( + "GET", RESOURCE_TYPE_XHR, net::URLRequestTestJob::test_url_error()); + request.priority = net::MAXIMUM_PRIORITY; + + bool called = false; + bool was_null = false; + SyncLoadResult result; + host_.OnSyncLoadWithMojo(0, 1, request, filter_.get(), + base::Bind(&StoreSyncLoadResult, + &called, &was_null, &result)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(called); + EXPECT_FALSE(was_null); + EXPECT_EQ(net::ERR_INVALID_URL, result.error_code); +} + +TEST_P(ResourceDispatcherHostTest, SyncLoadWithMojoCancel) { + ResourceRequest request = CreateResourceRequest( + "GET", RESOURCE_TYPE_XHR, net::URLRequestTestJob::test_url_error()); + request.priority = net::MAXIMUM_PRIORITY; + + bool called = false; + bool was_null = false; + SyncLoadResult result; + host_.OnSyncLoadWithMojo(0, 1, request, filter_.get(), + base::Bind(&StoreSyncLoadResult, + &called, &was_null, &result)); + host_.CancelRequestsForProcess(filter_->child_id()); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(called); + EXPECT_TRUE(was_null); +} + // A URLRequestTestJob that sets a test certificate on the |ssl_info| // field of the response. class TestHTTPSURLRequestJob : public net::URLRequestTestJob { diff --git a/chromium/content/browser/loader/resource_handler.cc b/chromium/content/browser/loader/resource_handler.cc index 358dd65a90a..1c9287e3ad2 100644 --- a/chromium/content/browser/loader/resource_handler.cc +++ b/chromium/content/browser/loader/resource_handler.cc @@ -8,6 +8,8 @@ namespace content { +ResourceHandler::~ResourceHandler() {} + ResourceHandler::ResourceHandler(net::URLRequest* request) : controller_(NULL), request_(request) { diff --git a/chromium/content/browser/loader/resource_handler.h b/chromium/content/browser/loader/resource_handler.h index 02ef6b87f12..a6cc6822c69 100644 --- a/chromium/content/browser/loader/resource_handler.h +++ b/chromium/content/browser/loader/resource_handler.h @@ -14,8 +14,9 @@ #include <string> +#include "base/compiler_specific.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/sequenced_task_runner_helpers.h" #include "base/threading/non_thread_safe.h" #include "content/common/content_export.h" @@ -40,7 +41,7 @@ struct ResourceResponse; class CONTENT_EXPORT ResourceHandler : public NON_EXPORTED_BASE(base::NonThreadSafe) { public: - virtual ~ResourceHandler() {} + virtual ~ResourceHandler(); // Sets the controller for this handler. virtual void SetController(ResourceController* controller); @@ -114,6 +115,8 @@ class CONTENT_EXPORT ResourceHandler private: ResourceController* controller_; net::URLRequest* request_; + + DISALLOW_COPY_AND_ASSIGN(ResourceHandler); }; } // namespace content diff --git a/chromium/content/browser/loader/resource_loader.cc b/chromium/content/browser/loader/resource_loader.cc index 6fda9a09bcb..f2f670a566d 100644 --- a/chromium/content/browser/loader/resource_loader.cc +++ b/chromium/content/browser/loader/resource_loader.cc @@ -6,18 +6,18 @@ #include <utility> +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/location.h" #include "base/metrics/histogram_macros.h" #include "base/profiler/scoped_tracker.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "content/browser/appcache/appcache_interceptor.h" #include "content/browser/child_process_security_policy_impl.h" -#include "content/browser/loader/cross_site_resource_handler.h" #include "content/browser/loader/detachable_resource_handler.h" +#include "content/browser/loader/resource_handler.h" #include "content/browser/loader/resource_loader_delegate.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/service_worker/service_worker_request_handler.h" @@ -25,6 +25,7 @@ #include "content/browser/ssl/ssl_client_auth_handler.h" #include "content/browser/ssl/ssl_manager.h" #include "content/public/browser/resource_dispatcher_host_login_delegate.h" +#include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "content/public/common/process_type.h" @@ -197,10 +198,11 @@ void ResourceLoader::CancelWithError(int error_code) { } void ResourceLoader::MarkAsTransferring( - const scoped_refptr<ResourceResponse>& response) { + const base::Closure& on_transfer_complete_callback) { CHECK(IsResourceTypeFrame(GetRequestInfo()->GetResourceType())) << "Can only transfer for navigations"; is_transferring_ = true; + on_transfer_complete_callback_ = on_transfer_complete_callback; int child_id = GetRequestInfo()->GetChildID(); AppCacheInterceptor::PrepareForCrossSiteTransfer(request(), child_id); @@ -211,13 +213,14 @@ void ResourceLoader::MarkAsTransferring( } void ResourceLoader::CompleteTransfer() { - // Although CrossSiteResourceHandler defers at OnResponseStarted + // Although NavigationResourceThrottle defers at WillProcessResponse // (DEFERRED_READ), it may be seeing a replay of events via - // MimeTypeResourceHandler, and so the request itself is actually deferred - // at a later read stage. + // MimeTypeResourceHandler, and so the request itself is actually deferred at + // a later read stage. DCHECK(DEFERRED_READ == deferred_stage_ || DEFERRED_RESPONSE_COMPLETE == deferred_stage_); DCHECK(is_transferring_); + DCHECK(!on_transfer_complete_callback_.is_null()); // In some cases, a process transfer doesn't really happen and the // request is resumed in the original process. Real transfers to a new process @@ -231,7 +234,7 @@ void ResourceLoader::CompleteTransfer() { handler->MaybeCompleteCrossSiteTransferInOldProcess(child_id); is_transferring_ = false; - GetRequestInfo()->cross_site_handler()->ResumeResponse(); + base::ResetAndReturn(&on_transfer_complete_callback_).Run(); } ResourceRequestInfoImpl* ResourceLoader::GetRequestInfo() { @@ -254,14 +257,21 @@ void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused, ResourceRequestInfoImpl* info = GetRequestInfo(); - if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( - info->GetChildID(), redirect_info.new_url)) { - DVLOG(1) << "Denied unauthorized request for " - << redirect_info.new_url.possibly_invalid_spec(); + // With PlzNavigate for frame navigations this check is done in the + // NavigationRequest::OnReceivedRedirect() function. + bool check_handled_elsewhere = IsBrowserSideNavigationEnabled() && + IsResourceTypeFrame(info->GetResourceType()); - // Tell the renderer that this request was disallowed. - Cancel(); - return; + if (!check_handled_elsewhere) { + if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( + info->GetChildID(), redirect_info.new_url)) { + DVLOG(1) << "Denied unauthorized request for " + << redirect_info.new_url.possibly_invalid_spec(); + + // Tell the renderer that this request was disallowed. + Cancel(); + return; + } } if (delegate_->HandleExternalProtocol(this, redirect_info.new_url)) { diff --git a/chromium/content/browser/loader/resource_loader.h b/chromium/content/browser/loader/resource_loader.h index 8dcaf63fc45..6ebcd2de50b 100644 --- a/chromium/content/browser/loader/resource_loader.h +++ b/chromium/content/browser/loader/resource_loader.h @@ -7,10 +7,10 @@ #include <memory> +#include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "base/timer/timer.h" -#include "content/browser/loader/resource_handler.h" +#include "base/time/time.h" #include "content/browser/ssl/ssl_client_auth_handler.h" #include "content/browser/ssl/ssl_error_handler.h" #include "content/common/content_export.h" @@ -23,6 +23,7 @@ class X509Certificate; namespace content { class ResourceDispatcherHostLoginDelegate; +class ResourceHandler; class ResourceLoaderDelegate; class ResourceRequestInfoImpl; @@ -43,7 +44,7 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate, void CancelRequest(bool from_renderer); bool is_transferring() const { return is_transferring_; } - void MarkAsTransferring(const scoped_refptr<ResourceResponse>& response); + void MarkAsTransferring(const base::Closure& on_transfer_complete_callback); void CompleteTransfer(); net::URLRequest* request() { return request_.get(); } @@ -129,6 +130,9 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate, // which point we'll receive a new ResourceHandler. bool is_transferring_; + // Called when a navigation has finished transfer. + base::Closure on_transfer_complete_callback_; + // Instrumentation add to investigate http://crbug.com/503306. // TODO(mmenke): Remove once bug is fixed. int times_cancelled_before_request_start_; diff --git a/chromium/content/browser/loader/resource_loader_delegate.h b/chromium/content/browser/loader/resource_loader_delegate.h index 36702faf567..a6b2be979c0 100644 --- a/chromium/content/browser/loader/resource_loader_delegate.h +++ b/chromium/content/browser/loader/resource_loader_delegate.h @@ -7,6 +7,8 @@ #include "content/common/content_export.h" +class GURL; + namespace net { class AuthChallengeInfo; class ClientCertStore; diff --git a/chromium/content/browser/loader/resource_message_filter.cc b/chromium/content/browser/loader/resource_message_filter.cc index b14afa1b422..2ee8ff5c7a7 100644 --- a/chromium/content/browser/loader/resource_message_filter.cc +++ b/chromium/content/browser/loader/resource_message_filter.cc @@ -6,8 +6,8 @@ #include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" -#include "content/browser/host_zoom_level_context.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/browser/loader/url_loader_factory_impl.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/common/resource_messages.h" #include "content/public/browser/resource_context.h" @@ -22,27 +22,34 @@ ResourceMessageFilter::ResourceMessageFilter( ChromeBlobStorageContext* blob_storage_context, storage::FileSystemContext* file_system_context, ServiceWorkerContextWrapper* service_worker_context, - HostZoomLevelContext* host_zoom_level_context, const GetContextsCallback& get_contexts_callback) : BrowserMessageFilter(ResourceMsgStart), + BrowserAssociatedInterface<mojom::URLLoaderFactory>(this, this), child_id_(child_id), process_type_(process_type), + is_channel_closed_(false), appcache_service_(appcache_service), blob_storage_context_(blob_storage_context), file_system_context_(file_system_context), service_worker_context_(service_worker_context), - host_zoom_level_context_(host_zoom_level_context), get_contexts_callback_(get_contexts_callback), - weak_ptr_factory_(this) { -} + weak_ptr_factory_(this) {} ResourceMessageFilter::~ResourceMessageFilter() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(is_channel_closed_); + DCHECK(!weak_ptr_factory_.HasWeakPtrs()); } void ResourceMessageFilter::OnChannelClosing() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + // Unhook us from all pending network requests so they don't get sent to a // deleted object. ResourceDispatcherHostImpl::Get()->CancelRequestsForProcess(child_id_); + + weak_ptr_factory_.InvalidateWeakPtrs(); + is_channel_closed_ = true; } bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message) { @@ -63,15 +70,28 @@ void ResourceMessageFilter::GetContexts( request_context); } -const HostZoomMap* ResourceMessageFilter::GetHostZoomMap() const { - if (host_zoom_level_context_.get()) - return host_zoom_level_context_->GetHostZoomMap(); - return NULL; -} - base::WeakPtr<ResourceMessageFilter> ResourceMessageFilter::GetWeakPtr() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - return weak_ptr_factory_.GetWeakPtr(); + return is_channel_closed_ ? nullptr : weak_ptr_factory_.GetWeakPtr(); +} + +void ResourceMessageFilter::CreateLoaderAndStart( + mojom::URLLoaderAssociatedRequest request, + int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info) { + URLLoaderFactoryImpl::CreateLoaderAndStart(std::move(request), routing_id, + request_id, url_request, + std::move(client_ptr_info), this); +} + +void ResourceMessageFilter::SyncLoad(int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + const SyncLoadCallback& callback) { + URLLoaderFactoryImpl::SyncLoad(routing_id, request_id, url_request, callback, + this); } } // namespace content diff --git a/chromium/content/browser/loader/resource_message_filter.h b/chromium/content/browser/loader/resource_message_filter.h index 4037826d7b1..bcc07138851 100644 --- a/chromium/content/browser/loader/resource_message_filter.h +++ b/chromium/content/browser/loader/resource_message_filter.h @@ -11,8 +11,9 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner_helpers.h" -#include "content/browser/host_zoom_level_context.h" #include "content/common/content_export.h" +#include "content/common/url_loader_factory.mojom.h" +#include "content/public/browser/browser_associated_interface.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/resource_type.h" @@ -37,7 +38,10 @@ class ServiceWorkerContextWrapper; // delayed by costly UI processing that may be occuring on the main thread of // the browser. It also means that any hangs in starting a network request // will not interfere with browser UI. -class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { +class CONTENT_EXPORT ResourceMessageFilter + : public BrowserMessageFilter, + public BrowserAssociatedInterface<mojom::URLLoaderFactory>, + public mojom::URLLoaderFactory { public: typedef base::Callback<void(ResourceType resource_type, ResourceContext**, @@ -54,7 +58,6 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { ChromeBlobStorageContext* blob_storage_context, storage::FileSystemContext* file_system_context, ServiceWorkerContextWrapper* service_worker_context, - HostZoomLevelContext* host_zoom_level_context, const GetContextsCallback& get_contexts_callback); // BrowserMessageFilter implementation. @@ -85,15 +88,22 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { return service_worker_context_.get(); } - // Returns a raw pointer to the HostZoomLevelContext's associated HostZoomMap, - // or NULL if no context is present. - const HostZoomMap* GetHostZoomMap() const; - int child_id() const { return child_id_; } int process_type() const { return process_type_; } base::WeakPtr<ResourceMessageFilter> GetWeakPtr(); + void CreateLoaderAndStart( + mojom::URLLoaderAssociatedRequest request, + int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info) override; + void SyncLoad(int32_t routing_id, + int32_t request_id, + const ResourceRequest& request, + const SyncLoadCallback& callback) override; + protected: // Protected destructor so that we can be overriden in tests. ~ResourceMessageFilter() override; @@ -106,12 +116,12 @@ class CONTENT_EXPORT ResourceMessageFilter : public BrowserMessageFilter { int child_id_; int process_type_; + bool is_channel_closed_; scoped_refptr<ChromeAppCacheService> appcache_service_; scoped_refptr<ChromeBlobStorageContext> blob_storage_context_; scoped_refptr<storage::FileSystemContext> file_system_context_; scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_; - scoped_refptr<HostZoomLevelContext> host_zoom_level_context_; GetContextsCallback get_contexts_callback_; diff --git a/chromium/content/browser/loader/resource_request_info_impl.cc b/chromium/content/browser/loader/resource_request_info_impl.cc index d9f14b9706a..fc7d9730281 100644 --- a/chromium/content/browser/loader/resource_request_info_impl.cc +++ b/chromium/content/browser/loader/resource_request_info_impl.cc @@ -160,8 +160,7 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl( const std::string& original_headers, const scoped_refptr<ResourceRequestBodyImpl> body, bool initiated_in_secure_context) - : cross_site_handler_(NULL), - detachable_handler_(NULL), + : detachable_handler_(NULL), process_type_(process_type), child_id_(child_id), route_id_(route_id), @@ -233,6 +232,10 @@ int ResourceRequestInfoImpl::GetRouteID() const { return route_id_; } +GlobalRequestID ResourceRequestInfoImpl::GetGlobalRequestID() const { + return GlobalRequestID(child_id_, request_id_); +} + int ResourceRequestInfoImpl::GetOriginPID() const { return origin_pid_; } @@ -325,10 +328,6 @@ int ResourceRequestInfoImpl::GetRequestID() const { return request_id_; } -GlobalRequestID ResourceRequestInfoImpl::GetGlobalRequestID() const { - return GlobalRequestID(child_id_, request_id_); -} - GlobalRoutingID ResourceRequestInfoImpl::GetGlobalRoutingID() const { return GlobalRoutingID(child_id_, route_id_); } diff --git a/chromium/content/browser/loader/resource_request_info_impl.h b/chromium/content/browser/loader/resource_request_info_impl.h index 356efa4cf8a..97cc7198960 100644 --- a/chromium/content/browser/loader/resource_request_info_impl.h +++ b/chromium/content/browser/loader/resource_request_info_impl.h @@ -7,12 +7,14 @@ #include <memory> #include <string> +#include <utility> #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/supports_user_data.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/common/resource_request_body_impl.h" #include "content/public/browser/navigation_ui_data.h" #include "content/public/browser/resource_request_info.h" @@ -21,7 +23,6 @@ #include "net/base/load_states.h" namespace content { -class CrossSiteResourceHandler; class DetachableResourceHandler; class ResourceContext; class ResourceMessageFilter; @@ -78,6 +79,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, ResourceContext* GetContext() const override; int GetChildID() const override; int GetRouteID() const override; + GlobalRequestID GetGlobalRequestID() const override; int GetOriginPID() const override; int GetRenderFrameID() const override; int GetFrameTreeNodeId() const override; @@ -101,7 +103,6 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, CONTENT_EXPORT void AssociateWithRequest(net::URLRequest* request); CONTENT_EXPORT int GetRequestID() const; - CONTENT_EXPORT GlobalRequestID GetGlobalRequestID() const; GlobalRoutingID GetGlobalRoutingID() const; // PlzNavigate @@ -125,14 +126,6 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, int request_id, base::WeakPtr<ResourceMessageFilter> filter); - // CrossSiteResourceHandler for this request. May be null. - CrossSiteResourceHandler* cross_site_handler() { - return cross_site_handler_; - } - void set_cross_site_handler(CrossSiteResourceHandler* h) { - cross_site_handler_ = h; - } - // Whether this request is part of a navigation that should replace the // current session history entry. This state is shuffled up and down the stack // for request transfers. @@ -205,13 +198,23 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, navigation_ui_data_ = std::move(navigation_ui_data); } + // PlzNavigate: used in navigations to store the ServiceWorkerContext, since + // the ResourceMessageFilter will be null in this case. All other requests + // should access the ServiceWorkerContext through the ResourceMessageFilter. + void set_service_worker_context( + scoped_refptr<ServiceWorkerContextWrapper> service_worker_context) { + service_worker_context_ = service_worker_context; + } + ServiceWorkerContextWrapper* service_worker_context() const { + return service_worker_context_.get(); + } + private: FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest, DeletedFilterDetached); FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest, DeletedFilterDetachedRedirect); // Non-owning, may be NULL. - CrossSiteResourceHandler* cross_site_handler_; DetachableResourceHandler* detachable_handler_; int process_type_; @@ -249,6 +252,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, scoped_refptr<ResourceRequestBodyImpl> body_; bool initiated_in_secure_context_; std::unique_ptr<NavigationUIData> navigation_ui_data_; + scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_; DISALLOW_COPY_AND_ASSIGN(ResourceRequestInfoImpl); }; diff --git a/chromium/content/browser/loader/resource_scheduler.cc b/chromium/content/browser/loader/resource_scheduler.cc index 2253b57d4a9..ad67fa91cd5 100644 --- a/chromium/content/browser/loader/resource_scheduler.cc +++ b/chromium/content/browser/loader/resource_scheduler.cc @@ -10,6 +10,7 @@ #include <utility> #include <vector> +#include "base/feature_list.h" #include "base/macros.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram_macros.h" @@ -31,6 +32,13 @@ namespace content { namespace { +// When kPrioritySupportedRequestsDelayable is enabled, requests for +// H2/QUIC/SPDY resources can be delayed by the ResourceScheduler just as +// HTTP/1.1 resources are. Disabling this appears to have negative performance +// impact, see https://crbug.com/655585. +const base::Feature kPrioritySupportedRequestsDelayable{ + "PrioritySupportedRequestsDelayable", base::FEATURE_DISABLED_BY_DEFAULT}; + enum StartMode { START_SYNC, START_ASYNC @@ -170,6 +178,7 @@ class ResourceScheduler::ScheduledResourceRequest : public ResourceThrottle { scheduler_(scheduler), priority_(priority), fifo_ordering_(0), + host_port_pair_(net::HostPortPair::FromURL(request->url())), weak_ptr_factory_(this) { DCHECK(!request_->GetUserData(kUserDataKey)); request_->SetUserData(kUserDataKey, new UnownedPointer(this)); @@ -235,6 +244,7 @@ class ResourceScheduler::ScheduledResourceRequest : public ResourceThrottle { void set_attributes(RequestAttributes attributes) { attributes_ = attributes; } + const net::HostPortPair& host_port_pair() const { return host_port_pair_; } private: class UnownedPointer : public base::SupportsUserData::Data { @@ -268,6 +278,8 @@ class ResourceScheduler::ScheduledResourceRequest : public ResourceThrottle { ResourceScheduler* scheduler_; RequestPriorityParams priority_; uint32_t fifo_ordering_; + // Cached to excessive recomputation in ShouldKeepSearching. + const net::HostPortPair host_port_pair_; base::WeakPtrFactory<ResourceScheduler::ScheduledResourceRequest> weak_ptr_factory_; @@ -304,12 +316,13 @@ void ResourceScheduler::RequestQueue::Insert( // Each client represents a tab. class ResourceScheduler::Client { public: - explicit Client(ResourceScheduler* scheduler) + explicit Client(bool priority_requests_delayable) : is_loaded_(false), has_html_body_(false), using_spdy_proxy_(false), in_flight_delayable_count_(0), - total_layout_blocking_count_(0) {} + total_layout_blocking_count_(0), + priority_requests_delayable_(priority_requests_delayable) {} ~Client() {} @@ -513,14 +526,20 @@ class ResourceScheduler::Client { attributes |= kAttributeLayoutBlocking; } else if (request->url_request()->priority() < kDelayablePriorityThreshold) { - // Resources below the delayable priority threshold that are being - // requested from a server that does not support native prioritization are - // considered delayable. - url::SchemeHostPort scheme_host_port(request->url_request()->url()); - net::HttpServerProperties& http_server_properties = - *request->url_request()->context()->http_server_properties(); - if (!http_server_properties.SupportsRequestPriority(scheme_host_port)) + if (priority_requests_delayable_) { + // Resources below the delayable priority threshold that are considered + // delayable. attributes |= kAttributeDelayable; + } else { + // Resources below the delayable priority threshold that are being + // requested from a server that does not support native prioritization + // are considered delayable. + url::SchemeHostPort scheme_host_port(request->url_request()->url()); + net::HttpServerProperties& http_server_properties = + *request->url_request()->context()->http_server_properties(); + if (!http_server_properties.SupportsRequestPriority(scheme_host_port)) + attributes |= kAttributeDelayable; + } } return attributes; @@ -531,9 +550,7 @@ class ResourceScheduler::Client { size_t same_host_count = 0; for (RequestSet::const_iterator it = in_flight_requests_.begin(); it != in_flight_requests_.end(); ++it) { - net::HostPortPair host_port_pair = - net::HostPortPair::FromURL((*it)->url_request()->url()); - if (active_request_host.Equals(host_port_pair)) { + if (active_request_host.Equals((*it)->host_port_pair())) { same_host_count++; if (same_host_count >= kMaxNumDelayableRequestsPerHostPerClient) return true; @@ -595,20 +612,23 @@ class ResourceScheduler::Client { if (!url_request.url().SchemeIsHTTPOrHTTPS()) return START_REQUEST; - if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme)) - return START_REQUEST; + const net::HostPortPair& host_port_pair = request->host_port_pair(); - net::HostPortPair host_port_pair = - net::HostPortPair::FromURL(url_request.url()); - url::SchemeHostPort scheme_host_port(url_request.url()); - net::HttpServerProperties& http_server_properties = - *url_request.context()->http_server_properties(); + if (!priority_requests_delayable_) { + if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme)) + return START_REQUEST; - // TODO(willchan): We should really improve this algorithm as described in - // crbug.com/164101. Also, theoretically we should not count a - // request-priority capable request against the delayable requests limit. - if (http_server_properties.SupportsRequestPriority(scheme_host_port)) - return START_REQUEST; + url::SchemeHostPort scheme_host_port(url_request.url()); + + net::HttpServerProperties& http_server_properties = + *url_request.context()->http_server_properties(); + + // TODO(willchan): We should really improve this algorithm as described in + // crbug.com/164101. Also, theoretically we should not count a + // request-priority capable request against the delayable requests limit. + if (http_server_properties.SupportsRequestPriority(scheme_host_port)) + return START_REQUEST; + } // Non-delayable requests. if (!RequestAttributesAreSet(request->attributes(), kAttributeDelayable)) @@ -698,9 +718,15 @@ class ResourceScheduler::Client { size_t in_flight_delayable_count_; // The number of layout-blocking in-flight requests. size_t total_layout_blocking_count_; + + // True if requests to servers that support priorities (e.g., H2/QUIC) can + // be delayed. + bool priority_requests_delayable_; }; -ResourceScheduler::ResourceScheduler() {} +ResourceScheduler::ResourceScheduler() + : priority_requests_delayable_( + base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)) {} ResourceScheduler::~ResourceScheduler() { DCHECK(unowned_requests_.empty()); @@ -757,7 +783,7 @@ void ResourceScheduler::OnClientCreated(int child_id, ClientId client_id = MakeClientId(child_id, route_id); DCHECK(!base::ContainsKey(client_map_, client_id)); - Client* client = new Client(this); + Client* client = new Client(priority_requests_delayable_); client_map_[client_id] = client; } diff --git a/chromium/content/browser/loader/resource_scheduler.h b/chromium/content/browser/loader/resource_scheduler.h index 0b4218227b9..76b455c409f 100644 --- a/chromium/content/browser/loader/resource_scheduler.h +++ b/chromium/content/browser/loader/resource_scheduler.h @@ -20,7 +20,6 @@ #include "net/base/request_priority.h" namespace net { -class HostPortPair; class URLRequest; } @@ -137,6 +136,10 @@ class CONTENT_EXPORT ResourceScheduler : public base::NonThreadSafe { size_t max_num_delayable_requests_; RequestSet unowned_requests_; + // True if requests to servers that support priorities (e.g., H2/QUIC) can + // be delayed. + bool priority_requests_delayable_; + DISALLOW_COPY_AND_ASSIGN(ResourceScheduler); }; diff --git a/chromium/content/browser/loader/resource_scheduler_unittest.cc b/chromium/content/browser/loader/resource_scheduler_unittest.cc index 51b8a47b68e..cc40deabab4 100644 --- a/chromium/content/browser/loader/resource_scheduler_unittest.cc +++ b/chromium/content/browser/loader/resource_scheduler_unittest.cc @@ -8,18 +8,17 @@ #include "base/memory/ptr_util.h" #include "base/memory/scoped_vector.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" +#include "base/test/scoped_feature_list.h" #include "base/timer/mock_timer.h" #include "base/timer/timer.h" -#include "content/browser/browser_thread_impl.h" -#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/public/browser/resource_context.h" #include "content/public/browser/resource_controller.h" #include "content/public/browser/resource_throttle.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" +#include "content/public/test/test_browser_thread_bundle.h" #include "content/test/test_render_view_host_factory.h" #include "content/test/test_web_contents.h" #include "net/base/host_port_pair.h" @@ -46,6 +45,9 @@ const int kRouteId2 = 67; const int kBackgroundChildId = 35; const int kBackgroundRouteId = 43; +const char kPrioritySupportedRequestsDelayable[] = + "PrioritySupportedRequestsDelayable"; + class TestRequest : public ResourceController { public: TestRequest(std::unique_ptr<net::URLRequest> url_request, @@ -124,9 +126,7 @@ class FakeResourceContext : public ResourceContext { class ResourceSchedulerTest : public testing::Test { protected: - ResourceSchedulerTest() - : ui_thread_(BrowserThread::UI, &message_loop_), - io_thread_(BrowserThread::IO, &message_loop_) { + ResourceSchedulerTest() { InitializeScheduler(); context_.set_http_server_properties(&http_server_properties_); } @@ -242,10 +242,7 @@ class ResourceSchedulerTest : public testing::Test { return scheduler_.get(); } - base::MessageLoopForIO message_loop_; - BrowserThreadImpl ui_thread_; - BrowserThreadImpl io_thread_; - ResourceDispatcherHostImpl rdh_; + TestBrowserThreadBundle thread_bundle_; std::unique_ptr<ResourceScheduler> scheduler_; base::MockTimer* mock_timer_; net::HttpServerPropertiesImpl http_server_properties_; @@ -314,6 +311,11 @@ TEST_F(ResourceSchedulerTest, MediumDoesNotBlockCriticalComplete) { } TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInsertedExceptSpdy) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitFromCommandLine("", + kPrioritySupportedRequestsDelayable); + InitializeScheduler(); + http_server_properties_.SetSupportsSpdy( url::SchemeHostPort("https", "spdyhost", 443), true); std::unique_ptr<TestRequest> high( @@ -333,6 +335,32 @@ TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInsertedExceptSpdy) { EXPECT_TRUE(low2->started()); } +TEST_F(ResourceSchedulerTest, + OneLowLoadsUntilBodyInsertedEvenSpdyWhenDelayable) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitFromCommandLine(kPrioritySupportedRequestsDelayable, + ""); + + InitializeScheduler(); + http_server_properties_.SetSupportsSpdy( + url::SchemeHostPort("https", "spdyhost", 443), true); + std::unique_ptr<TestRequest> high( + NewRequest("http://host/high", net::HIGHEST)); + std::unique_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST)); + std::unique_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST)); + std::unique_ptr<TestRequest> low_spdy( + NewRequest("https://spdyhost/low", net::LOWEST)); + EXPECT_TRUE(high->started()); + EXPECT_TRUE(low->started()); + EXPECT_FALSE(low2->started()); + EXPECT_FALSE(low_spdy->started()); + + scheduler()->OnWillInsertBody(kChildId, kRouteId); + high.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(low2->started()); +} + TEST_F(ResourceSchedulerTest, SpdyLowBlocksOtherLowUntilBodyInserted) { http_server_properties_.SetSupportsSpdy( url::SchemeHostPort("https", "spdyhost", 443), true); @@ -631,6 +659,11 @@ TEST_F(ResourceSchedulerTest, NonHTTPSchedulesImmediately) { } TEST_F(ResourceSchedulerTest, SpdyProxySchedulesImmediately) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitFromCommandLine("", + kPrioritySupportedRequestsDelayable); + InitializeScheduler(); + std::unique_ptr<TestRequest> high( NewRequest("http://host/high", net::HIGHEST)); std::unique_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST)); @@ -648,7 +681,35 @@ TEST_F(ResourceSchedulerTest, SpdyProxySchedulesImmediately) { EXPECT_TRUE(after->started()); } +TEST_F(ResourceSchedulerTest, SpdyProxyDelayable) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitFromCommandLine(kPrioritySupportedRequestsDelayable, + ""); + InitializeScheduler(); + + std::unique_ptr<TestRequest> high( + NewRequest("http://host/high", net::HIGHEST)); + std::unique_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST)); + + std::unique_ptr<TestRequest> request( + NewRequest("http://host/req", net::IDLE)); + EXPECT_FALSE(request->started()); + + scheduler()->OnReceivedSpdyProxiedHttpResponse(kChildId, kRouteId); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(request->started()); + + std::unique_ptr<TestRequest> after( + NewRequest("http://host/after", net::IDLE)); + EXPECT_FALSE(after->started()); +} + TEST_F(ResourceSchedulerTest, NewSpdyHostInDelayableRequests) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitFromCommandLine("", + kPrioritySupportedRequestsDelayable); + InitializeScheduler(); + scheduler()->OnWillInsertBody(kChildId, kRouteId); const int kMaxNumDelayableRequestsPerClient = 10; // Should match the .cc. @@ -682,6 +743,45 @@ TEST_F(ResourceSchedulerTest, NewSpdyHostInDelayableRequests) { EXPECT_TRUE(low2->started()); } +TEST_F(ResourceSchedulerTest, NewDelayableSpdyHostInDelayableRequests) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitFromCommandLine(kPrioritySupportedRequestsDelayable, + ""); + InitializeScheduler(); + + scheduler()->OnWillInsertBody(kChildId, kRouteId); + const int kMaxNumDelayableRequestsPerClient = 10; // Should match the .cc. + + std::unique_ptr<TestRequest> low1_spdy( + NewRequest("http://spdyhost1:8080/low", net::LOWEST)); + // Cancel a request after we learn the server supports SPDY. + ScopedVector<TestRequest> lows; + for (int i = 0; i < kMaxNumDelayableRequestsPerClient - 1; ++i) { + string url = "http://host" + base::IntToString(i) + "/low"; + lows.push_back(NewRequest(url.c_str(), net::LOWEST)); + } + std::unique_ptr<TestRequest> low1(NewRequest("http://host/low", net::LOWEST)); + EXPECT_FALSE(low1->started()); + http_server_properties_.SetSupportsSpdy( + url::SchemeHostPort("http", "spdyhost1", 8080), true); + low1_spdy.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(low1->started()); + + low1.reset(); + base::RunLoop().RunUntilIdle(); + std::unique_ptr<TestRequest> low2_spdy( + NewRequest("http://spdyhost2:8080/low", net::IDLE)); + // Reprioritize a request after we learn the server supports SPDY. + EXPECT_TRUE(low2_spdy->started()); + http_server_properties_.SetSupportsSpdy( + url::SchemeHostPort("http", "spdyhost2", 8080), true); + ChangeRequestPriority(low2_spdy.get(), net::LOWEST); + base::RunLoop().RunUntilIdle(); + std::unique_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST)); + EXPECT_FALSE(low2->started()); +} + // Async revalidations which are not started when the tab is closed must be // started at some point, or they will hang around forever and prevent other // async revalidations to the same URL from being issued. diff --git a/chromium/content/browser/loader/sync_resource_handler.cc b/chromium/content/browser/loader/sync_resource_handler.cc index 47991f5fe24..b0475ce0db1 100644 --- a/chromium/content/browser/loader/sync_resource_handler.cc +++ b/chromium/content/browser/loader/sync_resource_handler.cc @@ -4,6 +4,7 @@ #include "content/browser/loader/sync_resource_handler.h" +#include "base/callback_helpers.h" #include "base/logging.h" #include "content/browser/loader/netlog_observer.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" @@ -20,25 +21,19 @@ namespace content { SyncResourceHandler::SyncResourceHandler( net::URLRequest* request, - IPC::Message* result_message, + const SyncLoadResultCallback& result_handler, ResourceDispatcherHostImpl* resource_dispatcher_host) : ResourceHandler(request), read_buffer_(new net::IOBuffer(kReadBufSize)), - result_message_(result_message), + result_handler_(result_handler), rdh_(resource_dispatcher_host), total_transfer_size_(0) { result_.final_url = request->url(); } SyncResourceHandler::~SyncResourceHandler() { - if (result_message_) { - result_message_->set_reply_error(); - ResourceMessageFilter* filter = GetFilter(); - // If the filter doesn't exist at this point, the process has died and isn't - // waiting for the result message anymore. - if (filter) - filter->Send(result_message_); - } + if (result_handler_) + result_handler_.Run(nullptr); } bool SyncResourceHandler::OnRequestRedirected( @@ -114,20 +109,13 @@ bool SyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { void SyncResourceHandler::OnResponseCompleted( const net::URLRequestStatus& status, bool* defer) { - ResourceMessageFilter* filter = GetFilter(); - if (!filter) - return; - result_.error_code = status.error(); int total_transfer_size = request()->GetTotalReceivedBytes(); result_.encoded_data_length = total_transfer_size_ + total_transfer_size; result_.encoded_body_length = request()->GetRawBodyBytes(); - ResourceHostMsg_SyncLoad::WriteReplyParams(result_message_, result_); - filter->Send(result_message_); - result_message_ = NULL; - return; + base::ResetAndReturn(&result_handler_).Run(&result_); } void SyncResourceHandler::OnDataDownloaded(int bytes_downloaded) { diff --git a/chromium/content/browser/loader/sync_resource_handler.h b/chromium/content/browser/loader/sync_resource_handler.h index 00e2794002c..2ab87a79ca5 100644 --- a/chromium/content/browser/loader/sync_resource_handler.h +++ b/chromium/content/browser/loader/sync_resource_handler.h @@ -9,6 +9,7 @@ #include <string> +#include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_handler.h" #include "content/public/common/resource_response.h" @@ -22,16 +23,16 @@ class URLRequest; } namespace content { -class ResourceContext; -class ResourceDispatcherHostImpl; -class ResourceMessageFilter; // Used to complete a synchronous resource request in response to resource load // events from the resource dispatcher host. class SyncResourceHandler : public ResourceHandler { public: + using SyncLoadResultCallback = + ResourceDispatcherHostImpl::SyncLoadResultCallback; + SyncResourceHandler(net::URLRequest* request, - IPC::Message* result_message, + const SyncLoadResultCallback& sync_result_handler, ResourceDispatcherHostImpl* resource_dispatcher_host); ~SyncResourceHandler() override; @@ -54,7 +55,7 @@ class SyncResourceHandler : public ResourceHandler { scoped_refptr<net::IOBuffer> read_buffer_; SyncLoadResult result_; - IPC::Message* result_message_; + SyncLoadResultCallback result_handler_; ResourceDispatcherHostImpl* rdh_; int64_t total_transfer_size_; }; diff --git a/chromium/content/browser/loader/test_resource_handler.cc b/chromium/content/browser/loader/test_resource_handler.cc new file mode 100644 index 00000000000..0ee72ae6744 --- /dev/null +++ b/chromium/content/browser/loader/test_resource_handler.cc @@ -0,0 +1,111 @@ +// Copyright 2016 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/test_resource_handler.h" + +#include "base/logging.h" +#include "net/url_request/url_request_status.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +TestResourceHandler::TestResourceHandler(net::URLRequestStatus* request_status, + std::string* body) + : ResourceHandler(nullptr), request_status_(request_status), body_(body) { + SetBufferSize(2048); +} + +TestResourceHandler::TestResourceHandler() + : TestResourceHandler(nullptr, nullptr) {} + +TestResourceHandler::~TestResourceHandler() {} + +void TestResourceHandler::SetController(ResourceController* controller) {} + +bool TestResourceHandler::OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + bool* defer) { + NOTREACHED() << "Redirects are not supported by the TestResourceHandler."; + return false; +} + +bool TestResourceHandler::OnResponseStarted(ResourceResponse* response, + bool* defer) { + EXPECT_EQ(1, on_will_start_called_); + EXPECT_EQ(0, on_response_started_called_); + EXPECT_EQ(0, on_response_completed_called_); + ++on_response_started_called_; + + if (!on_response_started_result_) + return false; + *defer = defer_on_response_started_; + defer_on_response_started_ = false; + return true; +} + +bool TestResourceHandler::OnWillStart(const GURL& url, bool* defer) { + EXPECT_EQ(0, on_response_started_called_); + EXPECT_EQ(0, on_will_start_called_); + EXPECT_EQ(0, on_response_completed_called_); + ++on_will_start_called_; + + if (!on_will_start_result_) + return false; + + *defer = defer_on_will_start_; + return true; +} + +bool TestResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, + int* buf_size, + int min_size) { + EXPECT_EQ(0, on_response_completed_called_); + ++on_will_read_called_; + + *buf = buffer_; + *buf_size = buffer_size_; + memset(buffer_->data(), '\0', buffer_size_); + return on_will_read_result_; +} + +bool TestResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { + EXPECT_EQ(1, on_will_start_called_); + EXPECT_EQ(1, on_response_started_called_); + EXPECT_EQ(0, on_response_completed_called_); + ++on_read_completed_called_; + + EXPECT_LE(static_cast<size_t>(bytes_read), buffer_size_); + if (body_) + body_->append(buffer_->data(), bytes_read); + if (!on_read_completed_result_) + return false; + *defer = defer_on_read_completed_; + defer_on_read_completed_ = false; + return true; +} + +void TestResourceHandler::OnResponseCompleted( + const net::URLRequestStatus& status, + bool* defer) { + EXPECT_EQ(0, on_response_completed_called_); + ++on_response_completed_called_; + + if (request_status_) + *request_status_ = status; + *defer = defer_on_response_completed_; + defer_on_response_completed_ = false; +} + +void TestResourceHandler::OnDataDownloaded(int bytes_downloaded) { + NOTREACHED() << "Saving to file is not supported by the TestResourceHandler."; +} + +void TestResourceHandler::SetBufferSize(int buffer_size) { + buffer_ = new net::IOBuffer(buffer_size); + buffer_size_ = buffer_size; + memset(buffer_->data(), '\0', buffer_size); +} + +} // namespace content diff --git a/chromium/content/browser/loader/test_resource_handler.h b/chromium/content/browser/loader/test_resource_handler.h new file mode 100644 index 00000000000..134f9a6e42c --- /dev/null +++ b/chromium/content/browser/loader/test_resource_handler.h @@ -0,0 +1,132 @@ +// Copyright 2016 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_TEST_RESOURCE_HANDLER_H_ +#define CONTENT_BROWSER_LOADER_TEST_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/io_buffer.h" + +class GURL; + +namespace net { +class URLRequest; +class URLRequestStatus; +} + +namespace content { + +class ResourceHandler; +struct ResourceResponse; + +// A test version of a ResourceHandler. It returns a configurable buffer in +// response to OnWillStart. It records what ResourceHandler methods are called, +// and verifies that they are called in the correct order. It can optionally +// defer or fail the request at any stage, and record the response body and +// final status it sees. Redirects currently not supported. +class TestResourceHandler : public ResourceHandler { + public: + // If non-null, |request_status| will be updated when the response is complete + // with the final status of the request received by the handler and |body| + // will be updated on each OnReadCompleted call. + TestResourceHandler(net::URLRequestStatus* request_status, std::string* body); + TestResourceHandler(); + ~TestResourceHandler() override; + + // ResourceHandler implementation: + void SetController(ResourceController* controller) override; + bool OnRequestRedirected(const net::RedirectInfo& redirect_info, + ResourceResponse* response, + bool* defer) override; + bool OnResponseStarted(ResourceResponse* response, bool* defer) override; + bool OnWillStart(const GURL& url, bool* defer) override; + bool OnWillRead(scoped_refptr<net::IOBuffer>* buf, + int* buf_size, + int min_size) override; + bool OnReadCompleted(int bytes_read, bool* defer) override; + void OnResponseCompleted(const net::URLRequestStatus& status, + bool* defer) override; + void OnDataDownloaded(int bytes_downloaded) override; + + // Sets the size of the read buffer returned by OnWillRead. Releases reference + // to previous read buffer. Default size is 2048 bytes. + void SetBufferSize(int buffer_size); + + scoped_refptr<net::IOBuffer> buffer() const { return buffer_; } + + // Sets the result returned by each method. All default to returning true. + void set_on_will_start_result(bool on_will_start_result) { + on_will_start_result_ = on_will_start_result; + } + void set_on_response_started_result(bool on_response_started_result) { + on_response_started_result_ = on_response_started_result; + } + void set_on_will_read_result(bool on_will_read_result) { + on_will_read_result_ = on_will_read_result; + } + void set_on_read_completed_result(bool on_read_completed_result) { + on_read_completed_result_ = on_read_completed_result; + } + + // Cause |defer| to be set to true when the specified method is invoked. The + // test itself is responsible for resuming the request after deferral. + + void set_defer_on_will_start(bool defer_on_will_start) { + defer_on_will_start_ = defer_on_will_start; + } + void set_defer_on_response_started(bool defer_on_response_started) { + defer_on_response_started_ = defer_on_response_started; + } + // Only the next OnReadCompleted call will set |defer| to true. + void set_defer_on_read_completed(bool defer_on_read_completed) { + defer_on_read_completed_ = defer_on_read_completed; + } + void set_defer_on_response_completed(bool defer_on_response_completed) { + defer_on_response_completed_ = defer_on_response_completed; + } + + // Return the number of times the corresponding method was invoked. + + int on_will_start_called() const { return on_will_start_called_; } + // Redirection currently not supported. + int on_request_redirected_called() const { return 0; } + int on_response_started_called() const { return on_response_started_called_; } + int on_will_read_called() const { return on_will_read_called_; } + int on_read_completed_called() const { return on_read_completed_called_; } + int on_response_completed_called() const { + return on_response_completed_called_; + } + + private: + net::URLRequestStatus* request_status_; + std::string* body_; + scoped_refptr<net::IOBuffer> buffer_; + size_t buffer_size_; + + bool on_will_start_result_ = true; + bool on_response_started_result_ = true; + bool on_will_read_result_ = true; + bool on_read_completed_result_ = true; + + bool defer_on_will_start_ = false; + bool defer_on_response_started_ = false; + bool defer_on_read_completed_ = false; + bool defer_on_response_completed_ = false; + + int on_will_start_called_ = 0; + int on_response_started_called_ = 0; + int on_will_read_called_ = 0; + int on_read_completed_called_ = 0; + int on_response_completed_called_ = 0; + + DISALLOW_COPY_AND_ASSIGN(TestResourceHandler); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_LOADER_TEST_RESOURCE_HANDLER_H_ diff --git a/chromium/content/browser/loader/test_url_loader_client.cc b/chromium/content/browser/loader/test_url_loader_client.cc index 035a050d28a..caa858312f4 100644 --- a/chromium/content/browser/loader/test_url_loader_client.cc +++ b/chromium/content/browser/loader/test_url_loader_client.cc @@ -4,7 +4,9 @@ #include "content/browser/loader/test_url_loader_client.h" +#include "base/memory/ref_counted.h" #include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" namespace content { @@ -15,8 +17,31 @@ void TestURLLoaderClient::OnReceiveResponse( const ResourceResponseHead& response_head) { has_received_response_ = true; response_head_ = response_head; - if (quit_closure_for_on_received_response_) - quit_closure_for_on_received_response_.Run(); + if (quit_closure_for_on_receive_response_) + quit_closure_for_on_receive_response_.Run(); +} + +void TestURLLoaderClient::OnReceiveRedirect( + const net::RedirectInfo& redirect_info, + const ResourceResponseHead& response_head) { + EXPECT_FALSE(response_body_.is_valid()); + EXPECT_FALSE(has_received_response_); + // Use ClearHasReceivedRedirect to accept more redirects. + EXPECT_FALSE(has_received_redirect_); + has_received_redirect_ = true; + redirect_info_ = redirect_info; + response_head_ = response_head; + if (quit_closure_for_on_receive_redirect_) + quit_closure_for_on_receive_redirect_.Run(); +} + +void TestURLLoaderClient::OnDataDownloaded(int64_t data_length, + int64_t encoded_data_length) { + has_data_downloaded_ = true; + download_data_length_ += data_length; + encoded_download_data_length_ += encoded_data_length; + if (quit_closure_for_on_data_downloaded_) + quit_closure_for_on_data_downloaded_.Run(); } void TestURLLoaderClient::OnStartLoadingResponseBody( @@ -34,8 +59,16 @@ void TestURLLoaderClient::OnComplete( quit_closure_for_on_complete_.Run(); } -mojom::URLLoaderClientPtr TestURLLoaderClient::CreateInterfacePtrAndBind() { - return binding_.CreateInterfacePtrAndBind(); +void TestURLLoaderClient::ClearHasReceivedRedirect() { + has_received_redirect_ = false; +} + +mojom::URLLoaderClientAssociatedPtrInfo +TestURLLoaderClient::CreateRemoteAssociatedPtrInfo( + mojo::AssociatedGroup* associated_group) { + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info; + binding_.Bind(&client_ptr_info, associated_group); + return client_ptr_info; } void TestURLLoaderClient::Unbind() { @@ -45,9 +78,23 @@ void TestURLLoaderClient::Unbind() { void TestURLLoaderClient::RunUntilResponseReceived() { base::RunLoop run_loop; - quit_closure_for_on_received_response_ = run_loop.QuitClosure(); + quit_closure_for_on_receive_response_ = run_loop.QuitClosure(); + run_loop.Run(); + quit_closure_for_on_receive_response_.Reset(); +} + +void TestURLLoaderClient::RunUntilRedirectReceived() { + base::RunLoop run_loop; + quit_closure_for_on_receive_redirect_ = run_loop.QuitClosure(); + run_loop.Run(); + quit_closure_for_on_receive_redirect_.Reset(); +} + +void TestURLLoaderClient::RunUntilDataDownloaded() { + base::RunLoop run_loop; + quit_closure_for_on_data_downloaded_ = run_loop.QuitClosure(); run_loop.Run(); - quit_closure_for_on_received_response_.Reset(); + quit_closure_for_on_data_downloaded_.Reset(); } void TestURLLoaderClient::RunUntilResponseBodyArrived() { diff --git a/chromium/content/browser/loader/test_url_loader_client.h b/chromium/content/browser/loader/test_url_loader_client.h index 78cb46f3f27..a12709bcca6 100644 --- a/chromium/content/browser/loader/test_url_loader_client.h +++ b/chromium/content/browser/loader/test_url_loader_client.h @@ -5,13 +5,17 @@ #ifndef CONTENT_BROWSER_LOADER_TEST_URL_LOADER_CLIENT_H_ #define CONTENT_BROWSER_LOADER_TEST_URL_LOADER_CLIENT_H_ +#include <stdint.h> + #include "base/callback.h" #include "base/macros.h" #include "content/common/resource_request_completion_status.h" #include "content/common/url_loader.mojom.h" +#include "content/common/url_loader_factory.mojom.h" #include "content/public/common/resource_response.h" #include "mojo/public/c/system/data_pipe.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/associated_binding.h" +#include "net/url_request/redirect_info.h" namespace content { @@ -25,35 +29,61 @@ class TestURLLoaderClient final : public mojom::URLLoaderClient { ~TestURLLoaderClient() override; void OnReceiveResponse(const ResourceResponseHead& response_head) override; + void OnReceiveRedirect(const net::RedirectInfo& redirect_info, + const ResourceResponseHead& response_head) override; + void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override; void OnStartLoadingResponseBody( mojo::ScopedDataPipeConsumerHandle body) override; void OnComplete(const ResourceRequestCompletionStatus& status) override; bool has_received_response() const { return has_received_response_; } + bool has_received_redirect() const { return has_received_redirect_; } + bool has_data_downloaded() const { return has_data_downloaded_; } bool has_received_completion() const { return has_received_completion_; } const ResourceResponseHead& response_head() const { return response_head_; } + const net::RedirectInfo& redirect_info() const { return redirect_info_; } mojo::DataPipeConsumerHandle response_body() { return response_body_.get(); } const ResourceRequestCompletionStatus& completion_status() const { return completion_status_; } + int64_t download_data_length() const { return download_data_length_; } + int64_t encoded_download_data_length() const { + return encoded_download_data_length_; + } + + void ClearHasReceivedRedirect(); + // Creates an AssociatedPtrInfo, binds it to |*this| and returns it. The + // returned PtrInfo is marked as remote, i.e., expected to be passed to the + // remote endpoint. + mojom::URLLoaderClientAssociatedPtrInfo CreateRemoteAssociatedPtrInfo( + mojo::AssociatedGroup* associated_group); - mojom::URLLoaderClientPtr CreateInterfacePtrAndBind(); void Unbind(); void RunUntilResponseReceived(); + void RunUntilRedirectReceived(); + void RunUntilDataDownloaded(); void RunUntilResponseBodyArrived(); void RunUntilComplete(); private: - mojo::Binding<mojom::URLLoaderClient> binding_; + mojo::AssociatedBinding<mojom::URLLoaderClient> binding_; ResourceResponseHead response_head_; + net::RedirectInfo redirect_info_; mojo::ScopedDataPipeConsumerHandle response_body_; ResourceRequestCompletionStatus completion_status_; bool has_received_response_ = false; + bool has_received_redirect_ = false; + bool has_data_downloaded_ = false; bool has_received_completion_ = false; - base::Closure quit_closure_for_on_received_response_; + base::Closure quit_closure_for_on_receive_response_; + base::Closure quit_closure_for_on_receive_redirect_; + base::Closure quit_closure_for_on_data_downloaded_; base::Closure quit_closure_for_on_start_loading_response_body_; base::Closure quit_closure_for_on_complete_; + mojom::URLLoaderFactoryPtr url_loader_factory_; + int64_t download_data_length_ = 0; + int64_t encoded_download_data_length_ = 0; DISALLOW_COPY_AND_ASSIGN(TestURLLoaderClient); }; diff --git a/chromium/content/browser/loader/upload_data_stream_builder.cc b/chromium/content/browser/loader/upload_data_stream_builder.cc index f3d79c6fd5a..389a8e9b394 100644 --- a/chromium/content/browser/loader/upload_data_stream_builder.cc +++ b/chromium/content/browser/loader/upload_data_stream_builder.cc @@ -14,6 +14,7 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" #include "content/browser/fileapi/upload_file_system_file_element_reader.h" #include "content/common/resource_request_body_impl.h" #include "net/base/elements_upload_data_stream.h" @@ -24,6 +25,10 @@ #include "storage/browser/blob/blob_storage_context.h" #include "storage/browser/blob/upload_blob_element_reader.h" +namespace base { +class TaskRunner; +} + namespace disk_cache { class Entry; } diff --git a/chromium/content/browser/loader/url_loader_factory_impl.cc b/chromium/content/browser/loader/url_loader_factory_impl.cc index 7b11666452e..c510c147f2b 100644 --- a/chromium/content/browser/loader/url_loader_factory_impl.cc +++ b/chromium/content/browser/loader/url_loader_factory_impl.cc @@ -13,6 +13,26 @@ namespace content { +namespace { + +void DispatchSyncLoadResult( + const URLLoaderFactoryImpl::SyncLoadCallback& callback, + const SyncLoadResult* result) { + // |result| can be null when a loading task is aborted unexpectedly. Reply + // with a failure result on that case. + // TODO(tzik): Test null-result case. + if (!result) { + SyncLoadResult failure; + failure.error_code = net::ERR_FAILED; + callback.Run(failure); + return; + } + + callback.Run(*result); +} + +} // namespace + URLLoaderFactoryImpl::URLLoaderFactoryImpl( scoped_refptr<ResourceMessageFilter> resource_message_filter) : resource_message_filter_(std::move(resource_message_filter)) { @@ -25,17 +45,53 @@ URLLoaderFactoryImpl::~URLLoaderFactoryImpl() { } void URLLoaderFactoryImpl::CreateLoaderAndStart( - mojom::URLLoaderRequest request, + mojom::URLLoaderAssociatedRequest request, int32_t routing_id, int32_t request_id, const ResourceRequest& url_request, - mojom::URLLoaderClientPtr client) { + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info) { + CreateLoaderAndStart(std::move(request), routing_id, request_id, url_request, + std::move(client_ptr_info), + resource_message_filter_.get()); +} + +void URLLoaderFactoryImpl::SyncLoad(int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + const SyncLoadCallback& callback) { + SyncLoad(routing_id, request_id, url_request, callback, + resource_message_filter_.get()); +} + +// static +void URLLoaderFactoryImpl::CreateLoaderAndStart( + mojom::URLLoaderAssociatedRequest request, + int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info, + ResourceMessageFilter* filter) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + mojom::URLLoaderClientAssociatedPtr client; + client.Bind(std::move(client_ptr_info)); + ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get(); rdh->OnRequestResourceWithMojo(routing_id, request_id, url_request, - std::move(request), std::move(client), - resource_message_filter_.get()); + std::move(request), std::move(client), filter); +} + +// static +void URLLoaderFactoryImpl::SyncLoad(int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + const SyncLoadCallback& callback, + ResourceMessageFilter* filter) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get(); + rdh->OnSyncLoadWithMojo(routing_id, request_id, url_request, filter, + base::Bind(&DispatchSyncLoadResult, callback)); } void URLLoaderFactoryImpl::Create( diff --git a/chromium/content/browser/loader/url_loader_factory_impl.h b/chromium/content/browser/loader/url_loader_factory_impl.h index a54c44bf6c6..e989c3d872b 100644 --- a/chromium/content/browser/loader/url_loader_factory_impl.h +++ b/chromium/content/browser/loader/url_loader_factory_impl.h @@ -9,7 +9,6 @@ #include "base/memory/ref_counted.h" #include "content/common/content_export.h" #include "content/common/url_loader_factory.mojom.h" -#include "mojo/public/cpp/bindings/interface_request.h" namespace content { @@ -21,11 +20,29 @@ class URLLoaderFactoryImpl final : public mojom::URLLoaderFactory { public: ~URLLoaderFactoryImpl() override; - void CreateLoaderAndStart(mojom::URLLoaderRequest request, - int32_t routing_id, - int32_t request_id, - const ResourceRequest& url_request, - mojom::URLLoaderClientPtr client) override; + void CreateLoaderAndStart( + mojom::URLLoaderAssociatedRequest request, + int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info) override; + void SyncLoad(int32_t routing_id, + int32_t request_id, + const ResourceRequest& request, + const SyncLoadCallback& callback) override; + + static void CreateLoaderAndStart( + mojom::URLLoaderAssociatedRequest request, + int32_t routing_id, + int32_t request_id, + const ResourceRequest& url_request, + mojom::URLLoaderClientAssociatedPtrInfo client_ptr_info, + ResourceMessageFilter* filter); + static void SyncLoad(int32_t routing_id, + int32_t request_id, + const ResourceRequest& request, + const SyncLoadCallback& callback, + ResourceMessageFilter* filter); // Creates a URLLoaderFactoryImpl instance. The instance is held by the // StrongBinding in it, so this function doesn't return the instance. diff --git a/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc b/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc index 84d082b6292..97f81a3f83b 100644 --- a/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc +++ b/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/url_loader_factory_impl.h" +#include <stdint.h> + #include <memory> #include <string> #include <utility> @@ -46,6 +48,7 @@ #include "net/http/http_util.h" #include "net/test/url_request/url_request_failed_job.h" #include "net/test/url_request/url_request_mock_http_job.h" +#include "net/test/url_request/url_request_slow_download_job.h" #include "net/url_request/url_request_filter.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -90,7 +93,6 @@ class URLLoaderFactoryImplTest : public ::testing::TestWithParam<size_t> { nullptr, nullptr, nullptr, - nullptr, base::Bind(&URLLoaderFactoryImplTest::GetContexts, base::Unretained(this)))) { MojoAsyncResourceHandler::SetAllocationSizeForTesting(GetParam()); @@ -108,6 +110,7 @@ class URLLoaderFactoryImplTest : public ::testing::TestWithParam<size_t> { rdh_.SetDelegate(nullptr); net::URLRequestFilter::GetInstance()->ClearHandlers(); + resource_message_filter_->OnChannelClosing(); rdh_.CancelRequestsForProcess(resource_message_filter_->child_id()); base::RunLoop().RunUntilIdle(); MojoAsyncResourceHandler::SetAllocationSizeForTesting( @@ -136,7 +139,7 @@ TEST_P(URLLoaderFactoryImplTest, GetResponse) { constexpr int32_t kRoutingId = 81; constexpr int32_t kRequestId = 28; NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing(true); - mojom::URLLoaderPtr loader; + mojom::URLLoaderAssociatedPtr loader; base::FilePath root; PathService::Get(DIR_TEST_DATA, &root); net::URLRequestMockHTTPJob::AddUrlHandlers(root, @@ -147,9 +150,10 @@ TEST_P(URLLoaderFactoryImplTest, GetResponse) { request.url = net::URLRequestMockHTTPJob::GetMockUrl("hello.html"); request.method = "GET"; request.is_main_frame = true; - factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), kRoutingId, - kRequestId, request, - client.CreateInterfacePtrAndBind()); + factory_->CreateLoaderAndStart( + mojo::GetProxy(&loader, factory_.associated_group()), kRoutingId, + kRequestId, request, + client.CreateRemoteAssociatedPtrInfo(factory_.associated_group())); ASSERT_FALSE(client.has_received_response()); ASSERT_FALSE(client.response_body().is_valid()); @@ -168,7 +172,6 @@ TEST_P(URLLoaderFactoryImplTest, GetResponse) { EXPECT_EQ(kRequestId, request_info->GetRequestID()); ASSERT_FALSE(client.has_received_completion()); - ASSERT_FALSE(client.has_received_completion()); client.RunUntilResponseBodyArrived(); ASSERT_TRUE(client.response_body().is_valid()); @@ -204,15 +207,16 @@ TEST_P(URLLoaderFactoryImplTest, GetResponse) { TEST_P(URLLoaderFactoryImplTest, GetFailedResponse) { NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing(true); - mojom::URLLoaderPtr loader; + mojom::URLLoaderAssociatedPtr loader; ResourceRequest request; TestURLLoaderClient client; net::URLRequestFailedJob::AddUrlHandler(); request.url = net::URLRequestFailedJob::GetMockHttpUrlWithFailurePhase( net::URLRequestFailedJob::START, net::ERR_TIMED_OUT); request.method = "GET"; - factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 2, 1, request, - client.CreateInterfacePtrAndBind()); + factory_->CreateLoaderAndStart( + mojo::GetProxy(&loader, factory_.associated_group()), 2, 1, request, + client.CreateRemoteAssociatedPtrInfo(factory_.associated_group())); client.RunUntilComplete(); ASSERT_FALSE(client.has_received_response()); @@ -223,14 +227,15 @@ TEST_P(URLLoaderFactoryImplTest, GetFailedResponse) { // This test tests a case where resource loading is cancelled before started. TEST_P(URLLoaderFactoryImplTest, InvalidURL) { - mojom::URLLoaderPtr loader; + mojom::URLLoaderAssociatedPtr loader; ResourceRequest request; TestURLLoaderClient client; request.url = GURL(); request.method = "GET"; ASSERT_FALSE(request.url.is_valid()); - factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 2, 1, request, - client.CreateInterfacePtrAndBind()); + factory_->CreateLoaderAndStart( + mojo::GetProxy(&loader, factory_.associated_group()), 2, 1, request, + client.CreateRemoteAssociatedPtrInfo(factory_.associated_group())); client.RunUntilComplete(); ASSERT_FALSE(client.has_received_response()); @@ -241,15 +246,16 @@ TEST_P(URLLoaderFactoryImplTest, InvalidURL) { // This test tests a case where resource loading is cancelled before started. TEST_P(URLLoaderFactoryImplTest, ShouldNotRequestURL) { - mojom::URLLoaderPtr loader; + mojom::URLLoaderAssociatedPtr loader; RejectingResourceDispatcherHostDelegate rdh_delegate; rdh_.SetDelegate(&rdh_delegate); ResourceRequest request; TestURLLoaderClient client; request.url = GURL("http://localhost/"); request.method = "GET"; - factory_->CreateLoaderAndStart(mojo::GetProxy(&loader), 2, 1, request, - client.CreateInterfacePtrAndBind()); + factory_->CreateLoaderAndStart( + mojo::GetProxy(&loader, factory_.associated_group()), 2, 1, request, + client.CreateRemoteAssociatedPtrInfo(factory_.associated_group())); client.RunUntilComplete(); rdh_.SetDelegate(nullptr); @@ -260,6 +266,160 @@ TEST_P(URLLoaderFactoryImplTest, ShouldNotRequestURL) { EXPECT_EQ(net::ERR_ABORTED, client.completion_status().error_code); } +TEST_P(URLLoaderFactoryImplTest, DownloadToFile) { + constexpr int32_t kRoutingId = 1; + constexpr int32_t kRequestId = 2; + + mojom::URLLoaderAssociatedPtr loader; + base::FilePath root; + PathService::Get(DIR_TEST_DATA, &root); + net::URLRequestMockHTTPJob::AddUrlHandlers(root, + BrowserThread::GetBlockingPool()); + + ResourceRequest request; + TestURLLoaderClient client; + request.url = net::URLRequestMockHTTPJob::GetMockUrl("hello.html"); + request.method = "GET"; + request.resource_type = RESOURCE_TYPE_XHR; + request.download_to_file = true; + request.request_initiator = url::Origin(); + factory_->CreateLoaderAndStart( + mojo::GetProxy(&loader, factory_.associated_group()), kRoutingId, + kRequestId, request, + client.CreateRemoteAssociatedPtrInfo(factory_.associated_group())); + ASSERT_FALSE(client.has_received_response()); + ASSERT_FALSE(client.has_data_downloaded()); + ASSERT_FALSE(client.has_received_completion()); + + client.RunUntilResponseReceived(); + + net::URLRequest* url_request = + rdh_.GetURLRequest(GlobalRequestID(kChildId, kRequestId)); + ASSERT_TRUE(url_request); + ResourceRequestInfoImpl* request_info = + ResourceRequestInfoImpl::ForRequest(url_request); + ASSERT_TRUE(request_info); + EXPECT_EQ(kChildId, request_info->GetChildID()); + EXPECT_EQ(kRoutingId, request_info->GetRouteID()); + EXPECT_EQ(kRequestId, request_info->GetRequestID()); + + ASSERT_FALSE(client.has_received_completion()); + + client.RunUntilComplete(); + ASSERT_TRUE(client.has_data_downloaded()); + ASSERT_TRUE(client.has_received_completion()); + + EXPECT_EQ(200, client.response_head().headers->response_code()); + std::string content_type; + client.response_head().headers->GetNormalizedHeader("content-type", + &content_type); + EXPECT_EQ("text/html", content_type); + EXPECT_EQ(0, client.completion_status().error_code); + + std::string contents; + base::ReadFileToString(client.response_head().download_file_path, &contents); + + EXPECT_EQ(static_cast<int64_t>(contents.size()), + client.download_data_length()); + EXPECT_EQ(static_cast<int64_t>(contents.size()), + client.encoded_download_data_length()); + + std::string expected; + base::ReadFileToString( + root.Append(base::FilePath(FILE_PATH_LITERAL("hello.html"))), &expected); + EXPECT_EQ(expected, contents); +} + +TEST_P(URLLoaderFactoryImplTest, DownloadToFileFailure) { + constexpr int32_t kRoutingId = 1; + constexpr int32_t kRequestId = 2; + + mojom::URLLoaderAssociatedPtr loader; + base::FilePath root; + PathService::Get(DIR_TEST_DATA, &root); + net::URLRequestSlowDownloadJob::AddUrlHandler(); + + ResourceRequest request; + TestURLLoaderClient client; + request.url = GURL(net::URLRequestSlowDownloadJob::kKnownSizeUrl); + request.method = "GET"; + request.resource_type = RESOURCE_TYPE_XHR; + request.download_to_file = true; + request.request_initiator = url::Origin(); + factory_->CreateLoaderAndStart( + mojo::GetProxy(&loader, factory_.associated_group()), kRoutingId, + kRequestId, request, + client.CreateRemoteAssociatedPtrInfo(factory_.associated_group())); + ASSERT_FALSE(client.has_received_response()); + ASSERT_FALSE(client.has_data_downloaded()); + ASSERT_FALSE(client.has_received_completion()); + + client.RunUntilResponseReceived(); + + net::URLRequest* url_request = + rdh_.GetURLRequest(GlobalRequestID(kChildId, kRequestId)); + ASSERT_TRUE(url_request); + ResourceRequestInfoImpl* request_info = + ResourceRequestInfoImpl::ForRequest(url_request); + ASSERT_TRUE(request_info); + EXPECT_EQ(kChildId, request_info->GetChildID()); + EXPECT_EQ(kRoutingId, request_info->GetRouteID()); + EXPECT_EQ(kRequestId, request_info->GetRequestID()); + + ASSERT_FALSE(client.has_received_completion()); + + client.RunUntilDataDownloaded(); + ASSERT_TRUE(client.has_data_downloaded()); + ASSERT_FALSE(client.has_received_completion()); + EXPECT_LT(0, client.download_data_length()); + EXPECT_GE( + static_cast<int64_t>(net::URLRequestSlowDownloadJob::kFirstDownloadSize), + client.download_data_length()); + EXPECT_LT(0, client.encoded_download_data_length()); + EXPECT_GE( + static_cast<int64_t>(net::URLRequestSlowDownloadJob::kFirstDownloadSize), + client.encoded_download_data_length()); + + url_request->Cancel(); + client.RunUntilComplete(); + + ASSERT_TRUE(client.has_received_completion()); + + EXPECT_EQ(200, client.response_head().headers->response_code()); + EXPECT_EQ(net::ERR_ABORTED, client.completion_status().error_code); +} + +// Removing the loader in the remote side will cancel the request. +TEST_P(URLLoaderFactoryImplTest, CancelFromRenderer) { + constexpr int32_t kRoutingId = 81; + constexpr int32_t kRequestId = 28; + NavigationResourceThrottle::set_ui_checks_always_succeed_for_testing(true); + mojom::URLLoaderAssociatedPtr loader; + base::FilePath root; + PathService::Get(DIR_TEST_DATA, &root); + net::URLRequestFailedJob::AddUrlHandler(); + ResourceRequest request; + TestURLLoaderClient client; + // Assume the file contents is small enough to be stored in the data pipe. + request.url = net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_IO_PENDING); + request.method = "GET"; + request.is_main_frame = true; + factory_->CreateLoaderAndStart( + mojo::GetProxy(&loader, factory_.associated_group()), kRoutingId, + kRequestId, request, + client.CreateRemoteAssociatedPtrInfo(factory_.associated_group())); + + base::RunLoop().RunUntilIdle(); + ASSERT_TRUE(rdh_.GetURLRequest(GlobalRequestID(kChildId, kRequestId))); + ASSERT_FALSE(client.has_received_response()); + ASSERT_FALSE(client.response_body().is_valid()); + ASSERT_FALSE(client.has_received_completion()); + + loader = nullptr; + base::RunLoop().RunUntilIdle(); + ASSERT_FALSE(rdh_.GetURLRequest(GlobalRequestID(kChildId, kRequestId))); +} + INSTANTIATE_TEST_CASE_P(URLLoaderFactoryImplTest, URLLoaderFactoryImplTest, ::testing::Values(128, 32 * 1024)); |