Index: chrome/browser/android/offline_pages/offline_page_request_job.cc |
diff --git a/chrome/browser/android/offline_pages/offline_page_request_job.cc b/chrome/browser/android/offline_pages/offline_page_request_job.cc |
index 886ab76848337dbf2d8b5570c8de32ab997b43eb..e4b065e1b9dd30d55d457828dfd4d3075b72adcb 100644 |
--- a/chrome/browser/android/offline_pages/offline_page_request_job.cc |
+++ b/chrome/browser/android/offline_pages/offline_page_request_job.cc |
@@ -10,6 +10,7 @@ |
#include "base/files/file_path.h" |
#include "base/logging.h" |
#include "base/metrics/histogram_macros.h" |
+#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_tokenizer.h" |
#include "base/strings/string_util.h" |
#include "base/threading/thread_task_runner_handle.h" |
@@ -17,7 +18,6 @@ |
#include "chrome/browser/android/offline_pages/offline_page_tab_helper.h" |
#include "chrome/browser/android/offline_pages/offline_page_utils.h" |
#include "chrome/browser/browser_process.h" |
-#include "chrome/browser/profiles/profile.h" |
#include "chrome/browser/profiles/profile_manager.h" |
#include "components/offline_pages/offline_page_model.h" |
#include "components/previews/previews_experiments.h" |
@@ -33,9 +33,12 @@ |
namespace offline_pages { |
-const char kLoadingOfflinePageHeader[] = "X-Chrome-offline"; |
-const char kLoadingOfflinePageReason[] = "reason="; |
-const char kLoadingOfflinePageDueToNetError[] = "error"; |
+const char kOfflinePageHeader[] = "X-Chrome-offline"; |
+const char kOfflinePageHeaderReasonKey[] = "reason"; |
+const char kOfflinePageHeaderReasonValueDueToNetError[] = "error"; |
+const char kOfflinePageHeaderReasonValueFromDownload[] = "download"; |
+const char kOfflinePageHeaderPersistKey[] = "persist"; |
+const char kOfflinePageHeaderIDKey[] = "id"; |
namespace { |
@@ -113,27 +116,68 @@ class DefaultDelegate : public OfflinePageRequestJob::Delegate { |
DISALLOW_COPY_AND_ASSIGN(DefaultDelegate); |
}; |
-// Returns true if custom offline header is present. |
-// |reason| may be set with the reason to trigger the offline page loading. |
-bool ParseOfflineHeader(net::URLRequest* request, std::string* reason) { |
- std::string value; |
- if (!request->extra_request_headers().GetHeader(kLoadingOfflinePageHeader, |
- &value)) { |
- return false; |
- } |
+// Used to parse the extra request header string that defines offline page |
+// loading behaviors. |
+class OfflinePageHeader { |
+ public: |
+ enum class Reason { |
+ NET_ERROR, |
+ DOWNLOAD, |
+ UNKNOWN |
+ }; |
+ |
+ explicit OfflinePageHeader(const std::string& header_string); |
+ ~OfflinePageHeader() {} |
+ |
+ bool successfully_parsed() const { return successfully_parsed_; } |
+ bool need_to_persist() const { return need_to_persist_; } |
+ Reason reason() const { return reason_; } |
+ const std::string& id() const { return id_; } |
+ |
+ private: |
+ // True if the header is present and parsed successfully. |
+ bool successfully_parsed_; |
+ // Flag to indicate if the header should be persisted across session restore. |
+ bool need_to_persist_; |
+ // Describes the reason to load offline page. |
+ Reason reason_; |
+ // The offline ID of the page to load. |
+ std::string id_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(OfflinePageHeader); |
+}; |
- // Currently we only support reason field. |
- base::StringTokenizer tokenizer(value, ", "); |
+OfflinePageHeader::OfflinePageHeader(const std::string& header_string) |
+ : successfully_parsed_(false), |
+ need_to_persist_(false), |
+ reason_(Reason::UNKNOWN) { |
+ // If the offline header is not present, treat it as not parsed successfully. |
+ if (header_string.empty()) |
+ return; |
+ |
+ base::StringTokenizer tokenizer(header_string, ", "); |
while (tokenizer.GetNext()) { |
- if (base::StartsWith(tokenizer.token(), |
- kLoadingOfflinePageReason, |
- base::CompareCase::INSENSITIVE_ASCII)) { |
- *reason = tokenizer.token().substr( |
- arraysize(kLoadingOfflinePageReason) - 1); |
- break; |
+ std::string pair = tokenizer.token(); |
+ std::size_t pos = pair.find('='); |
+ if (pos == std::string::npos) |
+ return; |
+ std::string key = base::ToLowerASCII(pair.substr(0, pos)); |
+ std::string value = base::ToLowerASCII(pair.substr(pos + 1)); |
+ if (key == kOfflinePageHeaderPersistKey) { |
+ need_to_persist_ = (value == "1"); |
+ } else if (key == kOfflinePageHeaderReasonKey) { |
+ if (value == kOfflinePageHeaderReasonValueDueToNetError) |
+ reason_ = Reason::NET_ERROR; |
+ else if (value == kOfflinePageHeaderReasonValueFromDownload) |
+ reason_ = Reason::DOWNLOAD; |
+ else |
+ reason_ = Reason::UNKNOWN; |
+ } else if (key == kOfflinePageHeaderIDKey) { |
+ id_ = value; |
} |
} |
- return true; |
+ |
+ successfully_parsed_ = true; |
} |
bool IsNetworkProhibitivelySlow(net::URLRequest* request) { |
@@ -158,13 +202,12 @@ bool IsNetworkProhibitivelySlow(net::URLRequest* request) { |
NetworkState GetNetworkState(net::URLRequest* request) { |
DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
- std::string reason; |
- bool has_offline_header = ParseOfflineHeader(request, &reason); |
- if (has_offline_header && |
- base::EqualsCaseInsensitiveASCII(reason, |
- kLoadingOfflinePageDueToNetError)) { |
+ std::string offline_header_string; |
+ request->extra_request_headers().GetHeader( |
+ kOfflinePageHeader, &offline_header_string); |
+ OfflinePageHeader offline_header(offline_header_string); |
+ if (offline_header.reason() == OfflinePageHeader::Reason::NET_ERROR) |
return NetworkState::FLAKY_NETWORK; |
- } |
if (net::NetworkChangeNotifier::IsOffline()) |
return NetworkState::DISCONNECTED_NETWORK; |
@@ -172,10 +215,11 @@ NetworkState GetNetworkState(net::URLRequest* request) { |
if (IsNetworkProhibitivelySlow(request)) |
return NetworkState::PROHIBITIVELY_SLOW_NETWORK; |
- // The presence of custom offline header will force to load an offline page |
- // even when network is connected. |
- return has_offline_header ? NetworkState::FORCE_OFFLINE_ON_CONNECTED_NETWORK |
- : NetworkState::CONNECTED_NETWORK; |
+ // If custom offline header is present, the offline page should be forced to |
+ // load even when the network is connected. |
+ return offline_header.successfully_parsed() |
+ ? NetworkState::FORCE_OFFLINE_ON_CONNECTED_NETWORK |
+ : NetworkState::CONNECTED_NETWORK; |
} |
OfflinePageRequestJob::AggregatedRequestResult |
@@ -241,6 +285,17 @@ void ReportRequestResult( |
RequestResultToAggregatedRequestResult(request_result, network_state)); |
} |
+OfflinePageModel* GetOfflinePageModel( |
+ content::ResourceRequestInfo::WebContentsGetter web_contents_getter) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ |
+ content::WebContents* web_contents = web_contents_getter.Run(); |
+ return web_contents ? |
+ OfflinePageModelFactory::GetForBrowserContext( |
+ web_contents->GetBrowserContext()) : |
+ nullptr; |
+} |
+ |
void NotifyOfflineFilePathOnIO(base::WeakPtr<OfflinePageRequestJob> job, |
const base::FilePath& offline_file_path) { |
DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
@@ -314,7 +369,7 @@ RequestResult AccessOfflineFile( |
} |
// Handles the result of finding an offline page. |
-void SelectPageForOnlineURLDone( |
+void SucceededToFindOfflinePage( |
NetworkState network_state, |
base::WeakPtr<OfflinePageRequestJob> job, |
content::ResourceRequestInfo::WebContentsGetter web_contents_getter, |
@@ -334,7 +389,7 @@ void SelectPageForOnlineURLDone( |
NotifyOfflineFilePathOnUI(job, offline_file_path); |
} |
-void FailedToSelectOfflinePage(base::WeakPtr<OfflinePageRequestJob> job) { |
+void FailedToFindOfflinePage(base::WeakPtr<OfflinePageRequestJob> job) { |
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
// Proceed with empty file path in order to notify the OfflinePageRequestJob |
@@ -344,46 +399,114 @@ void FailedToSelectOfflinePage(base::WeakPtr<OfflinePageRequestJob> job) { |
} |
// Tries to find the offline page to serve for |online_url|. |
-void SelectOfflinePage( |
+void SelectPageForOnlineURL( |
const GURL& online_url, |
NetworkState network_state, |
- void* profile_id, |
content::ResourceRequestInfo::WebContentsGetter web_contents_getter, |
OfflinePageRequestJob::Delegate::TabIdGetter tab_id_getter, |
base::WeakPtr<OfflinePageRequestJob> job) { |
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- // |profile_id| needs to be checked with ProfileManager::IsValidProfile |
- // before using it. |
- if (!g_browser_process->profile_manager()->IsValidProfile(profile_id)) { |
- FailedToSelectOfflinePage(job); |
- return; |
- } |
- Profile* profile = reinterpret_cast<Profile*>(profile_id); |
- |
content::WebContents* web_contents = web_contents_getter.Run(); |
if (!web_contents){ |
ReportRequestResult(RequestResult::NO_WEB_CONTENTS, network_state); |
- FailedToSelectOfflinePage(job); |
+ FailedToFindOfflinePage(job); |
return; |
} |
int tab_id; |
if (!tab_id_getter.Run(web_contents, &tab_id)) { |
ReportRequestResult(RequestResult::NO_TAB_ID, network_state); |
- FailedToSelectOfflinePage(job); |
+ FailedToFindOfflinePage(job); |
return; |
} |
OfflinePageUtils::SelectPageForOnlineURL( |
- profile, |
+ web_contents->GetBrowserContext(), |
online_url, |
tab_id, |
- base::Bind(&SelectPageForOnlineURLDone, |
+ base::Bind(&SucceededToFindOfflinePage, |
network_state, |
job, |
web_contents_getter)); |
} |
+void FindPageWithOfflineIDDone( |
+ const GURL& online_url, |
+ NetworkState network_state, |
+ content::ResourceRequestInfo::WebContentsGetter web_contents_getter, |
+ OfflinePageRequestJob::Delegate::TabIdGetter tab_id_getter, |
+ base::WeakPtr<OfflinePageRequestJob> job, |
+ const OfflinePageItem* offline_page) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ |
+ // If the found offline page does not has same URL as the request URL, fall |
+ // back to find the offline page based on the URL. |
+ if (!offline_page || offline_page->url != online_url) { |
+ SelectPageForOnlineURL( |
+ online_url, network_state, web_contents_getter, tab_id_getter, job); |
+ return; |
+ } |
+ |
+ SucceededToFindOfflinePage( |
+ network_state, job, web_contents_getter, offline_page); |
+} |
+ |
+// Tries to find an offline page associated with |offline_id|. |
+void FindPageWithOfflineID( |
+ const GURL& online_url, |
+ int64_t offline_id, |
+ NetworkState network_state, |
+ content::ResourceRequestInfo::WebContentsGetter web_contents_getter, |
+ OfflinePageRequestJob::Delegate::TabIdGetter tab_id_getter, |
+ base::WeakPtr<OfflinePageRequestJob> job) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ |
+ OfflinePageModel* offline_page_model = |
+ GetOfflinePageModel(web_contents_getter); |
+ if (!offline_page_model) { |
+ FailedToFindOfflinePage(job); |
+ return; |
+ } |
+ |
+ offline_page_model->GetPageByOfflineId( |
+ offline_id, |
+ base::Bind(&FindPageWithOfflineIDDone, |
+ online_url, |
+ network_state, |
+ web_contents_getter, |
+ tab_id_getter, |
+ job)); |
+} |
+ |
+// Tries to find the offline page to serve for |online_url|. |
+void SelectPage( |
+ const GURL& online_url, |
+ const std::string& offline_header_string, |
+ NetworkState network_state, |
+ content::ResourceRequestInfo::WebContentsGetter web_contents_getter, |
+ OfflinePageRequestJob::Delegate::TabIdGetter tab_id_getter, |
+ base::WeakPtr<OfflinePageRequestJob> job) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ |
+ OfflinePageHeader offline_header(offline_header_string); |
+ |
+ // If an offline ID is present in the offline header, try to load that |
+ // particular version. |
+ if (!offline_header.id().empty()) { |
+ // if the id string cannot be converted to int64 id, fall through to |
+ // select page via online URL. |
+ int64_t offline_id; |
+ if (base::StringToInt64(offline_header.id(), &offline_id)) { |
+ FindPageWithOfflineID(online_url, offline_id, network_state, |
+ web_contents_getter, tab_id_getter, job); |
+ return; |
+ } |
+ } |
+ |
+ SelectPageForOnlineURL(online_url, network_state, web_contents_getter, |
+ tab_id_getter, job); |
+} |
+ |
} // namespace |
// static |
@@ -396,7 +519,6 @@ void OfflinePageRequestJob::ReportAggregatedRequestResult( |
// static |
OfflinePageRequestJob* OfflinePageRequestJob::Create( |
- void* profile_id, |
net::URLRequest* request, |
net::NetworkDelegate* network_delegate) { |
const content::ResourceRequestInfo* resource_request_info = |
@@ -429,11 +551,10 @@ OfflinePageRequestJob* OfflinePageRequestJob::Create( |
request->SetUserData(&kUserDataKey, new OfflinePageRequestInfo()); |
} |
- return new OfflinePageRequestJob(profile_id, request, network_delegate); |
+ return new OfflinePageRequestJob(request, network_delegate); |
} |
OfflinePageRequestJob::OfflinePageRequestJob( |
- void* profile_id, |
net::URLRequest* request, |
net::NetworkDelegate* network_delegate) |
: net::URLRequestFileJob( |
@@ -443,7 +564,6 @@ OfflinePageRequestJob::OfflinePageRequestJob( |
content::BrowserThread::GetBlockingPool()-> |
GetTaskRunnerWithShutdownBehavior( |
base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), |
- profile_id_(profile_id), |
delegate_(new DefaultDelegate()), |
weak_ptr_factory_(this) { |
} |
@@ -464,13 +584,16 @@ void OfflinePageRequestJob::StartAsync() { |
return; |
} |
+ std::string offline_header_string; |
+ request()->extra_request_headers().GetHeader(kOfflinePageHeader, |
+ &offline_header_string); |
content::BrowserThread::PostTask( |
content::BrowserThread::UI, |
FROM_HERE, |
- base::Bind(&SelectOfflinePage, |
+ base::Bind(&SelectPage, |
request()->url(), |
+ offline_header_string, |
network_state, |
- profile_id_, |
delegate_->GetWebContentsGetter(request()), |
delegate_->GetTabIdGetter(), |
weak_ptr_factory_.GetWeakPtr())); |