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 "components/favicon/content/content_favicon_driver.h"
7 #include "base/location.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/run_loop.h"
10 #include "base/scoped_observer.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "chrome/app/chrome_command_ids.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_commands.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/test/base/in_process_browser_test.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "components/favicon/core/favicon_driver_observer.h"
21 #include "components/favicon/core/favicon_handler.h"
22 #include "content/public/browser/notification_observer.h"
23 #include "content/public/browser/notification_registrar.h"
24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/resource_dispatcher_host.h"
26 #include "content/public/browser/resource_dispatcher_host_delegate.h"
27 #include "net/base/load_flags.h"
28 #include "net/test/spawned_test_server/spawned_test_server.h"
29 #include "net/url_request/url_request.h"
30 #include "url/url_constants.h"
34 // Tracks whether the URL passed to the constructor is requested and whether
35 // the request bypasses the cache.
36 class TestResourceDispatcherHostDelegate
37 : public content::ResourceDispatcherHostDelegate
{
39 explicit TestResourceDispatcherHostDelegate(const GURL
& url
)
40 : url_(url
), was_requested_(false), bypassed_cache_(false) {}
41 ~TestResourceDispatcherHostDelegate() override
{}
44 was_requested_
= false;
45 bypassed_cache_
= false;
48 // Resturns whether |url_| was requested.
49 bool was_requested() const { return was_requested_
; }
51 // Returns whether any of the requests bypassed the HTTP cache.
52 bool bypassed_cache() const { return bypassed_cache_
; }
55 // content::ResourceDispatcherHostDelegate:
56 bool ShouldBeginRequest(const std::string
& method
,
58 content::ResourceType resource_type
,
59 content::ResourceContext
* resource_context
) override
{
63 void RequestBeginning(
64 net::URLRequest
* request
,
65 content::ResourceContext
* resource_context
,
66 content::AppCacheService
* appcache_service
,
67 content::ResourceType resource_type
,
68 ScopedVector
<content::ResourceThrottle
>* throttles
) override
{
69 if (request
->url() == url_
) {
70 was_requested_
= true;
71 if (request
->load_flags() & net::LOAD_BYPASS_CACHE
)
72 bypassed_cache_
= true;
76 void DownloadStarting(
77 net::URLRequest
* request
,
78 content::ResourceContext
* resource_context
,
82 bool is_content_initiated
,
84 ScopedVector
<content::ResourceThrottle
>* throttles
) override
{}
91 DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate
);
94 // Checks whether the FaviconDriver is waiting for a download to complete or
95 // for data from the FaviconService.
96 class FaviconDriverPendingTaskChecker
{
98 virtual ~FaviconDriverPendingTaskChecker() {}
100 virtual bool HasPendingTasks() = 0;
103 // Waits for the following the finish:
104 // - The pending navigation.
105 // - FaviconHandler's pending favicon database requests.
106 // - FaviconHandler's pending downloads.
107 class PendingTaskWaiter
: public content::NotificationObserver
,
108 public favicon::FaviconDriverObserver
{
110 PendingTaskWaiter(content::WebContents
* web_contents
,
111 FaviconDriverPendingTaskChecker
* checker
)
113 load_stopped_(false),
114 scoped_observer_(this),
115 weak_factory_(this) {
116 registrar_
.Add(this, content::NOTIFICATION_LOAD_STOP
,
117 content::Source
<content::NavigationController
>(
118 &web_contents
->GetController()));
119 scoped_observer_
.Add(
120 favicon::ContentFaviconDriver::FromWebContents(web_contents
));
122 ~PendingTaskWaiter() override
{}
125 if (load_stopped_
&& !checker_
->HasPendingTasks())
128 base::RunLoop run_loop
;
129 quit_closure_
= run_loop
.QuitClosure();
134 // content::NotificationObserver:
135 void Observe(int type
,
136 const content::NotificationSource
& source
,
137 const content::NotificationDetails
& details
) override
{
138 if (type
== content::NOTIFICATION_LOAD_STOP
)
139 load_stopped_
= true;
145 void OnFaviconAvailable(const gfx::Image
& image
) override
{}
146 void OnFaviconUpdated(favicon::FaviconDriver
* favicon_driver
,
147 bool icon_url_changed
) override
{
151 void OnNotification() {
152 if (!quit_closure_
.is_null()) {
153 // We stop waiting based on changes in state to FaviconHandler which occur
154 // immediately after OnFaviconUpdated() is called. Post a task to check if
155 // we can stop waiting.
156 base::ThreadTaskRunnerHandle::Get()->PostTask(
157 FROM_HERE
, base::Bind(&PendingTaskWaiter::EndLoopIfCanStopWaiting
,
158 weak_factory_
.GetWeakPtr()));
162 void EndLoopIfCanStopWaiting() {
163 if (!quit_closure_
.is_null() &&
165 !checker_
->HasPendingTasks()) {
170 FaviconDriverPendingTaskChecker
* checker_
; // Not owned.
172 base::Closure quit_closure_
;
173 content::NotificationRegistrar registrar_
;
174 ScopedObserver
<favicon::FaviconDriver
, PendingTaskWaiter
> scoped_observer_
;
175 base::WeakPtrFactory
<PendingTaskWaiter
> weak_factory_
;
177 DISALLOW_COPY_AND_ASSIGN(PendingTaskWaiter
);
182 class ContentFaviconDriverTest
: public InProcessBrowserTest
,
183 public FaviconDriverPendingTaskChecker
{
185 ContentFaviconDriverTest() {}
186 ~ContentFaviconDriverTest() override
{}
188 content::WebContents
* web_contents() {
189 return browser()->tab_strip_model()->GetActiveWebContents();
192 // FaviconDriverPendingTaskChecker:
193 bool HasPendingTasks() override
{
194 return favicon::ContentFaviconDriver::FromWebContents(web_contents())
195 ->HasPendingTasksForTest();
199 DISALLOW_COPY_AND_ASSIGN(ContentFaviconDriverTest
);
202 // Test that when a user reloads a page ignoring the cache that the favicon is
203 // is redownloaded and (not returned from either the favicon cache or the HTTP
205 IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest
, ReloadIgnoringCache
) {
206 ASSERT_TRUE(test_server()->Start());
207 GURL url
= test_server()->GetURL("files/favicon/page_with_favicon.html");
208 GURL icon_url
= test_server()->GetURL("files/favicon/icon.ico");
210 scoped_ptr
<TestResourceDispatcherHostDelegate
> delegate(
211 new TestResourceDispatcherHostDelegate(icon_url
));
212 content::ResourceDispatcherHost::Get()->SetDelegate(delegate
.get());
214 // Initial visit in order to populate the cache.
216 PendingTaskWaiter
waiter(web_contents(), this);
217 ui_test_utils::NavigateToURLWithDisposition(
218 browser(), url
, CURRENT_TAB
, ui_test_utils::BROWSER_TEST_NONE
);
221 ASSERT_TRUE(delegate
->was_requested());
222 EXPECT_FALSE(delegate
->bypassed_cache());
225 ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL
));
227 // A normal visit should fetch the favicon from either the favicon database or
230 PendingTaskWaiter
waiter(web_contents(), this);
231 ui_test_utils::NavigateToURLWithDisposition(
232 browser(), url
, CURRENT_TAB
, ui_test_utils::BROWSER_TEST_NONE
);
235 EXPECT_FALSE(delegate
->bypassed_cache());
238 // A reload ignoring the cache should refetch the favicon from the website.
240 PendingTaskWaiter
waiter(web_contents(), this);
241 chrome::ExecuteCommand(browser(), IDC_RELOAD_IGNORING_CACHE
);
244 ASSERT_TRUE(delegate
->was_requested());
245 EXPECT_TRUE(delegate
->bypassed_cache());