diff options
-rw-r--r-- | chrome/browser/android/offline_pages/offline_page_tab_helper.cc | 117 | ||||
-rw-r--r-- | chrome/browser/android/offline_pages/offline_page_tab_helper.h | 54 | ||||
-rw-r--r-- | chrome/browser/android/offline_pages/offline_page_tab_helper_unittest.cc | 195 | ||||
-rw-r--r-- | chrome/browser/ui/tab_helpers.cc | 2 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 1 | ||||
-rw-r--r-- | tools/metrics/histograms/histograms.xml | 18 |
7 files changed, 389 insertions, 0 deletions
diff --git a/chrome/browser/android/offline_pages/offline_page_tab_helper.cc b/chrome/browser/android/offline_pages/offline_page_tab_helper.cc new file mode 100644 index 0000000..c0e6e3b --- /dev/null +++ b/chrome/browser/android/offline_pages/offline_page_tab_helper.cc @@ -0,0 +1,117 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/android/offline_pages/offline_page_tab_helper.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/thread_task_runner_handle.h" +#include "chrome/browser/android/offline_pages/offline_page_utils.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" +#include "net/base/net_errors.h" +#include "net/base/network_change_notifier.h" + +DEFINE_WEB_CONTENTS_USER_DATA_KEY(offline_pages::OfflinePageTabHelper); + +namespace offline_pages { + +OfflinePageTabHelper::OfflinePageTabHelper(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), + weak_ptr_factory_(this) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); +} + +OfflinePageTabHelper::~OfflinePageTabHelper() {} + +void OfflinePageTabHelper::DidStartNavigation( + content::NavigationHandle* navigation_handle) { + GURL last_redirect_from_url_copy = last_redirect_from_url_; + last_redirect_from_url_ = GURL(); + + // Skips non-main frame. + if (!navigation_handle->IsInMainFrame()) + return; + + // Redirecting to online version will only take effect when there is network + // connection. + if (net::NetworkChangeNotifier::IsOffline()) + return; + + // Skips if not loading an offline copy of saved page. + GURL online_url = offline_pages::OfflinePageUtils::GetOnlineURLForOfflineURL( + web_contents()->GetBrowserContext(), navigation_handle->GetURL()); + if (!online_url.is_valid()) + return; + + // Avoids looping between online and offline redirections. + if (last_redirect_from_url_copy == online_url) + return; + last_redirect_from_url_ = navigation_handle->GetURL(); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&OfflinePageTabHelper::RedirectFromOfflineToOnline, + weak_ptr_factory_.GetWeakPtr(), + online_url)); +} + +void OfflinePageTabHelper::DidFailProvisionalLoad( + content::RenderFrameHost* render_frame_host, + const GURL& validated_url, + int error_code, + const base::string16& error_description, + bool was_ignored_by_handler) { + GURL last_redirect_from_url_copy = last_redirect_from_url_; + last_redirect_from_url_ = GURL(); + + // Skips non-main frame or load failure other than no network. + if (error_code != net::ERR_INTERNET_DISCONNECTED || + render_frame_host->GetParent() != nullptr) { + return; + } + + // Redirecting to offline version will only take effect when there is no + // network connection. + if (!net::NetworkChangeNotifier::IsOffline()) + return; + + // Skips if not loading an online version of saved page. + GURL offline_url = offline_pages::OfflinePageUtils::GetOfflineURLForOnlineURL( + web_contents()->GetBrowserContext(), validated_url); + if (!offline_url.is_valid()) + return; + + // Avoids looping between online and offline redirections. + if (last_redirect_from_url_copy == offline_url) + return; + last_redirect_from_url_ = validated_url; + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&OfflinePageTabHelper::RedirectFromOnlineToOffline, + weak_ptr_factory_.GetWeakPtr(), + offline_url)); +} + +void OfflinePageTabHelper::RedirectFromOfflineToOnline(const GURL& online_url) { + UMA_HISTOGRAM_COUNTS("OfflinePages.RedirectToOnlineCount", 1); + content::NavigationController::LoadURLParams load_params(online_url); + load_params.transition_type = ui::PAGE_TRANSITION_CLIENT_REDIRECT; + web_contents()->GetController().LoadURLWithParams(load_params); +} + +void OfflinePageTabHelper::RedirectFromOnlineToOffline( + const GURL& offline_url) { + UMA_HISTOGRAM_COUNTS("OfflinePages.RedirectToOfflineCount", 1); + content::NavigationController::LoadURLParams load_params(offline_url); + load_params.transition_type = ui::PAGE_TRANSITION_CLIENT_REDIRECT; + web_contents()->GetController().LoadURLWithParams(load_params); +} + +} // namespace offline_pages diff --git a/chrome/browser/android/offline_pages/offline_page_tab_helper.h b/chrome/browser/android/offline_pages/offline_page_tab_helper.h new file mode 100644 index 0000000..2339197 --- /dev/null +++ b/chrome/browser/android/offline_pages/offline_page_tab_helper.h @@ -0,0 +1,54 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ANDROID_OFFLINE_PAGES_OFFLINE_PAGE_TAB_HELPER_H_ +#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_OFFLINE_PAGE_TAB_HELPER_H_ + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" +#include "url/gurl.h" + +namespace content { +class WebContents; +} + +namespace offline_pages { + +// Per-tab class to manage switch between online version and offline version. +class OfflinePageTabHelper : + public content::WebContentsObserver, + public content::WebContentsUserData<OfflinePageTabHelper> { + public: + ~OfflinePageTabHelper() override; + + private: + friend class content::WebContentsUserData<OfflinePageTabHelper>; + friend class OfflinePageTabHelperTest; + + explicit OfflinePageTabHelper(content::WebContents* web_contents); + + // Overridden from content::WebContentsObserver: + void DidStartNavigation( + content::NavigationHandle* navigation_handle) override; + void DidFailProvisionalLoad( + content::RenderFrameHost* render_frame_host, + const GURL& validated_url, + int error_code, + const base::string16& error_description, + bool was_ignored_by_handler) override; + + void RedirectFromOfflineToOnline(const GURL& online_url); + void RedirectFromOnlineToOffline(const GURL& offline_url); + + GURL last_redirect_from_url_; + base::WeakPtrFactory<OfflinePageTabHelper> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(OfflinePageTabHelper); +}; + +} // namespace offline_pages + +#endif // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_OFFLINE_PAGE_TAB_HELPER_H_ diff --git a/chrome/browser/android/offline_pages/offline_page_tab_helper_unittest.cc b/chrome/browser/android/offline_pages/offline_page_tab_helper_unittest.cc new file mode 100644 index 0000000..3501f0e --- /dev/null +++ b/chrome/browser/android/offline_pages/offline_page_tab_helper_unittest.cc @@ -0,0 +1,195 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/run_loop.h" +#include "chrome/browser/android/offline_pages/offline_page_model_factory.h" +#include "chrome/browser/android/offline_pages/offline_page_tab_helper.h" +#include "chrome/browser/android/offline_pages/test_offline_page_model_builder.h" +#include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "components/offline_pages/offline_page_item.h" +#include "components/offline_pages/offline_page_model.h" +#include "components/offline_pages/offline_page_switches.h" +#include "components/offline_pages/offline_page_test_archiver.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" +#include "net/base/net_errors.h" +#include "net/base/network_change_notifier.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace offline_pages { + +namespace { + +const GURL kTestPageUrl("http://test.org/page1"); +const int64_t kTestPageBookmarkId = 1234; +const int64_t kTestFileSize = 876543LL; + +class TestNetworkChangeNotifier : public net::NetworkChangeNotifier { + public: + TestNetworkChangeNotifier() : online_(true) {} + + net::NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() + const override { + return online_ ? net::NetworkChangeNotifier::CONNECTION_UNKNOWN + : net::NetworkChangeNotifier::CONNECTION_NONE; + } + + void set_online(bool online) { online_ = online; } + + private: + bool online_; + + DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeNotifier); +}; + +} // namespace + +class OfflinePageTabHelperTest : + public ChromeRenderViewHostTestHarness, + public OfflinePageTestArchiver::Observer, + public base::SupportsWeakPtr<OfflinePageTabHelperTest> { + public: + OfflinePageTabHelperTest() + : network_change_notifier_(new TestNetworkChangeNotifier()) {} + ~OfflinePageTabHelperTest() override {} + + void SetUp() override; + void TearDown() override; + + void RunUntilIdle(); + void SimulateHasNetworkConnectivity(bool has_connectivity); + void StartLoad(const GURL& url); + void CommitLoad(const GURL& url); + void FailLoad(const GURL& url); + + OfflinePageTabHelper* offline_page_tab_helper() const { + return offline_page_tab_helper_; + } + + private: + // OfflinePageTestArchiver::Observer implementation: + void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override; + + scoped_ptr<OfflinePageTestArchiver> BuildArchiver( + const GURL& url, + const base::FilePath& file_name); + void OnSavePageDone(OfflinePageModel::SavePageResult result); + + scoped_ptr<TestNetworkChangeNotifier> network_change_notifier_; + OfflinePageTabHelper* offline_page_tab_helper_; // Not owned. + + DISALLOW_COPY_AND_ASSIGN(OfflinePageTabHelperTest); +}; + +void OfflinePageTabHelperTest::SetUp() { + // Creates a test web contents. + content::RenderViewHostTestHarness::SetUp(); + OfflinePageTabHelper::CreateForWebContents(web_contents()); + offline_page_tab_helper_ = + OfflinePageTabHelper::FromWebContents(web_contents()); + + // Enables offline pages feature. + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableOfflinePages); + + // Sets up the factory for testing. + OfflinePageModelFactory::GetInstance()->SetTestingFactoryAndUse( + browser_context(), BuildTestOfflinePageModel); + RunUntilIdle(); + + // Saves an offline page. + OfflinePageModel* model = + OfflinePageModelFactory::GetForBrowserContext(browser_context()); + scoped_ptr<OfflinePageTestArchiver> archiver(BuildArchiver( + kTestPageUrl, base::FilePath(FILE_PATH_LITERAL("page1.mhtml")))); + model->SavePage( + kTestPageUrl, kTestPageBookmarkId, std::move(archiver), + base::Bind(&OfflinePageTabHelperTest::OnSavePageDone, AsWeakPtr())); + RunUntilIdle(); +} + +void OfflinePageTabHelperTest::TearDown() { + content::RenderViewHostTestHarness::TearDown(); +} + +void OfflinePageTabHelperTest::RunUntilIdle() { + base::RunLoop().RunUntilIdle(); +} + +void OfflinePageTabHelperTest::SimulateHasNetworkConnectivity(bool online) { + network_change_notifier_->set_online(online); +} + +void OfflinePageTabHelperTest::StartLoad(const GURL& url) { + controller().LoadURL(url, content::Referrer(), ui::PAGE_TRANSITION_TYPED, + std::string()); +} + +void OfflinePageTabHelperTest::CommitLoad(const GURL& url) { + int entry_id = controller().GetPendingEntry()->GetUniqueID(); + content::RenderFrameHostTester::For(main_rfh())->SendNavigate( + 0, entry_id, true, url); +} + +void OfflinePageTabHelperTest::FailLoad(const GURL& url) { + content::RenderFrameHostTester::For(main_rfh())->SimulateNavigationError( + url, net::ERR_INTERNET_DISCONNECTED); + // Gives a chance to run delayed task to do redirection. + RunUntilIdle(); +} + +void OfflinePageTabHelperTest::SetLastPathCreatedByArchiver( + const base::FilePath& file_path) { +} + +scoped_ptr<OfflinePageTestArchiver> OfflinePageTabHelperTest::BuildArchiver( + const GURL& url, + const base::FilePath& file_name) { + scoped_ptr<OfflinePageTestArchiver> archiver(new OfflinePageTestArchiver( + this, url, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED, + kTestFileSize, base::ThreadTaskRunnerHandle::Get())); + archiver->set_filename(file_name); + return archiver; +} + +void OfflinePageTabHelperTest::OnSavePageDone( + OfflinePageModel::SavePageResult result) { +} + +TEST_F(OfflinePageTabHelperTest, SwitchToOnlineFromOffline) { + SimulateHasNetworkConnectivity(true); + + OfflinePageModel* model = + OfflinePageModelFactory::GetForBrowserContext(browser_context()); + const OfflinePageItem* page = model->GetPageByBookmarkId(kTestPageBookmarkId); + GURL offline_url = page->GetOfflineURL(); + GURL online_url = page->url; + + StartLoad(offline_url); + CommitLoad(online_url); + EXPECT_EQ(online_url, controller().GetLastCommittedEntry()->GetURL()); +} + +TEST_F(OfflinePageTabHelperTest, SwitchToOfflineFromOnline) { + SimulateHasNetworkConnectivity(false); + + OfflinePageModel* model = + OfflinePageModelFactory::GetForBrowserContext(browser_context()); + const OfflinePageItem* page = model->GetPageByBookmarkId(kTestPageBookmarkId); + GURL offline_url = page->GetOfflineURL(); + GURL online_url = page->url; + + StartLoad(online_url); + EXPECT_EQ(online_url, controller().GetPendingEntry()->GetURL()); + + FailLoad(online_url); + EXPECT_EQ(offline_url, controller().GetPendingEntry()->GetURL()); +} + +} // namespace offline_pages diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc index 97a1586..906defa 100644 --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc @@ -53,6 +53,7 @@ #if BUILDFLAG(ANDROID_JAVA_UI) #include "chrome/browser/android/data_usage/data_use_tab_helper.h" +#include "chrome/browser/android/offline_pages/offline_page_tab_helper.h" #include "chrome/browser/android/voice_search_tab_helper.h" #include "chrome/browser/android/webapps/single_tab_mode_tab_helper.h" #include "chrome/browser/ui/android/context_menu_helper.h" @@ -179,6 +180,7 @@ void TabHelpers::AttachTabHelpers(WebContents* web_contents) { #if BUILDFLAG(ANDROID_JAVA_UI) ContextMenuHelper::CreateForWebContents(web_contents); DataUseTabHelper::CreateForWebContents(web_contents); + offline_pages::OfflinePageTabHelper::CreateForWebContents(web_contents); SingleTabModeTabHelper::CreateForWebContents(web_contents); ViewAndroidHelper::CreateForWebContents(web_contents); VoiceSearchTabHelper::CreateForWebContents(web_contents); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index b39aaee..ec0ffa5 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1498,6 +1498,8 @@ 'browser/android/offline_pages/offline_page_mhtml_archiver.h', 'browser/android/offline_pages/offline_page_model_factory.cc', 'browser/android/offline_pages/offline_page_model_factory.h', + 'browser/android/offline_pages/offline_page_tab_helper.cc', + 'browser/android/offline_pages/offline_page_tab_helper.h', 'browser/android/offline_pages/offline_page_utils.cc', 'browser/android/offline_pages/offline_page_utils.h', ], diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 14ad8a7..4d98dc9 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1643,6 +1643,7 @@ # Sources for Offline pages. For now only for Android. 'chrome_unit_tests_offline_pages_sources': [ 'browser/android/offline_pages/offline_page_mhtml_archiver_unittest.cc', + 'browser/android/offline_pages/offline_page_tab_helper_unittest.cc', 'browser/android/offline_pages/offline_page_utils_unittest.cc', 'browser/android/offline_pages/test_offline_page_model_builder.cc', 'browser/android/offline_pages/test_offline_page_model_builder.h', diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 30d96cf..b4817c6 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -32224,6 +32224,24 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. <summary>Size of the saved copy of an offline page.</summary> </histogram> +<histogram name="OfflinePages.RedirectToOfflineCount" units="count"> + <owner>jianli@chromium.org</owner> + <summary> + Number of times an offline copy was loaded instead when the user is trying + to load the online version of a saved page and there is no network + connection. + </summary> +</histogram> + +<histogram name="OfflinePages.RedirectToOnlineCount" units="count"> + <owner>jianli@chromium.org</owner> + <summary> + Number of times an online version was loaded instead when the user is trying + to load the offline copy of a saved page and there is network connection. + connection. + </summary> +</histogram> + <histogram name="OfflinePages.SavedPageCount" units="pages"> <owner>jianli@chromium.org</owner> <summary> |