OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/android/offline_pages/offline_page_tab_helper.h" | |
6 | |
7 #include <memory> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/feature_list.h" | |
11 #include "base/files/file_path.h" | |
12 #include "base/memory/ptr_util.h" | |
13 #include "base/memory/weak_ptr.h" | |
14 #include "base/metrics/field_trial.h" | |
15 #include "base/run_loop.h" | |
16 #include "base/strings/string16.h" | |
17 #include "base/strings/string_number_conversions.h" | |
18 #include "base/strings/utf_string_conversions.h" | |
19 #include "base/test/histogram_tester.h" | |
20 #include "base/test/simple_test_clock.h" | |
21 #include "base/time/time.h" | |
22 #include "chrome/browser/android/offline_pages/offline_page_model_factory.h" | |
23 #include "chrome/browser/android/offline_pages/offline_page_utils.h" | |
24 #include "chrome/browser/android/offline_pages/test_offline_page_model_builder.h
" | |
25 #include "chrome/browser/net/nqe/ui_network_quality_estimator_service.h" | |
26 #include "chrome/browser/net/nqe/ui_network_quality_estimator_service_factory.h" | |
27 #include "chrome/test/base/chrome_render_view_host_test_harness.h" | |
28 #include "chrome/test/base/testing_profile.h" | |
29 #include "components/offline_pages/client_namespace_constants.h" | |
30 #include "components/offline_pages/offline_page_feature.h" | |
31 #include "components/offline_pages/offline_page_item.h" | |
32 #include "components/offline_pages/offline_page_model.h" | |
33 #include "components/offline_pages/offline_page_test_archiver.h" | |
34 #include "components/offline_pages/offline_page_types.h" | |
35 #include "components/previews/core/previews_experiments.h" | |
36 #include "content/public/browser/navigation_entry.h" | |
37 #include "content/public/browser/web_contents.h" | |
38 #include "net/base/net_errors.h" | |
39 #include "net/base/network_change_notifier.h" | |
40 #include "net/nqe/network_quality_estimator.h" | |
41 #include "testing/gtest/include/gtest/gtest.h" | |
42 | |
43 namespace offline_pages { | |
44 | |
45 namespace { | |
46 | |
47 const GURL kTestPageUrl("http://test.org/page1"); | |
48 const ClientId kTestClientId = ClientId(kBookmarkNamespace, "1234"); | |
49 const int64_t kTestFileSize = 876543LL; | |
50 const base::string16 kTestTitle = base::UTF8ToUTF16("a title"); | |
51 const char kRedirectResultHistogram[] = "OfflinePages.RedirectResult"; | |
52 const int kTabId = 42; | |
53 | |
54 class TestNetworkChangeNotifier : public net::NetworkChangeNotifier { | |
55 public: | |
56 TestNetworkChangeNotifier() : online_(true) {} | |
57 | |
58 net::NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() | |
59 const override { | |
60 return online_ ? net::NetworkChangeNotifier::CONNECTION_UNKNOWN | |
61 : net::NetworkChangeNotifier::CONNECTION_NONE; | |
62 } | |
63 | |
64 void set_online(bool online) { online_ = online; } | |
65 | |
66 private: | |
67 bool online_; | |
68 | |
69 DISALLOW_COPY_AND_ASSIGN(TestNetworkChangeNotifier); | |
70 }; | |
71 | |
72 class TestDelegate : public OfflinePageTabHelper::Delegate { | |
73 public: | |
74 TestDelegate(bool has_tab_android, | |
75 int tab_id, | |
76 base::SimpleTestClock* clock) | |
77 : clock_(clock), has_tab_android_(has_tab_android), tab_id_(tab_id) {} | |
78 ~TestDelegate() override {} | |
79 | |
80 // offline_pages::OfflinePageTabHelper::Delegate implementation: | |
81 bool GetTabId(content::WebContents* web_contents, | |
82 int* tab_id) const override { | |
83 if (has_tab_android_) | |
84 *tab_id = tab_id_; | |
85 return has_tab_android_; | |
86 } | |
87 | |
88 base::Time Now() const override { return clock_->Now(); } | |
89 | |
90 private: | |
91 base::SimpleTestClock* clock_; | |
92 bool has_tab_android_; | |
93 int tab_id_; | |
94 }; | |
95 | |
96 } // namespace | |
97 | |
98 class OfflinePageTabHelperTest : | |
99 public ChromeRenderViewHostTestHarness, | |
100 public OfflinePageTestArchiver::Observer, | |
101 public base::SupportsWeakPtr<OfflinePageTabHelperTest> { | |
102 public: | |
103 OfflinePageTabHelperTest() | |
104 : network_change_notifier_(new TestNetworkChangeNotifier()), | |
105 clock_(new base::SimpleTestClock) {} | |
106 ~OfflinePageTabHelperTest() override {} | |
107 | |
108 void SetUp() override; | |
109 void TearDown() override; | |
110 | |
111 void RunUntilIdle(); | |
112 void SimulateHasNetworkConnectivity(bool has_connectivity); | |
113 void StartLoad(const GURL& url); | |
114 void FailLoad(const GURL& url); | |
115 std::unique_ptr<OfflinePageTestArchiver> BuildArchiver( | |
116 const GURL& url, | |
117 const base::FilePath& file_name); | |
118 void OnSavePageDone(SavePageResult result, int64_t offline_id); | |
119 | |
120 OfflinePageTabHelper* offline_page_tab_helper() const { | |
121 return offline_page_tab_helper_; | |
122 } | |
123 | |
124 const GURL& online_url() const { return offline_page_item_->url; } | |
125 GURL offline_url() const { return offline_page_item_->GetOfflineURL(); } | |
126 int64_t offline_id() const { return offline_page_item_->offline_id; } | |
127 | |
128 const base::HistogramTester& histograms() const { return histogram_tester_; } | |
129 | |
130 base::SimpleTestClock* clock() { return clock_.get(); } | |
131 | |
132 private: | |
133 // OfflinePageTestArchiver::Observer implementation: | |
134 void SetLastPathCreatedByArchiver(const base::FilePath& file_path) override; | |
135 | |
136 void OnGetPageByOfflineIdDone(const OfflinePageItem* result); | |
137 | |
138 std::unique_ptr<TestNetworkChangeNotifier> network_change_notifier_; | |
139 OfflinePageTabHelper* offline_page_tab_helper_; // Not owned. | |
140 | |
141 std::unique_ptr<OfflinePageItem> offline_page_item_; | |
142 | |
143 base::HistogramTester histogram_tester_; | |
144 | |
145 std::unique_ptr<base::SimpleTestClock> clock_; | |
146 | |
147 DISALLOW_COPY_AND_ASSIGN(OfflinePageTabHelperTest); | |
148 }; | |
149 | |
150 void OfflinePageTabHelperTest::SetUp() { | |
151 // Enables offline pages feature. | |
152 // TODO(jianli): Remove this once the feature is completely enabled. | |
153 base::FeatureList::ClearInstanceForTesting(); | |
154 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); | |
155 feature_list->InitializeFromCommandLine( | |
156 offline_pages::kOfflineBookmarksFeature.name, ""); | |
157 base::FeatureList::SetInstance(std::move(feature_list)); | |
158 | |
159 // Creates a test web contents. | |
160 content::RenderViewHostTestHarness::SetUp(); | |
161 OfflinePageTabHelper::CreateForWebContents(web_contents()); | |
162 offline_page_tab_helper_ = | |
163 OfflinePageTabHelper::FromWebContents(web_contents()); | |
164 offline_page_tab_helper_->SetDelegateForTesting( | |
165 base::MakeUnique<TestDelegate>(true, kTabId, clock_.get())); | |
166 | |
167 // Sets up the factory for testing. | |
168 OfflinePageModelFactory::GetInstance()->SetTestingFactoryAndUse( | |
169 browser_context(), BuildTestOfflinePageModel); | |
170 RunUntilIdle(); | |
171 | |
172 // Saves an offline page. | |
173 OfflinePageModel* model = | |
174 OfflinePageModelFactory::GetForBrowserContext(browser_context()); | |
175 std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver( | |
176 kTestPageUrl, base::FilePath(FILE_PATH_LITERAL("page1.mhtml")))); | |
177 model->SavePage( | |
178 kTestPageUrl, kTestClientId, 0l, std::move(archiver), | |
179 base::Bind(&OfflinePageTabHelperTest::OnSavePageDone, AsWeakPtr())); | |
180 RunUntilIdle(); | |
181 } | |
182 | |
183 void OfflinePageTabHelperTest::TearDown() { | |
184 content::RenderViewHostTestHarness::TearDown(); | |
185 } | |
186 | |
187 void OfflinePageTabHelperTest::RunUntilIdle() { | |
188 base::RunLoop().RunUntilIdle(); | |
189 } | |
190 | |
191 void OfflinePageTabHelperTest::SimulateHasNetworkConnectivity(bool online) { | |
192 network_change_notifier_->set_online(online); | |
193 } | |
194 | |
195 void OfflinePageTabHelperTest::StartLoad(const GURL& url) { | |
196 controller().LoadURL(url, content::Referrer(), ui::PAGE_TRANSITION_TYPED, | |
197 std::string()); | |
198 content::RenderFrameHostTester::For(main_rfh())-> | |
199 SimulateNavigationStart(url); | |
200 } | |
201 | |
202 void OfflinePageTabHelperTest::FailLoad(const GURL& url) { | |
203 content::RenderFrameHostTester::For(main_rfh())->SimulateNavigationStart(url); | |
204 // Set up the error code for the failed navigation. | |
205 content::RenderFrameHostTester::For(main_rfh())-> | |
206 SimulateNavigationError(url, net::ERR_INTERNET_DISCONNECTED); | |
207 content::RenderFrameHostTester::For(main_rfh())-> | |
208 SimulateNavigationErrorPageCommit(); | |
209 // Gives a chance to run delayed task to do redirection. | |
210 RunUntilIdle(); | |
211 } | |
212 | |
213 std::unique_ptr<OfflinePageTestArchiver> | |
214 OfflinePageTabHelperTest::BuildArchiver(const GURL& url, | |
215 const base::FilePath& file_name) { | |
216 std::unique_ptr<OfflinePageTestArchiver> archiver(new OfflinePageTestArchiver( | |
217 this, url, OfflinePageArchiver::ArchiverResult::SUCCESSFULLY_CREATED, | |
218 kTestTitle, kTestFileSize, base::ThreadTaskRunnerHandle::Get())); | |
219 archiver->set_filename(file_name); | |
220 return archiver; | |
221 } | |
222 | |
223 void OfflinePageTabHelperTest::OnSavePageDone(SavePageResult result, | |
224 int64_t offline_id) { | |
225 OfflinePageModel* model = | |
226 OfflinePageModelFactory::GetForBrowserContext(browser_context()); | |
227 model->GetPageByOfflineId(offline_id, | |
228 base::Bind(&OfflinePageTabHelperTest::OnGetPageByOfflineIdDone, | |
229 AsWeakPtr())); | |
230 } | |
231 | |
232 void OfflinePageTabHelperTest::SetLastPathCreatedByArchiver( | |
233 const base::FilePath& file_path) {} | |
234 | |
235 void OfflinePageTabHelperTest::OnGetPageByOfflineIdDone( | |
236 const OfflinePageItem* result) { | |
237 DCHECK(result); | |
238 offline_page_item_.reset(new OfflinePageItem(*result)); | |
239 } | |
240 | |
241 TEST_F(OfflinePageTabHelperTest, SwitchToOnlineFromOfflineOnNetwork) { | |
242 SimulateHasNetworkConnectivity(true); | |
243 | |
244 StartLoad(offline_url()); | |
245 // Gives a chance to run delayed task to do redirection. | |
246 RunUntilIdle(); | |
247 // Redirection will be done immediately on navigation start. | |
248 EXPECT_EQ(online_url(), controller().GetPendingEntry()->GetURL()); | |
249 histograms().ExpectUniqueSample( | |
250 kRedirectResultHistogram, | |
251 static_cast<int>(OfflinePageTabHelper::RedirectResult:: | |
252 REDIRECTED_ON_CONNECTED_NETWORK), | |
253 1); | |
254 } | |
255 | |
256 TEST_F(OfflinePageTabHelperTest, SwitchToOfflineFromOnlineOnNoNetwork) { | |
257 SimulateHasNetworkConnectivity(false); | |
258 | |
259 StartLoad(online_url()); | |
260 // Gives a chance to run delayed task to do redirection. | |
261 RunUntilIdle(); | |
262 // Redirection will be done immediately on navigation start. | |
263 EXPECT_EQ(offline_url(), controller().GetPendingEntry()->GetURL()); | |
264 histograms().ExpectUniqueSample( | |
265 kRedirectResultHistogram, | |
266 static_cast<int>(OfflinePageTabHelper::RedirectResult:: | |
267 REDIRECTED_ON_DISCONNECTED_NETWORK), | |
268 1); | |
269 } | |
270 | |
271 TEST_F(OfflinePageTabHelperTest, TestCurrentOfflinePage) { | |
272 SimulateHasNetworkConnectivity(false); | |
273 | |
274 StartLoad(online_url()); | |
275 // Gives a chance to run delayed task to do redirection. | |
276 RunUntilIdle(); | |
277 | |
278 const OfflinePageItem* item = | |
279 OfflinePageUtils::GetOfflinePageFromWebContents(web_contents()); | |
280 EXPECT_EQ(offline_url(), item->GetOfflineURL()); | |
281 EXPECT_EQ(online_url(), item->url); | |
282 | |
283 SimulateHasNetworkConnectivity(true); | |
284 StartLoad(offline_url()); | |
285 RunUntilIdle(); | |
286 item = OfflinePageUtils::GetOfflinePageFromWebContents(web_contents()); | |
287 EXPECT_EQ(nullptr, item); | |
288 } | |
289 | |
290 TEST_F(OfflinePageTabHelperTest, SwitchToOfflineFromOnlineOnError) { | |
291 SimulateHasNetworkConnectivity(true); | |
292 | |
293 StartLoad(online_url()); | |
294 RunUntilIdle(); | |
295 EXPECT_EQ(online_url(), controller().GetPendingEntry()->GetURL()); | |
296 | |
297 // Redirection will be done immediately on navigation end with error. | |
298 FailLoad(online_url()); | |
299 EXPECT_EQ(offline_url(), controller().GetPendingEntry()->GetURL()); | |
300 | |
301 histograms().ExpectUniqueSample( | |
302 kRedirectResultHistogram, | |
303 static_cast<int>(OfflinePageTabHelper::RedirectResult:: | |
304 REDIRECTED_ON_FLAKY_NETWORK), | |
305 1); | |
306 } | |
307 | |
308 TEST_F(OfflinePageTabHelperTest, NewNavigationCancelsPendingRedirects) { | |
309 SimulateHasNetworkConnectivity(false); | |
310 | |
311 StartLoad(online_url()); | |
312 const GURL unsaved_url("http://test.org/page2"); | |
313 | |
314 // We should have a pending task that will do the redirect. | |
315 ASSERT_TRUE(offline_page_tab_helper()->weak_ptr_factory_.HasWeakPtrs()); | |
316 ASSERT_EQ(online_url(), controller().GetPendingEntry()->GetURL()); | |
317 | |
318 // Should cancel pending tasks for previous URL. | |
319 StartLoad(unsaved_url); | |
320 | |
321 // Gives a chance to run delayed task to do redirection. | |
322 RunUntilIdle(); | |
323 | |
324 // Redirection should be cancelled so we should still navigate to | |
325 // |unsaved_url|. | |
326 EXPECT_EQ(unsaved_url, controller().GetPendingEntry()->GetURL()); | |
327 | |
328 // Should report attempt of redirect, but the page not found. | |
329 histograms().ExpectUniqueSample( | |
330 kRedirectResultHistogram, | |
331 static_cast<int>(OfflinePageTabHelper::RedirectResult:: | |
332 PAGE_NOT_FOUND_ON_DISCONNECTED_NETWORK), | |
333 1); | |
334 } | |
335 | |
336 // This test saves 3 pages (one in setup and 2 in test). The most appropriate | |
337 // test is related to |kTabId|, as it is saved in the latest moment and can be | |
338 // used in the current tab. | |
339 TEST_F(OfflinePageTabHelperTest, SelectBestPageForCurrentTab) { | |
340 // Saves an offline page. | |
341 OfflinePageModel* model = | |
342 OfflinePageModelFactory::GetForBrowserContext(browser_context()); | |
343 std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver( | |
344 kTestPageUrl, base::FilePath(FILE_PATH_LITERAL("page2.mhtml")))); | |
345 | |
346 // We expect this copy to be used later. | |
347 ClientId client_id(kLastNNamespace, base::IntToString(kTabId)); | |
348 model->SavePage( | |
349 kTestPageUrl, client_id, 0l, std::move(archiver), | |
350 base::Bind(&OfflinePageTabHelperTest::OnSavePageDone, AsWeakPtr())); | |
351 RunUntilIdle(); | |
352 const int64_t expected_offline_id = offline_id(); | |
353 const GURL expected_offline_url = offline_url(); | |
354 | |
355 archiver = BuildArchiver(kTestPageUrl, | |
356 base::FilePath(FILE_PATH_LITERAL("page3.html"))); | |
357 client_id.id = "39"; | |
358 model->SavePage( | |
359 kTestPageUrl, client_id, 0l, std::move(archiver), | |
360 base::Bind(&OfflinePageTabHelperTest::OnSavePageDone, AsWeakPtr())); | |
361 RunUntilIdle(); | |
362 | |
363 SimulateHasNetworkConnectivity(false); | |
364 StartLoad(kTestPageUrl); | |
365 // Gives a chance to run delayed task to do redirection. | |
366 RunUntilIdle(); | |
367 | |
368 const OfflinePageItem* item = | |
369 OfflinePageUtils::GetOfflinePageFromWebContents(web_contents()); | |
370 EXPECT_EQ(expected_offline_id, item->offline_id); | |
371 EXPECT_EQ(expected_offline_url, item->GetOfflineURL()); | |
372 EXPECT_EQ(kLastNNamespace, item->client_id.name_space); | |
373 EXPECT_EQ(base::IntToString(kTabId), item->client_id.id); | |
374 EXPECT_FALSE(offline_page_tab_helper()->is_offline_preview()); | |
375 } | |
376 | |
377 TEST_F(OfflinePageTabHelperTest, PageFor2GSlow) { | |
378 SimulateHasNetworkConnectivity(true); | |
379 TestingProfile* test_profile = profile(); | |
380 UINetworkQualityEstimatorService* nqe_service = | |
381 UINetworkQualityEstimatorServiceFactory::GetForProfile(test_profile); | |
382 nqe_service->SetEffectiveConnectionTypeForTesting( | |
383 net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G); | |
384 | |
385 clock()->SetNow(base::Time::Now()); | |
386 | |
387 StartLoad(kTestPageUrl); | |
388 // Gives a chance to run delayed task to do redirection. | |
389 RunUntilIdle(); | |
390 | |
391 // This is not included in the field trial, so it should not cause a redirect. | |
392 const OfflinePageItem* item = | |
393 OfflinePageUtils::GetOfflinePageFromWebContents(web_contents()); | |
394 EXPECT_FALSE(item); | |
395 | |
396 base::FieldTrialList field_trial_list(nullptr); | |
397 ASSERT_TRUE(previews::EnableOfflinePreviewsForTesting()); | |
398 | |
399 StartLoad(kTestPageUrl); | |
400 // Gives a chance to run delayed task to do redirection. | |
401 RunUntilIdle(); | |
402 | |
403 // This page should be fresh enough to cause a redirect. | |
404 item = OfflinePageUtils::GetOfflinePageFromWebContents(web_contents()); | |
405 EXPECT_EQ(offline_url(), item->GetOfflineURL()); | |
406 EXPECT_EQ(online_url(), item->url); | |
407 | |
408 EXPECT_TRUE(offline_page_tab_helper()->is_offline_preview()); | |
409 | |
410 clock()->Advance(base::TimeDelta::FromDays(8)); | |
411 StartLoad(kTestPageUrl); | |
412 // Gives a chance to run delayed task to do redirection. | |
413 RunUntilIdle(); | |
414 | |
415 // This page should not be fresh enough to cause a redirect. | |
416 item = OfflinePageUtils::GetOfflinePageFromWebContents(web_contents()); | |
417 EXPECT_EQ(nullptr, item); | |
418 EXPECT_FALSE(offline_page_tab_helper()->is_offline_preview()); | |
419 } | |
420 | |
421 // This test saves another copy of page from Async Loading namespace | |
422 // and verifies it is redirected to it (as it is more recent). | |
423 TEST_F(OfflinePageTabHelperTest, SwitchToOfflineAsyncLoadedPageOnNoNetwork) { | |
424 // Saves an offline page. | |
425 OfflinePageModel* model = | |
426 OfflinePageModelFactory::GetForBrowserContext(browser_context()); | |
427 std::unique_ptr<OfflinePageTestArchiver> archiver(BuildArchiver( | |
428 kTestPageUrl, | |
429 base::FilePath(FILE_PATH_LITERAL("AsyncLoadedPage.mhtml")))); | |
430 | |
431 // We expect this Async Loading Namespace copy to be used. | |
432 ClientId client_id(kAsyncNamespace, base::IntToString(kTabId)); | |
433 model->SavePage( | |
434 kTestPageUrl, client_id, 0l, std::move(archiver), | |
435 base::Bind(&OfflinePageTabHelperTest::OnSavePageDone, AsWeakPtr())); | |
436 RunUntilIdle(); | |
437 const int64_t expected_offline_id = offline_id(); | |
438 const GURL expected_offline_url = offline_url(); | |
439 | |
440 SimulateHasNetworkConnectivity(false); | |
441 StartLoad(kTestPageUrl); | |
442 // Gives a chance to run delayed task to do redirection. | |
443 RunUntilIdle(); | |
444 | |
445 const OfflinePageItem* item = | |
446 OfflinePageUtils::GetOfflinePageFromWebContents(web_contents()); | |
447 EXPECT_EQ(expected_offline_id, item->offline_id); | |
448 EXPECT_EQ(expected_offline_url, item->GetOfflineURL()); | |
449 EXPECT_EQ(kAsyncNamespace, item->client_id.name_space); | |
450 EXPECT_FALSE(offline_page_tab_helper()->is_offline_preview()); | |
451 } | |
452 | |
453 } // namespace offline_pages | |
OLD | NEW |