blob: e5facb74e706c7934a7fb7ba540f7b2ebf92582b [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"
9#include "chrome/app/chrome_command_ids.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/favicon/favicon_handler.h"
12#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"
17#include "content/public/browser/notification_observer.h"
18#include "content/public/browser/notification_registrar.h"
19#include "content/public/browser/notification_types.h"
20#include "content/public/browser/resource_dispatcher_host.h"
21#include "content/public/browser/resource_dispatcher_host_delegate.h"
22#include "net/base/load_flags.h"
23#include "net/test/spawned_test_server/spawned_test_server.h"
24#include "net/url_request/url_request.h"
25#include "url/url_constants.h"
26
27namespace {
28
29// Tracks whether the URL passed to the constructor is requested and whether
30// the request bypasses the cache.
31class TestResourceDispatcherHostDelegate
32 : public content::ResourceDispatcherHostDelegate {
33 public:
34 explicit TestResourceDispatcherHostDelegate(const GURL& url)
35 : url_(url), was_requested_(false), bypassed_cache_(false) {}
36 ~TestResourceDispatcherHostDelegate() override {}
37
38 void Reset() {
39 was_requested_ = false;
40 bypassed_cache_ = false;
41 }
42
43 // Resturns whether |url_| was requested.
44 bool was_requested() const { return was_requested_; }
45
46 // Returns whether any of the requests bypassed the HTTP cache.
47 bool bypassed_cache() const { return bypassed_cache_; }
48
49 private:
50 // content::ResourceDispatcherHostDelegate:
51 bool ShouldBeginRequest(const std::string& method,
52 const GURL& url,
53 content::ResourceType resource_type,
54 content::ResourceContext* resource_context) override {
55 return true;
56 }
57
58 void RequestBeginning(
59 net::URLRequest* request,
60 content::ResourceContext* resource_context,
61 content::AppCacheService* appcache_service,
62 content::ResourceType resource_type,
63 ScopedVector<content::ResourceThrottle>* throttles) override {
64 if (request->url() == url_) {
65 was_requested_ = true;
66 if (request->load_flags() & net::LOAD_BYPASS_CACHE)
67 bypassed_cache_ = true;
68 }
69 }
70
71 void DownloadStarting(
72 net::URLRequest* request,
73 content::ResourceContext* resource_context,
74 int child_id,
75 int route_id,
76 int request_id,
77 bool is_content_initiated,
78 bool must_download,
79 ScopedVector<content::ResourceThrottle>* throttles) override {}
80
81 private:
82 GURL url_;
83 bool was_requested_;
84 bool bypassed_cache_;
85
86 DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate);
87};
88
89// Checks whether the FaviconTabHelper is waiting for a download to complete or
90// for data from the FaviconService.
91class FaviconTabHelperPendingTaskChecker {
92 public:
93 virtual ~FaviconTabHelperPendingTaskChecker() {}
94
95 virtual bool HasPendingTasks() = 0;
96};
97
98// Waits for the following the finish:
99// - The pending navigation.
100// - FaviconHandler's pending favicon database requests.
101// - FaviconHandler's pending downloads.
102class PendingTaskWaiter : public content::NotificationObserver {
103 public:
104 PendingTaskWaiter(content::WebContents* web_contents,
105 FaviconTabHelperPendingTaskChecker* checker)
106 : checker_(checker), load_stopped_(false), weak_factory_(this) {
107 registrar_.Add(this, chrome::NOTIFICATION_FAVICON_UPDATED,
108 content::Source<content::WebContents>(web_contents));
109 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
110 content::Source<content::NavigationController>(
111 &web_contents->GetController()));
112 }
113 ~PendingTaskWaiter() override {}
114
115 void Wait() {
116 if (load_stopped_ && !checker_->HasPendingTasks())
117 return;
118
119 base::RunLoop run_loop;
120 quit_closure_ = run_loop.QuitClosure();
121 run_loop.Run();
122 }
123
124 private:
125 // content::NotificationObserver:
126 void Observe(int type,
127 const content::NotificationSource& source,
128 const content::NotificationDetails& details) override {
129 if (type == content::NOTIFICATION_LOAD_STOP)
130 load_stopped_ = true;
131
132 if (!quit_closure_.is_null()) {
133 // We stop waiting based on changes in state to FaviconHandler which occur
134 // immediately after NOTIFICATION_FAVICON_UPDATED is sent. Post a task to
135 // check if we can stop waiting.
136 base::MessageLoopForUI::current()->PostTask(
137 FROM_HERE, base::Bind(&PendingTaskWaiter::EndLoopIfCanStopWaiting,
138 weak_factory_.GetWeakPtr()));
139 }
140 }
141
142 void EndLoopIfCanStopWaiting() {
143 if (!quit_closure_.is_null() &&
144 load_stopped_ &&
145 !checker_->HasPendingTasks()) {
146 quit_closure_.Run();
147 }
148 }
149
150 FaviconTabHelperPendingTaskChecker* checker_; // Not owned.
151 bool load_stopped_;
152 base::Closure quit_closure_;
153 content::NotificationRegistrar registrar_;
154 base::WeakPtrFactory<PendingTaskWaiter> weak_factory_;
155
156 DISALLOW_COPY_AND_ASSIGN(PendingTaskWaiter);
157};
158
159} // namespace
160
161class FaviconTabHelperTest : public InProcessBrowserTest,
162 public FaviconTabHelperPendingTaskChecker {
163 public:
164 FaviconTabHelperTest() {}
165 ~FaviconTabHelperTest() override {}
166
167 content::WebContents* web_contents() {
168 return browser()->tab_strip_model()->GetActiveWebContents();
169 }
170
171 // FaviconTabHelperPendingTaskChecker:
172 bool HasPendingTasks() override {
173 FaviconHandler* favicon_handler =
174 FaviconTabHelper::FromWebContents(web_contents())
175 ->favicon_handler_.get();
176 return !favicon_handler->download_requests_.empty() ||
177 favicon_handler->cancelable_task_tracker_.HasTrackedTasks();
178 }
179
180 private:
181 DISALLOW_COPY_AND_ASSIGN(FaviconTabHelperTest);
182};
183
184// Test that when a user reloads a page ignoring the cache that the favicon is
185// is redownloaded and (not returned from either the favicon cache or the HTTP
186// cache).
187IN_PROC_BROWSER_TEST_F(FaviconTabHelperTest, ReloadIgnoringCache) {
188 ASSERT_TRUE(test_server()->Start());
189 GURL url = test_server()->GetURL("files/favicon/page_with_favicon.html");
190 GURL icon_url = test_server()->GetURL("files/favicon/icon.ico");
191
192 scoped_ptr<TestResourceDispatcherHostDelegate> delegate(
193 new TestResourceDispatcherHostDelegate(icon_url));
194 content::ResourceDispatcherHost::Get()->SetDelegate(delegate.get());
195
196 // Initial visit in order to populate the cache.
197 {
198 PendingTaskWaiter waiter(web_contents(), this);
199 ui_test_utils::NavigateToURLWithDisposition(
200 browser(), url, CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE);
201 waiter.Wait();
202 }
203 ASSERT_TRUE(delegate->was_requested());
204 EXPECT_FALSE(delegate->bypassed_cache());
205 delegate->Reset();
206
207 ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
208
209 // A normal visit should fetch the favicon from either the favicon database or
210 // the HTTP 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 EXPECT_FALSE(delegate->bypassed_cache());
218 delegate->Reset();
219
220 // A reload ignoring the cache should refetch the favicon from the website.
221 {
222 PendingTaskWaiter waiter(web_contents(), this);
223 chrome::ExecuteCommand(browser(), IDC_RELOAD_IGNORING_CACHE);
224 waiter.Wait();
225 }
226 ASSERT_TRUE(delegate->was_requested());
227 EXPECT_TRUE(delegate->bypassed_cache());
228}