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..30b4bbca19bf2d32649e0f916097db1d2b0f83c4 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" |
@@ -33,9 +34,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 kOfflinePageHeaderPersistKey[] = "persist"; |
+const char kOfflinePageHeaderNamespaceKey[] = "namespace"; |
+const char kOfflinePageHeaderIDKey[] = "id"; |
namespace { |
@@ -113,27 +117,54 @@ 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; |
+class OfflinePageHeader { |
Dmitry Titov
2016/09/09 04:33:06
Nice to add a class for that.
Please add a class
jianli
2016/09/10 00:56:03
Done.
|
+ public: |
+ explicit OfflinePageHeader(const std::string& header_string); |
+ ~OfflinePageHeader() {} |
+ |
+ bool succeeded_to_parse() const { return succeeded_to_parse_; } |
Dmitry Titov
2016/09/09 04:33:06
Naming suggestion: successfully_parsed_
jianli
2016/09/10 00:56:03
Done.
|
+ bool need_to_persist() const { return need_to_persist_; } |
Dmitry Titov
2016/09/09 04:33:06
Shouldn't this always be 'true' ?
Again, if persis
jianli
2016/09/10 00:56:04
I think it should always be false because we shoul
|
+ const std::string& reason() const { return reason_; } |
Dmitry Titov
2016/09/09 04:33:06
this looks like enum that this class should expose
jianli
2016/09/10 00:56:03
Done.
|
+ const std::string& name_space() const { return name_space_; } |
+ const std::string& id() const { return id_; } |
+ |
+ private: |
+ bool succeeded_to_parse_; |
+ |
Dmitry Titov
2016/09/09 04:33:06
extra empty line
jianli
2016/09/10 00:56:03
Done.
|
+ bool need_to_persist_; |
+ std::string reason_; |
+ std::string name_space_; |
+ std::string id_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(OfflinePageHeader); |
+}; |
+ |
+OfflinePageHeader::OfflinePageHeader(const std::string& header_string) |
+ : succeeded_to_parse_(false), need_to_persist_(false) { |
+ if (header_string.empty()) { |
+ succeeded_to_parse_ = true; |
+ return; |
} |
- // Currently we only support reason field. |
- base::StringTokenizer tokenizer(value, ", "); |
+ 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 = pair.substr(0, pos); |
+ std::string value = pair.substr(pos + 1); |
+ if (key == kOfflinePageHeaderPersistKey) |
fgorski
2016/09/08 02:25:02
is there a place where you call toLower? Or should
jianli
2016/09/10 00:56:03
Done.
|
+ need_to_persist_ = (value == "1"); |
+ else if (key == kOfflinePageHeaderReasonKey) |
+ reason_ = value; |
+ else if (key == kOfflinePageHeaderNamespaceKey) |
+ name_space_ = value; |
+ else if (key == kOfflinePageHeaderIDKey) |
+ id_ = value; |
} |
- return true; |
+ |
+ succeeded_to_parse_ = true; |
Dmitry Titov
2016/09/09 04:33:06
Are there valid combinations and invalid combinati
jianli
2016/09/10 00:56:03
After I remove namespace field, all remaining 3 fi
|
} |
bool IsNetworkProhibitivelySlow(net::URLRequest* request) { |
@@ -158,11 +189,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 (base::EqualsCaseInsensitiveASCII( |
+ offline_header.reason(), kOfflinePageHeaderReasonValueDueToNetError)) { |
return NetworkState::FLAKY_NETWORK; |
} |
@@ -172,10 +204,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 and contains the reason field, the |
+ // offline page should be forced to load even when the network is connected. |
+ return offline_header.succeeded_to_parse() && !offline_header.reason().empty() |
+ ? NetworkState::FORCE_OFFLINE_ON_CONNECTED_NETWORK |
+ : NetworkState::CONNECTED_NETWORK; |
} |
OfflinePageRequestJob::AggregatedRequestResult |
@@ -241,6 +274,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 +358,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 +378,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 +388,174 @@ 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, |
fgorski
2016/09/08 02:25:02
nit: since we no longer have the concept of offlin
jianli
2016/09/10 00:56:04
Agreed. But I rather make another patch after land
|
+ 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 online URL as the request URL, |
fgorski
2016/09/08 02:25:02
nit: does not have the same URL as the online URL
jianli
2016/09/10 00:56:03
Done.
|
+ // fall back to find the offline page based on the online URL. |
+ if (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)); |
+} |
+ |
+void GetOfflineIdsForClientIdDone( |
+ const GURL& online_url, |
+ NetworkState network_state, |
+ content::ResourceRequestInfo::WebContentsGetter web_contents_getter, |
+ OfflinePageRequestJob::Delegate::TabIdGetter tab_id_getter, |
+ base::WeakPtr<OfflinePageRequestJob> job, |
+ const MultipleOfflineIdResult& offline_ids) { |
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
+ |
+ if (offline_ids.empty()) { |
+ FailedToFindOfflinePage(job); |
+ return; |
+ } |
+ |
+ DCHECK_EQ(1u, offline_ids.size()); |
+ FindPageWithOfflineID(online_url, |
+ offline_ids[0], |
+ network_state, |
+ web_contents_getter, |
+ tab_id_getter, |
+ job); |
+} |
+ |
+// Tries to find an offline page associated with |client_id|. |
+void FindPageWithClientID( |
+ const GURL& online_url, |
+ const ClientId& client_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->GetOfflineIdsForClientId( |
+ client_id, |
+ base::Bind(&GetOfflineIdsForClientIdDone, |
+ 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 id, either offline ID or client ID, is present in the offline header, |
Dmitry Titov
2016/09/09 04:33:06
This logic is too complicated. It's hard to recall
jianli
2016/09/10 00:56:04
Per discussion, we only support passing offline id
|
+ // try to load that particular version. |
+ if (!offline_header.id().empty()) { |
+ // If namespace is not set in offline header, the id given in the offline |
+ // header should be used as offline ID. |
+ if (offline_header.name_space().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; |
+ } |
+ } else { |
+ // Otherwise, try to find the offline page based on the client ID. |
+ ClientId client_id(offline_header.name_space(), offline_header.id()); |
+ FindPageWithClientID(online_url, client_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 |
@@ -464,13 +636,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())); |