summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-20 02:53:08 +0000
committerrdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-20 02:53:08 +0000
commit7cf7ccb7750a8bb104fe9eb6fce3312871346237 (patch)
tree9e9be9db372821412f2b3be1d52939f6f4a82827
parentffc5e7758b4accca486b767b84c6158520aa39e7 (diff)
downloadchromium_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
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--content/browser/loader/global_routing_id.h42
-rw-r--r--content/browser/loader/offline_policy.cc73
-rw-r--r--content/browser/loader/offline_policy.h56
-rw-r--r--content/browser/loader/offline_policy_unittest.cc96
-rw-r--r--content/browser/loader/resource_dispatcher_host_impl.cc85
-rw-r--r--content/browser/loader/resource_dispatcher_host_impl.h11
-rw-r--r--content/browser/loader/resource_request_info_impl.cc5
-rw-r--r--content/browser/loader/resource_request_info_impl.h2
-rw-r--r--content/content_browser.gypi3
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/public/common/content_switches.cc3
-rw-r--r--content/public/common/content_switches.h1
-rw-r--r--net/http/http_cache_transaction.cc1
-rw-r--r--net/http/http_cache_unittest.cc46
-rw-r--r--net/http/http_network_layer_unittest.cc81
-rw-r--r--net/http/http_network_transaction.cc1
-rw-r--r--net/http/http_response_info.cc3
-rw-r--r--net/http/http_response_info.h4
-rw-r--r--net/http/http_transaction_unittest.cc1
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())