1 // 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.
5 #include "chrome/browser/favicon/favicon_tab_helper.h"
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/ui/browser.h"
12 #include "chrome/browser/ui/browser_commands.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/test/base/in_process_browser_test.h"
15 #include "chrome/test/base/ui_test_utils.h"
16 #include "components/favicon/core/favicon_handler.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"
29 // Tracks whether the URL passed to the constructor is requested and whether
30 // the request bypasses the cache.
31 class TestResourceDispatcherHostDelegate
32 : public content::ResourceDispatcherHostDelegate
{
34 explicit TestResourceDispatcherHostDelegate(const GURL
& url
)
35 : url_(url
), was_requested_(false), bypassed_cache_(false) {}
36 ~TestResourceDispatcherHostDelegate() override
{}
39 was_requested_
= false;
40 bypassed_cache_
= false;
43 // Resturns whether |url_| was requested.
44 bool was_requested() const { return was_requested_
; }
46 // Returns whether any of the requests bypassed the HTTP cache.
47 bool bypassed_cache() const { return bypassed_cache_
; }
50 // content::ResourceDispatcherHostDelegate:
51 bool ShouldBeginRequest(const std::string
& method
,
53 content::ResourceType resource_type
,
54 content::ResourceContext
* resource_context
) override
{
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;
71 void DownloadStarting(
72 net::URLRequest
* request
,
73 content::ResourceContext
* resource_context
,
77 bool is_content_initiated
,
79 ScopedVector
<content::ResourceThrottle
>* throttles
) override
{}
86 DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate
);
89 // Checks whether the FaviconTabHelper is waiting for a download to complete or
90 // for data from the FaviconService.
91 class FaviconTabHelperPendingTaskChecker
{
93 virtual ~FaviconTabHelperPendingTaskChecker() {}
95 virtual bool HasPendingTasks() = 0;
98 // Waits for the following the finish:
99 // - The pending navigation.
100 // - FaviconHandler's pending favicon database requests.
101 // - FaviconHandler's pending downloads.
102 class PendingTaskWaiter
: public content::NotificationObserver
{
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()));
113 ~PendingTaskWaiter() override
{}
116 if (load_stopped_
&& !checker_
->HasPendingTasks())
119 base::RunLoop run_loop
;
120 quit_closure_
= run_loop
.QuitClosure();
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;
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()));
142 void EndLoopIfCanStopWaiting() {
143 if (!quit_closure_
.is_null() &&
145 !checker_
->HasPendingTasks()) {
150 FaviconTabHelperPendingTaskChecker
* checker_
; // Not owned.
152 base::Closure quit_closure_
;
153 content::NotificationRegistrar registrar_
;
154 base::WeakPtrFactory
<PendingTaskWaiter
> weak_factory_
;
156 DISALLOW_COPY_AND_ASSIGN(PendingTaskWaiter
);
161 class FaviconTabHelperTest
: public InProcessBrowserTest
,
162 public FaviconTabHelperPendingTaskChecker
{
164 FaviconTabHelperTest() {}
165 ~FaviconTabHelperTest() override
{}
167 content::WebContents
* web_contents() {
168 return browser()->tab_strip_model()->GetActiveWebContents();
171 // FaviconTabHelperPendingTaskChecker:
172 bool HasPendingTasks() override
{
173 favicon::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();
181 DISALLOW_COPY_AND_ASSIGN(FaviconTabHelperTest
);
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
187 IN_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");
192 scoped_ptr
<TestResourceDispatcherHostDelegate
> delegate(
193 new TestResourceDispatcherHostDelegate(icon_url
));
194 content::ResourceDispatcherHost::Get()->SetDelegate(delegate
.get());
196 // Initial visit in order to populate the cache.
198 PendingTaskWaiter
waiter(web_contents(), this);
199 ui_test_utils::NavigateToURLWithDisposition(
200 browser(), url
, CURRENT_TAB
, ui_test_utils::BROWSER_TEST_NONE
);
203 ASSERT_TRUE(delegate
->was_requested());
204 EXPECT_FALSE(delegate
->bypassed_cache());
207 ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL
));
209 // A normal visit should fetch the favicon from either the favicon database or
212 PendingTaskWaiter
waiter(web_contents(), this);
213 ui_test_utils::NavigateToURLWithDisposition(
214 browser(), url
, CURRENT_TAB
, ui_test_utils::BROWSER_TEST_NONE
);
217 EXPECT_FALSE(delegate
->bypassed_cache());
220 // A reload ignoring the cache should refetch the favicon from the website.
222 PendingTaskWaiter
waiter(web_contents(), this);
223 chrome::ExecuteCommand(browser(), IDC_RELOAD_IGNORING_CACHE
);
226 ASSERT_TRUE(delegate
->was_requested());
227 EXPECT_TRUE(delegate
->bypassed_cache());