OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/favicon/content/content_favicon_driver.h" | 5 #include "components/favicon/content/content_favicon_driver.h" |
6 | 6 |
7 #include "base/location.h" | 7 #include "base/location.h" |
8 #include "base/macros.h" | 8 #include "base/macros.h" |
9 #include "base/memory/weak_ptr.h" | 9 #include "base/memory/weak_ptr.h" |
10 #include "base/run_loop.h" | 10 #include "base/run_loop.h" |
11 #include "base/scoped_observer.h" | |
12 #include "base/single_thread_task_runner.h" | 11 #include "base/single_thread_task_runner.h" |
| 12 #include "base/test/scoped_feature_list.h" |
13 #include "base/threading/thread_task_runner_handle.h" | 13 #include "base/threading/thread_task_runner_handle.h" |
14 #include "chrome/app/chrome_command_ids.h" | 14 #include "chrome/app/chrome_command_ids.h" |
15 #include "chrome/browser/chrome_notification_types.h" | 15 #include "chrome/browser/chrome_notification_types.h" |
| 16 #include "chrome/browser/favicon/favicon_service_factory.h" |
16 #include "chrome/browser/ui/browser.h" | 17 #include "chrome/browser/ui/browser.h" |
17 #include "chrome/browser/ui/browser_commands.h" | 18 #include "chrome/browser/ui/browser_commands.h" |
18 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 19 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
19 #include "chrome/test/base/in_process_browser_test.h" | 20 #include "chrome/test/base/in_process_browser_test.h" |
20 #include "chrome/test/base/ui_test_utils.h" | 21 #include "chrome/test/base/ui_test_utils.h" |
21 #include "components/favicon/core/favicon_driver_observer.h" | |
22 #include "components/favicon/core/favicon_handler.h" | 22 #include "components/favicon/core/favicon_handler.h" |
| 23 #include "components/favicon/core/favicon_service.h" |
23 #include "content/public/browser/notification_observer.h" | 24 #include "content/public/browser/notification_observer.h" |
24 #include "content/public/browser/notification_registrar.h" | 25 #include "content/public/browser/notification_registrar.h" |
25 #include "content/public/browser/notification_types.h" | 26 #include "content/public/browser/notification_types.h" |
26 #include "content/public/browser/resource_dispatcher_host.h" | 27 #include "content/public/browser/resource_dispatcher_host.h" |
27 #include "content/public/browser/resource_dispatcher_host_delegate.h" | 28 #include "content/public/browser/resource_dispatcher_host_delegate.h" |
28 #include "net/base/load_flags.h" | 29 #include "net/base/load_flags.h" |
29 #include "net/test/embedded_test_server/embedded_test_server.h" | 30 #include "net/test/embedded_test_server/embedded_test_server.h" |
30 #include "net/url_request/url_request.h" | 31 #include "net/url_request/url_request.h" |
31 #include "url/url_constants.h" | 32 #include "url/url_constants.h" |
32 | 33 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 public: | 89 public: |
89 virtual ~FaviconDriverPendingTaskChecker() {} | 90 virtual ~FaviconDriverPendingTaskChecker() {} |
90 | 91 |
91 virtual bool HasPendingTasks() = 0; | 92 virtual bool HasPendingTasks() = 0; |
92 }; | 93 }; |
93 | 94 |
94 // Waits for the following the finish: | 95 // Waits for the following the finish: |
95 // - The pending navigation. | 96 // - The pending navigation. |
96 // - FaviconHandler's pending favicon database requests. | 97 // - FaviconHandler's pending favicon database requests. |
97 // - FaviconHandler's pending downloads. | 98 // - FaviconHandler's pending downloads. |
98 class PendingTaskWaiter : public content::NotificationObserver, | 99 class PendingTaskWaiter : public content::NotificationObserver { |
99 public favicon::FaviconDriverObserver { | |
100 public: | 100 public: |
101 PendingTaskWaiter(content::WebContents* web_contents, | 101 PendingTaskWaiter(content::WebContents* web_contents, |
102 FaviconDriverPendingTaskChecker* checker) | 102 FaviconDriverPendingTaskChecker* checker) |
103 : checker_(checker), | 103 : checker_(checker), |
104 load_stopped_(false), | 104 load_stopped_(false), |
105 scoped_observer_(this), | |
106 weak_factory_(this) { | 105 weak_factory_(this) { |
107 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, | 106 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, |
108 content::Source<content::NavigationController>( | 107 content::Source<content::NavigationController>( |
109 &web_contents->GetController())); | 108 &web_contents->GetController())); |
110 scoped_observer_.Add( | |
111 favicon::ContentFaviconDriver::FromWebContents(web_contents)); | |
112 } | 109 } |
113 ~PendingTaskWaiter() override {} | 110 ~PendingTaskWaiter() override {} |
114 | 111 |
115 void Wait() { | 112 void Wait() { |
116 if (load_stopped_ && !checker_->HasPendingTasks()) | 113 if (load_stopped_ && !checker_->HasPendingTasks()) |
117 return; | 114 return; |
118 | 115 |
119 base::RunLoop run_loop; | 116 base::RunLoop run_loop; |
120 quit_closure_ = run_loop.QuitClosure(); | 117 quit_closure_ = run_loop.QuitClosure(); |
121 run_loop.Run(); | 118 run_loop.Run(); |
122 } | 119 } |
123 | 120 |
124 private: | 121 private: |
125 // content::NotificationObserver: | 122 // content::NotificationObserver: |
126 void Observe(int type, | 123 void Observe(int type, |
127 const content::NotificationSource& source, | 124 const content::NotificationSource& source, |
128 const content::NotificationDetails& details) override { | 125 const content::NotificationDetails& details) override { |
129 if (type == content::NOTIFICATION_LOAD_STOP) | 126 if (type == content::NOTIFICATION_LOAD_STOP) |
130 load_stopped_ = true; | 127 load_stopped_ = true; |
131 | 128 |
132 OnNotification(); | 129 // We need to poll periodically because Delegate::OnFaviconUpdated() is not |
| 130 // guaranteed to be called upon completion of the last database request / |
| 131 // download. In particular, OnFaviconUpdated() might not be called if a |
| 132 // database request confirms the data sent in the previous |
| 133 // OnFaviconUpdated() call. |
| 134 CheckStopWaitingPeriodically(); |
133 } | 135 } |
134 | 136 |
135 // favicon::Favicon | 137 void CheckStopWaitingPeriodically() { |
136 void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver, | 138 EndLoopIfCanStopWaiting(); |
137 NotificationIconType notification_icon_type, | 139 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
138 const GURL& icon_url, | 140 FROM_HERE, |
139 bool icon_url_changed, | 141 base::BindOnce(&PendingTaskWaiter::CheckStopWaitingPeriodically, |
140 const gfx::Image& image) override { | 142 weak_factory_.GetWeakPtr()), |
141 OnNotification(); | 143 base::TimeDelta::FromSeconds(1)); |
142 } | |
143 | |
144 void OnNotification() { | |
145 if (!quit_closure_.is_null()) { | |
146 // We stop waiting based on changes in state to FaviconHandler which occur | |
147 // immediately after OnFaviconUpdated() is called. Post a task to check if | |
148 // we can stop waiting. | |
149 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
150 FROM_HERE, base::BindOnce(&PendingTaskWaiter::EndLoopIfCanStopWaiting, | |
151 weak_factory_.GetWeakPtr())); | |
152 } | |
153 } | 144 } |
154 | 145 |
155 void EndLoopIfCanStopWaiting() { | 146 void EndLoopIfCanStopWaiting() { |
156 if (!quit_closure_.is_null() && | 147 if (!quit_closure_.is_null() && |
157 load_stopped_ && | 148 load_stopped_ && |
158 !checker_->HasPendingTasks()) { | 149 !checker_->HasPendingTasks()) { |
159 quit_closure_.Run(); | 150 quit_closure_.Run(); |
160 } | 151 } |
161 } | 152 } |
162 | 153 |
163 FaviconDriverPendingTaskChecker* checker_; // Not owned. | 154 FaviconDriverPendingTaskChecker* checker_; // Not owned. |
164 bool load_stopped_; | 155 bool load_stopped_; |
165 base::Closure quit_closure_; | 156 base::Closure quit_closure_; |
166 content::NotificationRegistrar registrar_; | 157 content::NotificationRegistrar registrar_; |
167 ScopedObserver<favicon::FaviconDriver, PendingTaskWaiter> scoped_observer_; | |
168 base::WeakPtrFactory<PendingTaskWaiter> weak_factory_; | 158 base::WeakPtrFactory<PendingTaskWaiter> weak_factory_; |
169 | 159 |
170 DISALLOW_COPY_AND_ASSIGN(PendingTaskWaiter); | 160 DISALLOW_COPY_AND_ASSIGN(PendingTaskWaiter); |
171 }; | 161 }; |
172 | 162 |
173 } // namespace | 163 } // namespace |
174 | 164 |
175 class ContentFaviconDriverTest : public InProcessBrowserTest, | 165 class ContentFaviconDriverTest : public InProcessBrowserTest, |
176 public FaviconDriverPendingTaskChecker { | 166 public FaviconDriverPendingTaskChecker { |
177 public: | 167 public: |
178 ContentFaviconDriverTest() {} | 168 ContentFaviconDriverTest() {} |
179 ~ContentFaviconDriverTest() override {} | 169 ~ContentFaviconDriverTest() override {} |
180 | 170 |
181 content::WebContents* web_contents() { | 171 content::WebContents* web_contents() { |
182 return browser()->tab_strip_model()->GetActiveWebContents(); | 172 return browser()->tab_strip_model()->GetActiveWebContents(); |
183 } | 173 } |
184 | 174 |
185 // FaviconDriverPendingTaskChecker: | 175 // FaviconDriverPendingTaskChecker: |
186 bool HasPendingTasks() override { | 176 bool HasPendingTasks() override { |
187 return favicon::ContentFaviconDriver::FromWebContents(web_contents()) | 177 return favicon::ContentFaviconDriver::FromWebContents(web_contents()) |
188 ->HasPendingTasksForTest(); | 178 ->HasPendingTasksForTest(); |
189 } | 179 } |
190 | 180 |
| 181 favicon_base::FaviconRawBitmapResult GetFaviconForPageURL(const GURL& url) { |
| 182 favicon::FaviconService* favicon_service = |
| 183 FaviconServiceFactory::GetForProfile( |
| 184 browser()->profile(), ServiceAccessType::EXPLICIT_ACCESS); |
| 185 |
| 186 std::vector<favicon_base::FaviconRawBitmapResult> results; |
| 187 base::CancelableTaskTracker tracker; |
| 188 base::RunLoop loop; |
| 189 favicon_service->GetFaviconForPageURL( |
| 190 url, favicon_base::FAVICON, /*desired_size_in_dip=*/0, |
| 191 base::Bind( |
| 192 [](std::vector<favicon_base::FaviconRawBitmapResult>* save_results, |
| 193 base::RunLoop* loop, |
| 194 const std::vector<favicon_base::FaviconRawBitmapResult>& |
| 195 results) { |
| 196 *save_results = results; |
| 197 loop->Quit(); |
| 198 }, |
| 199 &results, &loop), |
| 200 &tracker); |
| 201 loop.Run(); |
| 202 for (const favicon_base::FaviconRawBitmapResult& result : results) { |
| 203 if (result.is_valid()) |
| 204 return result; |
| 205 } |
| 206 return favicon_base::FaviconRawBitmapResult(); |
| 207 } |
| 208 |
191 private: | 209 private: |
192 DISALLOW_COPY_AND_ASSIGN(ContentFaviconDriverTest); | 210 DISALLOW_COPY_AND_ASSIGN(ContentFaviconDriverTest); |
193 }; | 211 }; |
194 | 212 |
195 // Test that when a user reloads a page ignoring the cache that the favicon is | 213 // Test that when a user reloads a page ignoring the cache that the favicon is |
196 // is redownloaded and (not returned from either the favicon cache or the HTTP | 214 // is redownloaded and (not returned from either the favicon cache or the HTTP |
197 // cache). | 215 // cache). |
198 IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, ReloadBypassingCache) { | 216 IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, ReloadBypassingCache) { |
199 ASSERT_TRUE(embedded_test_server()->Start()); | 217 ASSERT_TRUE(embedded_test_server()->Start()); |
200 GURL url = embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); | 218 GURL url = embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 | 250 |
233 // A reload ignoring the cache should refetch the favicon from the website. | 251 // A reload ignoring the cache should refetch the favicon from the website. |
234 { | 252 { |
235 PendingTaskWaiter waiter(web_contents(), this); | 253 PendingTaskWaiter waiter(web_contents(), this); |
236 chrome::ExecuteCommand(browser(), IDC_RELOAD_BYPASSING_CACHE); | 254 chrome::ExecuteCommand(browser(), IDC_RELOAD_BYPASSING_CACHE); |
237 waiter.Wait(); | 255 waiter.Wait(); |
238 } | 256 } |
239 ASSERT_TRUE(delegate->was_requested()); | 257 ASSERT_TRUE(delegate->was_requested()); |
240 EXPECT_TRUE(delegate->bypassed_cache()); | 258 EXPECT_TRUE(delegate->bypassed_cache()); |
241 } | 259 } |
| 260 |
| 261 // Test that loading a page that contains icons only in the Web Manifest causes |
| 262 // those icons to be used. |
| 263 IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, LoadIconFromWebManifest) { |
| 264 base::test::ScopedFeatureList override_features; |
| 265 override_features.InitAndEnableFeature(favicon::kFaviconsFromWebManifest); |
| 266 |
| 267 ASSERT_TRUE(embedded_test_server()->Start()); |
| 268 GURL url = embedded_test_server()->GetURL("/favicon/page_with_manifest.html"); |
| 269 GURL icon_url = embedded_test_server()->GetURL("/favicon/icon.png"); |
| 270 |
| 271 std::unique_ptr<TestResourceDispatcherHostDelegate> delegate( |
| 272 new TestResourceDispatcherHostDelegate(icon_url)); |
| 273 content::ResourceDispatcherHost::Get()->SetDelegate(delegate.get()); |
| 274 |
| 275 PendingTaskWaiter waiter(web_contents(), this); |
| 276 ui_test_utils::NavigateToURLWithDisposition( |
| 277 browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| 278 ui_test_utils::BROWSER_TEST_NONE); |
| 279 waiter.Wait(); |
| 280 |
| 281 EXPECT_TRUE(delegate->was_requested()); |
| 282 } |
| 283 |
| 284 // Test that loading a page that contains a Web Manifest without icons and a |
| 285 // regular favicon in the HTML reports the icon. The regular icon is initially |
| 286 // cached in the Favicon database. |
| 287 IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| 288 LoadRegularIconDespiteWebManifestWithoutIcons) { |
| 289 ASSERT_TRUE(embedded_test_server()->Start()); |
| 290 GURL url = embedded_test_server()->GetURL( |
| 291 "/favicon/page_with_manifest_without_icons.html"); |
| 292 GURL icon_url = embedded_test_server()->GetURL("/favicon/icon.png"); |
| 293 |
| 294 // Initial visit with the feature still disabled, to populate the cache. |
| 295 { |
| 296 PendingTaskWaiter waiter(web_contents(), this); |
| 297 ui_test_utils::NavigateToURLWithDisposition( |
| 298 browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| 299 ui_test_utils::BROWSER_TEST_NONE); |
| 300 waiter.Wait(); |
| 301 } |
| 302 ASSERT_NE(nullptr, GetFaviconForPageURL(url).bitmap_data); |
| 303 |
| 304 ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)); |
| 305 |
| 306 // Enable the feature and visit the page again. |
| 307 base::test::ScopedFeatureList override_features; |
| 308 override_features.InitAndEnableFeature(favicon::kFaviconsFromWebManifest); |
| 309 |
| 310 { |
| 311 PendingTaskWaiter waiter(web_contents(), this); |
| 312 ui_test_utils::NavigateToURLWithDisposition( |
| 313 browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| 314 ui_test_utils::BROWSER_TEST_NONE); |
| 315 waiter.Wait(); |
| 316 } |
| 317 |
| 318 EXPECT_NE(nullptr, GetFaviconForPageURL(url).bitmap_data); |
| 319 } |
OLD | NEW |