blob: 567d825f76ae1dab6735131a26b8592de1a2ccb7 [file] [log] [blame]
pkotwiczfd773552015-03-16 00:29:141// Copyright 2015 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/favicon/favicon_tab_helper.h"
6
7#include "base/memory/weak_ptr.h"
8#include "base/run_loop.h"
sdefresneb0befeec2015-04-08 09:21:569#include "base/scoped_observer.h"
pkotwiczfd773552015-03-16 00:29:1410#include "chrome/app/chrome_command_ids.h"
11#include "chrome/browser/chrome_notification_types.h"
pkotwiczfd773552015-03-16 00:29:1412#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/browser_commands.h"
14#include "chrome/browser/ui/tabs/tab_strip_model.h"
15#include "chrome/test/base/in_process_browser_test.h"
16#include "chrome/test/base/ui_test_utils.h"
sdefresneb0befeec2015-04-08 09:21:5617#include "components/favicon/core/favicon_driver_observer.h"
sdefresnecbacfd72015-03-20 12:11:3218#include "components/favicon/core/favicon_handler.h"
pkotwiczfd773552015-03-16 00:29:1419#include "content/public/browser/notification_observer.h"
20#include "content/public/browser/notification_registrar.h"
21#include "content/public/browser/notification_types.h"
22#include "content/public/browser/resource_dispatcher_host.h"
23#include "content/public/browser/resource_dispatcher_host_delegate.h"
24#include "net/base/load_flags.h"
25#include "net/test/spawned_test_server/spawned_test_server.h"
26#include "net/url_request/url_request.h"
27#include "url/url_constants.h"
28
29namespace {
30
31// Tracks whether the URL passed to the constructor is requested and whether
32// the request bypasses the cache.
33class TestResourceDispatcherHostDelegate
34 : public content::ResourceDispatcherHostDelegate {
35 public:
36 explicit TestResourceDispatcherHostDelegate(const GURL& url)
37 : url_(url), was_requested_(false), bypassed_cache_(false) {}
38 ~TestResourceDispatcherHostDelegate() override {}
39
40 void Reset() {
41 was_requested_ = false;
42 bypassed_cache_ = false;
43 }
44
45 // Resturns whether |url_| was requested.
46 bool was_requested() const { return was_requested_; }
47
48 // Returns whether any of the requests bypassed the HTTP cache.
49 bool bypassed_cache() const { return bypassed_cache_; }
50
51 private:
52 // content::ResourceDispatcherHostDelegate:
53 bool ShouldBeginRequest(const std::string& method,
54 const GURL& url,
55 content::ResourceType resource_type,
56 content::ResourceContext* resource_context) override {
57 return true;
58 }
59
60 void RequestBeginning(
61 net::URLRequest* request,
62 content::ResourceContext* resource_context,
63 content::AppCacheService* appcache_service,
64 content::ResourceType resource_type,
65 ScopedVector<content::ResourceThrottle>* throttles) override {
66 if (request->url() == url_) {
67 was_requested_ = true;
68 if (request->load_flags() & net::LOAD_BYPASS_CACHE)
69 bypassed_cache_ = true;
70 }
71 }
72
73 void DownloadStarting(
74 net::URLRequest* request,
75 content::ResourceContext* resource_context,
76 int child_id,
77 int route_id,
78 int request_id,
79 bool is_content_initiated,
80 bool must_download,
81 ScopedVector<content::ResourceThrottle>* throttles) override {}
82
83 private:
84 GURL url_;
85 bool was_requested_;
86 bool bypassed_cache_;
87
88 DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate);
89};
90
91// Checks whether the FaviconTabHelper is waiting for a download to complete or
92// for data from the FaviconService.
93class FaviconTabHelperPendingTaskChecker {
94 public:
95 virtual ~FaviconTabHelperPendingTaskChecker() {}
96
97 virtual bool HasPendingTasks() = 0;
98};
99
100// Waits for the following the finish:
101// - The pending navigation.
102// - FaviconHandler's pending favicon database requests.
103// - FaviconHandler's pending downloads.
sdefresneb0befeec2015-04-08 09:21:56104class PendingTaskWaiter : public content::NotificationObserver,
105 public favicon::FaviconDriverObserver {
pkotwiczfd773552015-03-16 00:29:14106 public:
107 PendingTaskWaiter(content::WebContents* web_contents,
108 FaviconTabHelperPendingTaskChecker* checker)
sdefresneb0befeec2015-04-08 09:21:56109 : checker_(checker),
110 load_stopped_(false),
111 scoped_observer_(this),
112 weak_factory_(this) {
pkotwiczfd773552015-03-16 00:29:14113 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
114 content::Source<content::NavigationController>(
115 &web_contents->GetController()));
sdefresneb0befeec2015-04-08 09:21:56116 scoped_observer_.Add(FaviconTabHelper::FromWebContents(web_contents));
pkotwiczfd773552015-03-16 00:29:14117 }
118 ~PendingTaskWaiter() override {}
119
120 void Wait() {
121 if (load_stopped_ && !checker_->HasPendingTasks())
122 return;
123
124 base::RunLoop run_loop;
125 quit_closure_ = run_loop.QuitClosure();
126 run_loop.Run();
127 }
128
129 private:
130 // content::NotificationObserver:
131 void Observe(int type,
132 const content::NotificationSource& source,
133 const content::NotificationDetails& details) override {
134 if (type == content::NOTIFICATION_LOAD_STOP)
135 load_stopped_ = true;
136
sdefresneb0befeec2015-04-08 09:21:56137 OnNotification();
138 }
139
140 // favicon::Favicon
141 void OnFaviconAvailable(const gfx::Image& image) override {}
142 void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
143 bool icon_url_changed) override {
144 OnNotification();
145 }
146
147 void OnNotification() {
pkotwiczfd773552015-03-16 00:29:14148 if (!quit_closure_.is_null()) {
149 // We stop waiting based on changes in state to FaviconHandler which occur
sdefresneb0befeec2015-04-08 09:21:56150 // immediately after OnFaviconUpdated() is called. Post a task to check if
151 // we can stop waiting.
pkotwiczfd773552015-03-16 00:29:14152 base::MessageLoopForUI::current()->PostTask(
153 FROM_HERE, base::Bind(&PendingTaskWaiter::EndLoopIfCanStopWaiting,
154 weak_factory_.GetWeakPtr()));
155 }
156 }
157
158 void EndLoopIfCanStopWaiting() {
159 if (!quit_closure_.is_null() &&
160 load_stopped_ &&
161 !checker_->HasPendingTasks()) {
162 quit_closure_.Run();
163 }
164 }
165
166 FaviconTabHelperPendingTaskChecker* checker_; // Not owned.
167 bool load_stopped_;
168 base::Closure quit_closure_;
169 content::NotificationRegistrar registrar_;
sdefresneb0befeec2015-04-08 09:21:56170 ScopedObserver<FaviconTabHelper, PendingTaskWaiter> scoped_observer_;
pkotwiczfd773552015-03-16 00:29:14171 base::WeakPtrFactory<PendingTaskWaiter> weak_factory_;
172
173 DISALLOW_COPY_AND_ASSIGN(PendingTaskWaiter);
174};
175
176} // namespace
177
178class FaviconTabHelperTest : public InProcessBrowserTest,
179 public FaviconTabHelperPendingTaskChecker {
180 public:
181 FaviconTabHelperTest() {}
182 ~FaviconTabHelperTest() override {}
183
184 content::WebContents* web_contents() {
185 return browser()->tab_strip_model()->GetActiveWebContents();
186 }
187
188 // FaviconTabHelperPendingTaskChecker:
189 bool HasPendingTasks() override {
sdefresne70a04802015-04-09 14:00:17190 return FaviconTabHelper::FromWebContents(web_contents())
191 ->HasPendingTasksForTest();
pkotwiczfd773552015-03-16 00:29:14192 }
193
194 private:
195 DISALLOW_COPY_AND_ASSIGN(FaviconTabHelperTest);
196};
197
198// Test that when a user reloads a page ignoring the cache that the favicon is
199// is redownloaded and (not returned from either the favicon cache or the HTTP
200// cache).
201IN_PROC_BROWSER_TEST_F(FaviconTabHelperTest, ReloadIgnoringCache) {
202 ASSERT_TRUE(test_server()->Start());
203 GURL url = test_server()->GetURL("files/favicon/page_with_favicon.html");
204 GURL icon_url = test_server()->GetURL("files/favicon/icon.ico");
205
206 scoped_ptr<TestResourceDispatcherHostDelegate> delegate(
207 new TestResourceDispatcherHostDelegate(icon_url));
208 content::ResourceDispatcherHost::Get()->SetDelegate(delegate.get());
209
210 // Initial visit in order to populate the cache.
211 {
212 PendingTaskWaiter waiter(web_contents(), this);
213 ui_test_utils::NavigateToURLWithDisposition(
214 browser(), url, CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE);
215 waiter.Wait();
216 }
217 ASSERT_TRUE(delegate->was_requested());
218 EXPECT_FALSE(delegate->bypassed_cache());
219 delegate->Reset();
220
221 ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
222
223 // A normal visit should fetch the favicon from either the favicon database or
224 // the HTTP cache.
225 {
226 PendingTaskWaiter waiter(web_contents(), this);
227 ui_test_utils::NavigateToURLWithDisposition(
228 browser(), url, CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE);
229 waiter.Wait();
230 }
231 EXPECT_FALSE(delegate->bypassed_cache());
232 delegate->Reset();
233
234 // A reload ignoring the cache should refetch the favicon from the website.
235 {
236 PendingTaskWaiter waiter(web_contents(), this);
237 chrome::ExecuteCommand(browser(), IDC_RELOAD_IGNORING_CACHE);
238 waiter.Wait();
239 }
240 ASSERT_TRUE(delegate->was_requested());
241 EXPECT_TRUE(delegate->bypassed_cache());
242}