diff options
author | rdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-20 02:53:08 +0000 |
---|---|---|
committer | rdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-20 02:53:08 +0000 |
commit | 7cf7ccb7750a8bb104fe9eb6fce3312871346237 (patch) | |
tree | 9e9be9db372821412f2b3be1d52939f6f4a82827 | |
parent | ffc5e7758b4accca486b767b84c6158520aa39e7 (diff) | |
download | chromium_src-7cf7ccb7750a8bb104fe9eb6fce3312871346237.zip chromium_src-7cf7ccb7750a8bb104fe9eb6fce3312871346237.tar.gz chromium_src-7cf7ccb7750a8bb104fe9eb6fce3312871346237.tar.bz2 |
Implement offline mode behind a flag.
BUG=2204
Review URL: https://chromiumcodereview.appspot.com/12886022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195374 0039d316-1c4b-4281-b951-d872f2087c98
21 files changed, 492 insertions, 36 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index b28eaf8..5adb11e 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -14917,6 +14917,12 @@ Some features may be unavailable. Please check that the profile exists and you <message name="IDS_FLAGS_ENABLE_GOOGLE_NOW_INTEGRATION_DESCRIPTION" desc="Description of about:flags option to turn on Google Now integration"> Enable Google Now notifications support. </message> + <message name="IDS_FLAGS_ENABLE_OFFLINE_MODE_NAME" desc="Name of the flag to make reads in offline mode come from the cache even if it's stale"> + Enable Offline Cache Mode + </message> + <message name="IDS_FLAGS_ENABLE_OFFLINE_MODE_DESCRIPTION" desc="Description of the flag to make reads in offline mode come from the cache even if it's stale"> + Reads from web resources when the source is unavailable will be satisfied from available stale cache entries. + </message> <message name="IDS_FLAGS_ENABLE_TRANSLATE_ALPHA_LANGUAGES_NAME" desc="Name of about:flag option to turn on Translate alpha languages support"> diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 097ba25..64fa234 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -1297,6 +1297,13 @@ const Experiment kExperiments[] = { kOsAndroid | kOsLinux | kOsCrOS, MULTI_VALUE_TYPE(kMaxTilesForInterestAreaChoices) }, + { + "enable-offline-mode", + IDS_FLAGS_ENABLE_OFFLINE_MODE_NAME, + IDS_FLAGS_ENABLE_OFFLINE_MODE_DESCRIPTION, + kOsAll, + SINGLE_VALUE_TYPE(switches::kEnableOfflineCacheAccess) + }, // TODO(sky): ifdef needed until focus sorted out in DesktopNativeWidgetAura. #if !defined(USE_AURA) { diff --git a/content/browser/loader/global_routing_id.h b/content/browser/loader/global_routing_id.h new file mode 100644 index 0000000..4715c5a --- /dev/null +++ b/content/browser/loader/global_routing_id.h @@ -0,0 +1,42 @@ +// 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_PUBLIC_BROWSER_GLOBAL_ROUTING_ID_H_ +#define CONTENT_PUBLIC_BROWSER_GLOBAL_ROUTING_ID_H_ + +namespace content { + +// Uniquely identifies the route from which a net::URLRequest comes. +struct GlobalRoutingID { + GlobalRoutingID() : child_id(-1), route_id(-1) { + } + + GlobalRoutingID(int child_id, int route_id) + : child_id(child_id), + route_id(route_id) { + } + + // The unique ID of the child process (different from OS's PID). + int child_id; + + // The route ID (unique for each URLRequest source). + int route_id; + + bool operator<(const GlobalRoutingID& other) const { + if (child_id == other.child_id) + return route_id < other.route_id; + return child_id < other.child_id; + } + bool operator==(const GlobalRoutingID& other) const { + return child_id == other.child_id && + route_id == other.route_id; + } + bool operator!=(const GlobalRoutingID& other) const { + return !(*this == other); + } +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_BROWSER_GLOBAL_ROUTING_ID_H_ diff --git a/content/browser/loader/offline_policy.cc b/content/browser/loader/offline_policy.cc new file mode 100644 index 0000000..bd904ee --- /dev/null +++ b/content/browser/loader/offline_policy.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2013 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/offline_policy.h" + +#include "base/command_line.h" +#include "content/public/common/content_switches.h" +#include "net/base/load_flags.h" +#include "net/http/http_response_info.h" +#include "net/url_request/url_request.h" + +namespace content { + +OfflinePolicy::OfflinePolicy() + : enabled_(CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableOfflineCacheAccess)), + state_(INIT) {} + +OfflinePolicy::~OfflinePolicy() {} + +int OfflinePolicy::GetAdditionalLoadFlags(int current_flags, + bool reset_state) { + + // Don't do anything if offline mode is disabled. + if (!enabled_) + return 0; + + if (reset_state) + state_ = INIT; + + // If a consumer has requested something contradictory, it wins; we + // don't modify the load flags. + if (current_flags & + (net::LOAD_BYPASS_CACHE | net::LOAD_PREFERRING_CACHE | + net::LOAD_ONLY_FROM_CACHE | net::LOAD_FROM_CACHE_IF_OFFLINE | + net::LOAD_DISABLE_CACHE)) { + return 0; + } + + switch(state_) { + case INIT: + return net::LOAD_FROM_CACHE_IF_OFFLINE; + case ONLINE: + return 0; + case OFFLINE: + return net::LOAD_ONLY_FROM_CACHE; + } + NOTREACHED(); + return 0; +} + +void OfflinePolicy::UpdateStateForCompletedRequest( + const net::HttpResponseInfo& response_info) { + // Don't do anything if offline mode is disabled. + if (!enabled_) + return; + + if (state_ != INIT) + // We've already made the decision for the rest of this set + // of navigations. + return; + + if (response_info.server_data_unavailable) { + state_ = OFFLINE; + } else if (response_info.network_accessed) { + // If we got the response from the network or validated it as part + // of this request, that means our connection to the host is working. + state_ = ONLINE; + } +} + +} // namespace content diff --git a/content/browser/loader/offline_policy.h b/content/browser/loader/offline_policy.h new file mode 100644 index 0000000..b1acfa9 --- /dev/null +++ b/content/browser/loader/offline_policy.h @@ -0,0 +1,56 @@ +// Copyright (c) 2013 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_OFFLINE_POLICY +#define CONTENT_BROWSER_LOADER_OFFLINE_POLICY + +#include <map> + +#include "base/basictypes.h" +#include "content/common/content_export.h" + +struct ResourceHostMsg_Request; + +namespace net { +class HttpResponseInfo; +class URLRequest; +} + +namespace content { + +// This class controls under what conditions resources will be fetched +// from cache even if stale rather than from the network. For example, +// one policy would be that if requests for a particular route (e.g. "tab") +// is unable to reach the server, other requests made with the same route +// can be loaded from cache without first requiring a network timeout. +// +// There is a single OfflinePolicy object per user navigation unit +// (generally a tab). +class CONTENT_EXPORT OfflinePolicy { + public: + OfflinePolicy(); + ~OfflinePolicy(); + + // Return any additional load flags to be ORed for a request from + // this route with the given |resource_type|. |reset_state| indicates + // that this request should reinitialized the internal state for this + // policy object (e.g. in the case of a main frame load). + int GetAdditionalLoadFlags(int current_flags, bool reset_state); + + // Incorporate online/offline information from a completed request. + void UpdateStateForCompletedRequest( + const net::HttpResponseInfo& response_info); + +private: + enum State { INIT, ONLINE, OFFLINE }; + + bool enabled_; + State state_; + + DISALLOW_COPY_AND_ASSIGN(OfflinePolicy); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_LOADER_OFFLINE_POLICY diff --git a/content/browser/loader/offline_policy_unittest.cc b/content/browser/loader/offline_policy_unittest.cc new file mode 100644 index 0000000..91256c6 --- /dev/null +++ b/content/browser/loader/offline_policy_unittest.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2013 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/offline_policy.h" + +#include "base/command_line.h" +#include "content/public/common/content_switches.h" +#include "net/base/load_flags.h" +#include "net/http/http_response_info.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/glue/resource_type.h" + +namespace content { + +class OfflinePolicyTest : public testing::Test { + protected: + virtual void SetUp() { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableOfflineCacheAccess); + policy_ = new OfflinePolicy; + } + + virtual void TearDown() { + delete policy_; + policy_ = NULL; + } + + OfflinePolicy* policy_; +}; + +// Confirm that the initial state of an offline object is to return +// LOAD_FROM_CACHE_IF_OFFLINE until it gets changed. +TEST_F(OfflinePolicyTest, InitialState) { + // Two loads without any reset, no UpdateStateForCompletedRequest. + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, true)); + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, false)); +} + +// Completion without any network probing doesn't change result value. +TEST_F(OfflinePolicyTest, CompletedUncertain) { + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, true)); + net::HttpResponseInfo response_info; + policy_->UpdateStateForCompletedRequest(response_info); + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, false)); +} + +// Completion with a failed network probe changes result value. +TEST_F(OfflinePolicyTest, CompletedNoNetwork) { + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, true)); + net::HttpResponseInfo response_info; + response_info.server_data_unavailable = true; + policy_->UpdateStateForCompletedRequest(response_info); + EXPECT_EQ(net::LOAD_ONLY_FROM_CACHE, + policy_->GetAdditionalLoadFlags(0, false)); +} + +// Completion with a successful network probe changes result value. +TEST_F(OfflinePolicyTest, CompletedNetwork) { + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, true)); + net::HttpResponseInfo response_info; + response_info.network_accessed = true; + policy_->UpdateStateForCompletedRequest(response_info); + EXPECT_EQ(0, policy_->GetAdditionalLoadFlags(0, false)); +} + +// A new navigation resets a state change. +TEST_F(OfflinePolicyTest, NewNavigationReset) { + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, true)); + net::HttpResponseInfo response_info; + response_info.network_accessed = true; + policy_->UpdateStateForCompletedRequest(response_info); + EXPECT_EQ(0, policy_->GetAdditionalLoadFlags(0, false)); + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, true)); + EXPECT_EQ(net::LOAD_FROM_CACHE_IF_OFFLINE, + policy_->GetAdditionalLoadFlags(0, false)); +} + +// Cache related flags inhibit the returning of any special flags. +TEST_F(OfflinePolicyTest, ConsumerFlagOverride) { + EXPECT_EQ(0, policy_->GetAdditionalLoadFlags(net::LOAD_BYPASS_CACHE, true)); + net::HttpResponseInfo response_info; + response_info.server_data_unavailable = true; + policy_->UpdateStateForCompletedRequest(response_info); + EXPECT_EQ(0, policy_->GetAdditionalLoadFlags(net::LOAD_BYPASS_CACHE, false)); +} + +} diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 7eee77b..f3a441b 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc @@ -231,8 +231,10 @@ net::Error CallbackAndReturn( return net_error; } -int BuildLoadFlagsForRequest(const ResourceHostMsg_Request& request_data, - int child_id, bool is_sync_load) { +int BuildLoadFlagsForRequest( + const ResourceHostMsg_Request& request_data, + int child_id, + bool is_sync_load) { int load_flags = request_data.load_flags; // Although EV status is irrelevant to sub-frames and sub-resources, we have @@ -480,6 +482,13 @@ net::Error ResourceDispatcherHostImpl::BeginDownload( extra_load_flags |= net::LOAD_DISABLE_CACHE; } request->set_load_flags(request->load_flags() | extra_load_flags); + + // No need to get offline load flags for downloads, but make sure + // we have an OfflinePolicy to receive request completions. + GlobalRoutingID id(child_id, route_id); + if (!offline_policy_map_[id]) + offline_policy_map_[id] = new OfflinePolicy(); + // Check if the renderer is permitted to request the requested URL. if (!ChildProcessSecurityPolicyImpl::GetInstance()-> CanRequestURL(child_id, url)) { @@ -702,6 +711,12 @@ void ResourceDispatcherHostImpl::DidReceiveRedirect(ResourceLoader* loader, void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) { ResourceRequestInfoImpl* info = loader->GetRequestInfo(); + // There should be an entry in the map created when we dispatched the + // request. + GlobalRoutingID routing_id(info->GetGlobalRoutingID()); + DCHECK(offline_policy_map_.end() != offline_policy_map_.find(routing_id)); + offline_policy_map_[routing_id]-> + UpdateStateForCompletedRequest(loader->request()->response_info()); int render_process_id, render_view_id; if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id)) @@ -789,17 +804,17 @@ void ResourceDispatcherHostImpl::OnShutdown() { // Note that we have to do this in 2 passes as we cannot call // CancelBlockedRequestsForRoute while iterating over // blocked_loaders_map_, as it modifies it. - std::set<ProcessRouteIDs> ids; + std::set<GlobalRoutingID> ids; for (BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.begin(); iter != blocked_loaders_map_.end(); ++iter) { - std::pair<std::set<ProcessRouteIDs>::iterator, bool> result = + std::pair<std::set<GlobalRoutingID>::iterator, bool> result = ids.insert(iter->first); // We should not have duplicates. DCHECK(result.second); } - for (std::set<ProcessRouteIDs>::const_iterator iter = ids.begin(); + for (std::set<GlobalRoutingID>::const_iterator iter = ids.begin(); iter != ids.end(); ++iter) { - CancelBlockedRequestsForRoute(iter->first, iter->second); + CancelBlockedRequestsForRoute(iter->child_id, iter->route_id); } scheduler_.reset(); @@ -936,6 +951,12 @@ void ResourceDispatcherHostImpl::BeginRequest( int load_flags = BuildLoadFlagsForRequest(request_data, child_id, is_sync_load); + GlobalRoutingID id(child_id, route_id); + if (!offline_policy_map_[id]) + offline_policy_map_[id] = new OfflinePolicy(); + load_flags |= offline_policy_map_[id]->GetAdditionalLoadFlags( + load_flags, request_data.resource_type == ResourceType::MAIN_FRAME); + // Construct the request. scoped_ptr<net::URLRequest> new_request; net::URLRequest* request; @@ -1276,6 +1297,12 @@ void ResourceDispatcherHostImpl::BeginSaveFile( // future, maybe we can use a configuration to configure this behavior. request->set_load_flags(net::LOAD_PREFERRING_CACHE); + // No need to get offline load flags for save files, but make sure + // we have an OfflinePolicy to receive request completions. + GlobalRoutingID id(child_id, route_id); + if (!offline_policy_map_[id]) + offline_policy_map_[id] = new OfflinePolicy(); + // Since we're just saving some resources we need, disallow downloading. ResourceRequestInfoImpl* extra_info = CreateRequestInfo(child_id, route_id, false, context); @@ -1351,7 +1378,7 @@ void ResourceDispatcherHostImpl::CancelRequestsForRoute(int child_id, // Now deal with blocked requests if any. if (route_id != -1) { - if (blocked_loaders_map_.find(ProcessRouteIDs(child_id, route_id)) != + if (blocked_loaders_map_.find(GlobalRoutingID(child_id, route_id)) != blocked_loaders_map_.end()) { CancelBlockedRequestsForRoute(child_id, route_id); } @@ -1363,14 +1390,22 @@ void ResourceDispatcherHostImpl::CancelRequestsForRoute(int child_id, std::set<int> route_ids; for (BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.begin(); iter != blocked_loaders_map_.end(); ++iter) { - if (iter->first.first == child_id) - route_ids.insert(iter->first.second); + if (iter->first.child_id == child_id) + route_ids.insert(iter->first.route_id); } for (std::set<int>::const_iterator iter = route_ids.begin(); iter != route_ids.end(); ++iter) { CancelBlockedRequestsForRoute(child_id, *iter); } } + + // Cleanup the offline state for the route. + OfflineMap::iterator it = offline_policy_map_.find( + GlobalRoutingID(child_id, route_id)); + if (offline_policy_map_.end() != it) { + delete it->second; + offline_policy_map_.erase(it); + } } // Cancels the request and removes it from the list. @@ -1502,8 +1537,8 @@ void ResourceDispatcherHostImpl::BeginRequestInternal( linked_ptr<ResourceLoader> loader( new ResourceLoader(request.Pass(), handler.Pass(), this)); - ProcessRouteIDs pair_id(info->GetChildID(), info->GetRouteID()); - BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.find(pair_id); + GlobalRoutingID id(info->GetGlobalRoutingID()); + BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.find(id); if (iter != blocked_loaders_map_.end()) { // The request should be blocked. iter->second->push_back(loader); @@ -1565,8 +1600,8 @@ struct LoadInfo { uint64 upload_size; }; -// Map from ProcessID+ViewID pair to LoadState -typedef std::map<std::pair<int, int>, LoadInfo> LoadInfoMap; +// Map from ProcessID+RouteID pair to LoadState +typedef std::map<GlobalRoutingID, LoadInfo> LoadInfoMap; // Used to marshal calls to LoadStateChanged from the IO to UI threads. We do // them all as a single callback to avoid spamming the UI thread. @@ -1574,7 +1609,7 @@ void LoadInfoUpdateCallback(const LoadInfoMap& info_map) { LoadInfoMap::const_iterator i; for (i = info_map.begin(); i != info_map.end(); ++i) { RenderViewHostImpl* view = - RenderViewHostImpl::FromID(i->first.first, i->first.second); + RenderViewHostImpl::FromID(i->first.child_id, i->first.route_id); if (view) // The view could be gone at this point. view->LoadStateChanged(i->second.url, i->second.load_state, i->second.upload_position, @@ -1593,16 +1628,16 @@ void ResourceDispatcherHostImpl::UpdateLoadStates() { // Determine the largest upload size of all requests // in each View (good chance it's zero). - std::map<std::pair<int, int>, uint64> largest_upload_size; + std::map<GlobalRoutingID, uint64> largest_upload_size; for (i = pending_loaders_.begin(); i != pending_loaders_.end(); ++i) { net::URLRequest* request = i->second->request(); ResourceRequestInfoImpl* info = i->second->GetRequestInfo(); uint64 upload_size = request->GetUploadProgress().size(); if (request->GetLoadState().state != net::LOAD_STATE_SENDING_REQUEST) upload_size = 0; - std::pair<int, int> key(info->GetChildID(), info->GetRouteID()); - if (upload_size && largest_upload_size[key] < upload_size) - largest_upload_size[key] = upload_size; + GlobalRoutingID id(info->GetGlobalRoutingID()); + if (upload_size && largest_upload_size[id] < upload_size) + largest_upload_size[id] = upload_size; } for (i = pending_loaders_.begin(); i != pending_loaders_.end(); ++i) { @@ -1615,23 +1650,23 @@ void ResourceDispatcherHostImpl::UpdateLoadStates() { // progress ipc messages to the plugin process. i->second->ReportUploadProgress(); - std::pair<int, int> key(info->GetChildID(), info->GetRouteID()); + GlobalRoutingID id(info->GetGlobalRoutingID()); // If a request is uploading data, ignore all other requests so that the // upload progress takes priority for being shown in the status bar. - if (largest_upload_size.find(key) != largest_upload_size.end() && - progress.size() < largest_upload_size[key]) + if (largest_upload_size.find(id) != largest_upload_size.end() && + progress.size() < largest_upload_size[id]) continue; net::LoadStateWithParam to_insert = load_state; - LoadInfoMap::iterator existing = info_map.find(key); + LoadInfoMap::iterator existing = info_map.find(id); if (existing != info_map.end()) { to_insert = MoreInterestingLoadState(existing->second.load_state, load_state); if (to_insert.state == existing->second.load_state.state) continue; } - LoadInfo& load_info = info_map[key]; + LoadInfo& load_info = info_map[id]; load_info.url = request->url(); load_info.load_state = to_insert; load_info.upload_size = progress.size(); @@ -1649,7 +1684,7 @@ void ResourceDispatcherHostImpl::UpdateLoadStates() { void ResourceDispatcherHostImpl::BlockRequestsForRoute(int child_id, int route_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - ProcessRouteIDs key(child_id, route_id); + GlobalRoutingID key(child_id, route_id); DCHECK(blocked_loaders_map_.find(key) == blocked_loaders_map_.end()) << "BlockRequestsForRoute called multiple time for the same RVH"; blocked_loaders_map_[key] = new BlockedLoadersList(); @@ -1670,7 +1705,7 @@ void ResourceDispatcherHostImpl::ProcessBlockedRequestsForRoute( int route_id, bool cancel_requests) { BlockedLoadersMap::iterator iter = blocked_loaders_map_.find( - std::pair<int, int>(child_id, route_id)); + GlobalRoutingID(child_id, route_id)); if (iter == blocked_loaders_map_.end()) { // It's possible to reach here if the renderer crashed while an interstitial // page was showing. diff --git a/content/browser/loader/resource_dispatcher_host_impl.h b/content/browser/loader/resource_dispatcher_host_impl.h index 5dc36e6..b459ec8 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.h +++ b/content/browser/loader/resource_dispatcher_host_impl.h @@ -25,6 +25,8 @@ #include "base/time.h" #include "base/timer.h" #include "content/browser/download/download_resource_handler.h" +#include "content/browser/loader/global_routing_id.h" +#include "content/browser/loader/offline_policy.h" #include "content/browser/loader/render_view_host_tracker.h" #include "content/browser/loader/resource_loader.h" #include "content/browser/loader/resource_loader_delegate.h" @@ -32,6 +34,7 @@ #include "content/common/content_export.h" #include "content/public/browser/child_process_data.h" #include "content/public/browser/download_id.h" +#include "content/public/browser/global_request_id.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/resource_dispatcher_host.h" #include "ipc/ipc_message.h" @@ -60,7 +63,6 @@ class ResourceRequestInfoImpl; class SaveFileManager; class WebContentsImpl; struct DownloadSaveInfo; -struct GlobalRequestID; struct Referrer; class CONTENT_EXPORT ResourceDispatcherHostImpl @@ -409,8 +411,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl bool is_shutdown_; typedef std::vector<linked_ptr<ResourceLoader> > BlockedLoadersList; - typedef std::pair<int, int> ProcessRouteIDs; - typedef std::map<ProcessRouteIDs, BlockedLoadersList*> BlockedLoadersMap; + typedef std::map<GlobalRoutingID, BlockedLoadersList*> BlockedLoadersMap; BlockedLoadersMap blocked_loaders_map_; // Maps the child_ids to the approximate number of bytes @@ -452,6 +453,10 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl RenderViewHostTracker tracker_; // Lives on UI thread. + typedef std::map<GlobalRoutingID, OfflinePolicy*> OfflineMap; + + OfflineMap offline_policy_map_; + DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHostImpl); }; diff --git a/content/browser/loader/resource_request_info_impl.cc b/content/browser/loader/resource_request_info_impl.cc index 8039a12..09b82ca 100644 --- a/content/browser/loader/resource_request_info_impl.cc +++ b/content/browser/loader/resource_request_info_impl.cc @@ -4,6 +4,7 @@ #include "content/browser/loader/resource_request_info_impl.h" +#include "content/browser/loader/global_routing_id.h" #include "content/browser/worker_host/worker_service_impl.h" #include "content/common/net/url_request_user_data.h" #include "content/public/browser/global_request_id.h" @@ -221,6 +222,10 @@ GlobalRequestID ResourceRequestInfoImpl::GetGlobalRequestID() const { return GlobalRequestID(child_id_, request_id_); } +GlobalRoutingID ResourceRequestInfoImpl::GetGlobalRoutingID() const { + return GlobalRoutingID(child_id_, route_id_); +} + void ResourceRequestInfoImpl::set_requested_blob_data( webkit_blob::BlobData* data) { requested_blob_data_ = data; diff --git a/content/browser/loader/resource_request_info_impl.h b/content/browser/loader/resource_request_info_impl.h index 8b23b2a..1c663257 100644 --- a/content/browser/loader/resource_request_info_impl.h +++ b/content/browser/loader/resource_request_info_impl.h @@ -24,6 +24,7 @@ namespace content { class CrossSiteResourceHandler; class ResourceContext; struct GlobalRequestID; +struct GlobalRoutingID; // Holds the data ResourceDispatcherHost associates with each request. // Retrieve this data by calling ResourceDispatcherHost::InfoForRequest. @@ -82,6 +83,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, CONTENT_EXPORT void AssociateWithRequest(net::URLRequest* request); CONTENT_EXPORT GlobalRequestID GetGlobalRequestID() const; + GlobalRoutingID GetGlobalRoutingID() const; // CrossSiteResourceHandler for this request. May be null. CrossSiteResourceHandler* cross_site_handler() { diff --git a/content/content_browser.gypi b/content/content_browser.gypi index f238f9a..0bec54e 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -558,8 +558,11 @@ 'browser/loader/cross_site_resource_handler.h', 'browser/loader/doomed_resource_handler.cc', 'browser/loader/doomed_resource_handler.h', + 'browser/loader/global_routing_id.h', 'browser/loader/layered_resource_handler.cc', 'browser/loader/layered_resource_handler.h', + 'browser/loader/offline_policy.cc', + 'browser/loader/offline_policy.h', 'browser/loader/power_save_block_resource_throttle.cc', 'browser/loader/power_save_block_resource_throttle.h', 'browser/loader/redirect_to_file_resource_handler.cc', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index ae7f650..97f8f95 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -304,6 +304,7 @@ 'browser/in_process_webkit/webkit_thread_unittest.cc', 'browser/indexed_db/indexed_db_unittest.cc', 'browser/indexed_db/indexed_db_quota_client_unittest.cc', + 'browser/loader/offline_policy_unittest.cc', 'browser/loader/resource_buffer_unittest.cc', 'browser/loader/resource_dispatcher_host_unittest.cc', 'browser/loader/resource_loader_unittest.cc', diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index e50916b..b66ce99 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -339,6 +339,9 @@ const char kEnableMonitorProfile[] = "enable-monitor-profile"; const char kEnablePinch[] = "enable-pinch"; const char kDisablePinch[] = "disable-pinch"; +// Enables use of cache if offline, even if it's stale +const char kEnableOfflineCacheAccess[] = "enable-offline-cache-access"; + // Enable caching of pre-parsed JS script data. See http://crbug.com/32407. const char kEnablePreparsedJsCaching[] = "enable-preparsed-js-caching"; diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index 6501fb4..c1c75be 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -116,6 +116,7 @@ CONTENT_EXPORT extern const char kUseFakeDeviceForMediaStream[]; extern const char kEnableMonitorProfile[]; extern const char kEnableUserMediaScreenCapturing[]; extern const char kEnablePinch[]; +CONTENT_EXPORT extern const char kEnableOfflineCacheAccess[]; extern const char kDisablePinch[]; extern const char kEnablePreparsedJsCaching[]; CONTENT_EXPORT extern const char kEnablePrivilegedWebGLExtensions[]; diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index 6a3c6b2..d839483 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc @@ -1176,6 +1176,7 @@ int HttpCache::Transaction::DoUpdateCachedResponse() { response_.headers->Update(*new_response_->headers); response_.response_time = new_response_->response_time; response_.request_time = new_response_->request_time; + response_.network_accessed = new_response_->network_accessed; if (response_.headers->HasHeaderValue("cache-control", "no-store")) { if (!entry_->doomed) { diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc index b7fe187..fbdd4e6 100644 --- a/net/http/http_cache_unittest.cc +++ b/net/http/http_cache_unittest.cc @@ -878,6 +878,7 @@ TEST(HttpCache, SimpleGET_CacheOverride_Network) { EXPECT_EQ(2, cache.network_layer()->transaction_count()); EXPECT_FALSE(response_info.server_data_unavailable); + EXPECT_TRUE(response_info.network_accessed); RemoveMockTransaction(&transaction); } @@ -917,6 +918,7 @@ TEST(HttpCache, SimpleGET_CacheOverride_Offline) { ASSERT_TRUE(response_info); EXPECT_TRUE(response_info->server_data_unavailable); EXPECT_TRUE(response_info->was_cached); + EXPECT_FALSE(response_info->network_accessed); ReadAndVerifyTransaction(trans.get(), transaction); EXPECT_EQ(2, cache.network_layer()->transaction_count()); @@ -924,7 +926,7 @@ TEST(HttpCache, SimpleGET_CacheOverride_Offline) { } // Tests that LOAD_FROM_CACHE_IF_OFFLINE returns proper response on -// non-offline failure failure +// non-offline failure. TEST(HttpCache, SimpleGET_CacheOverride_NonOffline) { MockHttpCache cache; @@ -953,6 +955,43 @@ TEST(HttpCache, SimpleGET_CacheOverride_NonOffline) { RemoveMockTransaction(&transaction); } +// Confirm if we have an empty cache, a read is marked as network verified. +TEST(HttpCache, SimpleGET_NetworkAccessed_Network) { + MockHttpCache cache; + + // write to the cache + net::HttpResponseInfo response_info; + RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction, + &response_info); + + EXPECT_EQ(1, cache.network_layer()->transaction_count()); + EXPECT_EQ(0, cache.disk_cache()->open_count()); + EXPECT_EQ(1, cache.disk_cache()->create_count()); + EXPECT_TRUE(response_info.network_accessed); +} + +// Confirm if we have a fresh entry in cache, it isn't marked as +// network verified. +TEST(HttpCache, SimpleGET_NetworkAccessed_Cache) { + MockHttpCache cache; + + // Prime cache. + MockTransaction transaction(kSimpleGET_Transaction); + + RunTransactionTest(cache.http_cache(), transaction); + EXPECT_EQ(1, cache.network_layer()->transaction_count()); + EXPECT_EQ(1, cache.disk_cache()->create_count()); + + // Re-run transaction; make sure we don't mark the network as accessed. + net::HttpResponseInfo response_info; + RunTransactionTestWithResponseInfo(cache.http_cache(), transaction, + &response_info); + + EXPECT_EQ(1, cache.network_layer()->transaction_count()); + EXPECT_FALSE(response_info.server_data_unavailable); + EXPECT_FALSE(response_info.network_accessed); +} + TEST(HttpCache, SimpleGET_LoadBypassCache) { MockHttpCache cache; @@ -1044,11 +1083,14 @@ TEST(HttpCache, SimpleGET_LoadValidateCache) { MockTransaction transaction(kSimpleGET_Transaction); transaction.load_flags |= net::LOAD_VALIDATE_CACHE; - RunTransactionTest(cache.http_cache(), transaction); + net::HttpResponseInfo response_info; + RunTransactionTestWithResponseInfo(cache.http_cache(), transaction, + &response_info); EXPECT_EQ(2, cache.network_layer()->transaction_count()); EXPECT_EQ(1, cache.disk_cache()->open_count()); EXPECT_EQ(1, cache.disk_cache()->create_count()); + EXPECT_TRUE(response_info.network_accessed); } TEST(HttpCache, SimpleGET_LoadValidateCache_Implicit) { diff --git a/net/http/http_network_layer_unittest.cc b/net/http/http_network_layer_unittest.cc index ecd33e9..ad1885f 100644 --- a/net/http/http_network_layer_unittest.cc +++ b/net/http/http_network_layer_unittest.cc @@ -88,12 +88,12 @@ TEST_F(HttpNetworkLayerTest, GET) { }; MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n" - "User-Agent: Foo/1.0\r\n\r\n"), + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n" + "User-Agent: Foo/1.0\r\n\r\n"), }; StaticSocketDataProvider data(data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); + data_writes, arraysize(data_writes)); mock_socket_factory_.AddSocketDataProvider(&data); TestCompletionCallback callback; @@ -110,8 +110,7 @@ TEST_F(HttpNetworkLayerTest, GET) { EXPECT_EQ(OK, rv); rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); - if (rv == ERR_IO_PENDING) - rv = callback.WaitForResult(); + rv = callback.GetResult(rv); ASSERT_EQ(OK, rv); std::string contents; @@ -290,6 +289,76 @@ TEST_F(HttpNetworkLayerTest, ProxyBypassIgnoredOnDirectConnection) { ASSERT_EQ(0u, proxy_service_->proxy_retry_info().size()); } +TEST_F(HttpNetworkLayerTest, NetworkVerified) { + MockRead data_reads[] = { + MockRead("HTTP/1.0 200 OK\r\n\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + MockWrite data_writes[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n" + "User-Agent: Foo/1.0\r\n\r\n"), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), + data_writes, arraysize(data_writes)); + mock_socket_factory_.AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + HttpRequestInfo request_info; + request_info.url = GURL("http://www.google.com/"); + request_info.method = "GET"; + request_info.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, + "Foo/1.0"); + request_info.load_flags = LOAD_NORMAL; + + scoped_ptr<HttpTransaction> trans; + int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + EXPECT_EQ(OK, rv); + + rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); + ASSERT_EQ(OK, callback.GetResult(rv)); + + EXPECT_TRUE(trans->GetResponseInfo()->network_accessed); +} + +TEST_F(HttpNetworkLayerTest, NetworkUnVerified) { + MockRead data_reads[] = { + MockRead(ASYNC, ERR_CONNECTION_RESET), + }; + MockWrite data_writes[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n" + "User-Agent: Foo/1.0\r\n\r\n"), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), + data_writes, arraysize(data_writes)); + mock_socket_factory_.AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + HttpRequestInfo request_info; + request_info.url = GURL("http://www.google.com/"); + request_info.method = "GET"; + request_info.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, + "Foo/1.0"); + request_info.load_flags = LOAD_NORMAL; + + scoped_ptr<HttpTransaction> trans; + int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + EXPECT_EQ(OK, rv); + + rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); + ASSERT_EQ(ERR_CONNECTION_RESET, callback.GetResult(rv)); + + // If the response info is null, that means that any consumer won't + // see the network accessed bit set. + EXPECT_EQ(NULL, trans->GetResponseInfo()); +} + } // namespace } // namespace net diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index f3e95d5..791822b 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -826,6 +826,7 @@ int HttpNetworkTransaction::DoSendRequestComplete(int result) { send_end_time_ = base::TimeTicks::Now(); if (result < 0) return HandleIOError(result); + response_.network_accessed = true; next_state_ = STATE_READ_HEADERS; return OK; } diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc index a4d672d..57825e6 100644 --- a/net/http/http_response_info.cc +++ b/net/http/http_response_info.cc @@ -94,6 +94,7 @@ enum { HttpResponseInfo::HttpResponseInfo() : was_cached(false), server_data_unavailable(false), + network_accessed(false), was_fetched_via_spdy(false), was_npn_negotiated(false), was_fetched_via_proxy(false), @@ -104,6 +105,7 @@ HttpResponseInfo::HttpResponseInfo() HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs) : was_cached(rhs.was_cached), server_data_unavailable(rhs.server_data_unavailable), + network_accessed(rhs.network_accessed), was_fetched_via_spdy(rhs.was_fetched_via_spdy), was_npn_negotiated(rhs.was_npn_negotiated), was_fetched_via_proxy(rhs.was_fetched_via_proxy), @@ -127,6 +129,7 @@ HttpResponseInfo::~HttpResponseInfo() { HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) { was_cached = rhs.was_cached; server_data_unavailable = rhs.server_data_unavailable; + network_accessed = rhs.network_accessed; was_fetched_via_spdy = rhs.was_fetched_via_spdy; was_npn_negotiated = rhs.was_npn_negotiated; was_fetched_via_proxy = rhs.was_fetched_via_proxy; diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h index e0bb41a..de27825 100644 --- a/net/http/http_response_info.h +++ b/net/http/http_response_info.h @@ -64,6 +64,10 @@ class NET_EXPORT HttpResponseInfo { // was unable to contact the server. bool server_data_unavailable; + // True if the request accessed the network in the process of retrieving + // data. + bool network_accessed; + // True if the request was fetched over a SPDY channel. bool was_fetched_via_spdy; diff --git a/net/http/http_transaction_unittest.cc b/net/http/http_transaction_unittest.cc index ba2d3aa..333263d 100644 --- a/net/http/http_transaction_unittest.cc +++ b/net/http/http_transaction_unittest.cc @@ -264,6 +264,7 @@ int MockNetworkTransaction::Start(const net::HttpRequestInfo* request, response_.request_time = t->request_time; response_.was_cached = false; + response_.network_accessed = true; response_.response_time = base::Time::Now(); if (!t->response_time.is_null()) |