| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/favicon/content/content_favicon_driver.h" |
| |
| #include <set> |
| |
| #include "base/command_line.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/optional.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/pattern.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/favicon/favicon_service_factory.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/favicon/core/favicon_handler.h" |
| #include "components/favicon/core/favicon_service.h" |
| #include "components/network_session_configurator/common/network_switches.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/url_loader_interceptor.h" |
| #include "net/base/load_flags.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/url_request/url_request.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "ui/gfx/image/image_unittest_util.h" |
| #include "url/url_constants.h" |
| |
| const base::FilePath::CharType kDocRoot[] = |
| FILE_PATH_LITERAL("chrome/test/data"); |
| |
| namespace { |
| |
| using testing::ElementsAre; |
| |
| // Tracks which URLs are loaded and whether the requests bypass the cache. |
| class TestURLLoaderInterceptor { |
| public: |
| TestURLLoaderInterceptor() |
| : interceptor_( |
| base::BindRepeating(&TestURLLoaderInterceptor::InterceptURLRequest, |
| base::Unretained(this))) {} |
| |
| bool was_loaded(const GURL& url) const { |
| return intercepted_urls_.find(url) != intercepted_urls_.end(); |
| } |
| |
| bool did_bypass_cache(const GURL& url) const { |
| return bypass_cache_urls_.find(url) != bypass_cache_urls_.end(); |
| } |
| |
| void Reset() { |
| intercepted_urls_.clear(); |
| bypass_cache_urls_.clear(); |
| } |
| |
| private: |
| bool InterceptURLRequest( |
| content::URLLoaderInterceptor::RequestParams* params) { |
| intercepted_urls_.insert(params->url_request.url); |
| if (params->url_request.load_flags & net::LOAD_BYPASS_CACHE) |
| bypass_cache_urls_.insert(params->url_request.url); |
| return false; |
| } |
| |
| std::set<GURL> intercepted_urls_; |
| std::set<GURL> bypass_cache_urls_; |
| content::URLLoaderInterceptor interceptor_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestURLLoaderInterceptor); |
| }; |
| |
| // Waits for the following the finish: |
| // - The pending navigation. |
| // - FaviconHandler's pending favicon database requests. |
| // - FaviconHandler's pending downloads. |
| // - Optionally, for a specific page URL (as a mechanism to wait of Javascript |
| // completion). |
| class PendingTaskWaiter : public content::WebContentsObserver { |
| public: |
| explicit PendingTaskWaiter(content::WebContents* web_contents) |
| : WebContentsObserver(web_contents), weak_factory_(this) {} |
| ~PendingTaskWaiter() override {} |
| |
| void AlsoRequireUrl(const GURL& url) { required_url_ = url; } |
| |
| void AlsoRequireTitle(const base::string16& title) { |
| required_title_ = title; |
| } |
| |
| void Wait() { |
| base::RunLoop run_loop; |
| quit_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| private: |
| // content::WebContentsObserver: |
| void DidStopLoading() override { TestUrlAndTitle(); } |
| |
| void TitleWasSet(content::NavigationEntry* entry) override { |
| TestUrlAndTitle(); |
| } |
| |
| void TestUrlAndTitle() { |
| if (!required_url_.is_empty() && |
| required_url_ != web_contents()->GetLastCommittedURL()) { |
| return; |
| } |
| |
| if (required_title_.has_value() && |
| *required_title_ != web_contents()->GetTitle()) { |
| return; |
| } |
| |
| // We need to poll periodically because Delegate::OnFaviconUpdated() is not |
| // guaranteed to be called upon completion of the last database request / |
| // download. In particular, OnFaviconUpdated() might not be called if a |
| // database request confirms the data sent in the previous |
| // OnFaviconUpdated() call. |
| CheckStopWaitingPeriodically(); |
| } |
| |
| void CheckStopWaitingPeriodically() { |
| EndLoopIfCanStopWaiting(); |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&PendingTaskWaiter::CheckStopWaitingPeriodically, |
| weak_factory_.GetWeakPtr()), |
| base::TimeDelta::FromSeconds(1)); |
| } |
| |
| void EndLoopIfCanStopWaiting() { |
| if (!quit_closure_.is_null() && |
| !favicon::ContentFaviconDriver::FromWebContents(web_contents()) |
| ->HasPendingTasksForTest()) { |
| quit_closure_.Run(); |
| } |
| } |
| |
| base::Closure quit_closure_; |
| GURL required_url_; |
| base::Optional<base::string16> required_title_; |
| base::WeakPtrFactory<PendingTaskWaiter> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PendingTaskWaiter); |
| }; |
| |
| class PageLoadStopper : public content::WebContentsObserver { |
| public: |
| explicit PageLoadStopper(content::WebContents* web_contents) |
| : WebContentsObserver(web_contents), stop_on_finish_(false) {} |
| ~PageLoadStopper() override {} |
| |
| void StopOnDidFinishNavigation() { stop_on_finish_ = true; } |
| |
| const std::vector<GURL>& last_favicon_candidates() const { |
| return last_favicon_candidates_; |
| } |
| |
| private: |
| // content::WebContentsObserver: |
| void DidFinishNavigation( |
| content::NavigationHandle* navigation_handle) override { |
| if (navigation_handle->IsInMainFrame() && stop_on_finish_) { |
| web_contents()->Stop(); |
| stop_on_finish_ = false; |
| } |
| } |
| |
| void DidUpdateFaviconURL( |
| const std::vector<content::FaviconURL>& candidates) override { |
| last_favicon_candidates_.clear(); |
| for (const content::FaviconURL& candidate : candidates) |
| last_favicon_candidates_.push_back(candidate.icon_url); |
| } |
| |
| bool stop_on_finish_; |
| std::vector<GURL> last_favicon_candidates_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PageLoadStopper); |
| }; |
| |
| } // namespace |
| |
| class ContentFaviconDriverTest : public InProcessBrowserTest { |
| public: |
| ContentFaviconDriverTest() {} |
| ~ContentFaviconDriverTest() override {} |
| |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitch(switches::kIgnoreCertificateErrors); |
| } |
| |
| content::WebContents* web_contents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| favicon::FaviconService* favicon_service() { |
| return FaviconServiceFactory::GetForProfile( |
| browser()->profile(), ServiceAccessType::EXPLICIT_ACCESS); |
| } |
| |
| favicon_base::FaviconRawBitmapResult GetFaviconForPageURL( |
| const GURL& url, |
| favicon_base::IconType icon_type) { |
| std::vector<favicon_base::FaviconRawBitmapResult> results; |
| base::CancelableTaskTracker tracker; |
| base::RunLoop loop; |
| favicon_service()->GetFaviconForPageURL( |
| url, {icon_type}, /*desired_size_in_dip=*/0, |
| base::Bind( |
| [](std::vector<favicon_base::FaviconRawBitmapResult>* save_results, |
| base::RunLoop* loop, |
| const std::vector<favicon_base::FaviconRawBitmapResult>& |
| results) { |
| *save_results = results; |
| loop->Quit(); |
| }, |
| &results, &loop), |
| &tracker); |
| loop.Run(); |
| for (const favicon_base::FaviconRawBitmapResult& result : results) { |
| if (result.is_valid()) |
| return result; |
| } |
| return favicon_base::FaviconRawBitmapResult(); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ContentFaviconDriverTest); |
| }; |
| |
| // Test that when a user reloads a page ignoring the cache that the favicon is |
| // is redownloaded and (not returned from either the favicon cache or the HTTP |
| // cache). |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, ReloadBypassingCache) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); |
| GURL icon_url = embedded_test_server()->GetURL("/favicon/icon.png"); |
| |
| TestURLLoaderInterceptor url_loader_interceptor; |
| // Initial visit in order to populate the cache. |
| { |
| PendingTaskWaiter waiter(web_contents()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| } |
| ASSERT_TRUE(url_loader_interceptor.was_loaded(icon_url)); |
| EXPECT_FALSE(url_loader_interceptor.did_bypass_cache(icon_url)); |
| url_loader_interceptor.Reset(); |
| |
| ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)); |
| |
| // A normal visit should fetch the favicon from either the favicon database or |
| // the HTTP cache. |
| { |
| PendingTaskWaiter waiter(web_contents()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| } |
| EXPECT_FALSE(url_loader_interceptor.did_bypass_cache(icon_url)); |
| url_loader_interceptor.Reset(); |
| |
| // A reload ignoring the cache should refetch the favicon from the website. |
| { |
| PendingTaskWaiter waiter(web_contents()); |
| chrome::ExecuteCommand(browser(), IDC_RELOAD_BYPASSING_CACHE); |
| waiter.Wait(); |
| } |
| ASSERT_TRUE(url_loader_interceptor.was_loaded(icon_url)); |
| EXPECT_TRUE(url_loader_interceptor.did_bypass_cache(icon_url)); |
| } |
| |
| // Test that favicon mappings are removed if the page initially lists a favicon |
| // and later uses Javascript to replace it with a touch icon. |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| ReplaceFaviconWithTouchIconViaJavascript) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/favicon/page_change_favicon_type_to_touch_icon_via_js.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireTitle(base::ASCIIToUTF16("OK")); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_EQ( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| } |
| |
| // Test that favicon changes via Javascript affecting a specific type (touch |
| // icons) do not influence the rest of the types (favicons). |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, ChangeTouchIconViaJavascript) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/favicon/page_change_touch_icon_via_js.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireTitle(base::ASCIIToUTF16("OK")); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| } |
| |
| // Test that favicon mappings are removed if the page initially lists a touch |
| // icon and later uses Javascript to remove it. |
| #if defined(OS_ANDROID) |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, RemoveTouchIconViaJavascript) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/favicon/page_change_favicon_type_to_favicon_via_js.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireTitle(base::ASCIIToUTF16("OK")); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_EQ(nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kTouchIcon) |
| .bitmap_data); |
| } |
| #endif |
| |
| // Test that favicon mappings are not removed if the page with favicons (cached |
| // in favicon database) is stopped while being loaded. More precisely, we test |
| // the stop event immediately following DidFinishNavigation(), before favicons |
| // have been processed in the HTML, because this is an example where Content |
| // reports an incomplete favicon list (or, like in this test, falls back to the |
| // default favicon.ico). |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, DoNotRemoveMappingIfStopped) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/favicon/slow_page_with_favicon.html"); |
| GURL icon_url = embedded_test_server()->GetURL("/favicon/icon.png"); |
| GURL default_icon_url = embedded_test_server()->GetURL("/favicon.ico"); |
| |
| // Prepopulate the favicon cache for the page (with synthetic content). |
| favicon_service()->SetFavicons({url}, icon_url, |
| favicon_base::IconType::kFavicon, |
| gfx::test::CreateImage(32, 32)); |
| ASSERT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| |
| // Stop the loading of the page as soon as DidFinishNaviation() is received, |
| // which should be during the load of the slow-loading script and before |
| // favicons are processed (verified in assertion below). |
| PageLoadStopper stopper(web_contents()); |
| stopper.StopOnDidFinishNavigation(); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| // The whole purpose of this test is due to the fact that Content returns a |
| // partial list in the scenario described above. If this behavior changes |
| // (e.g. if Content sent no favicon candidates), we could likely simplify some |
| // code (and remove this test). |
| ASSERT_THAT(stopper.last_favicon_candidates(), ElementsAre(default_icon_url)); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| } |
| |
| // Test that loading a page that contains icons only in the Web Manifest causes |
| // those icons to be used. |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, LoadIconFromWebManifest) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/favicon/page_with_manifest.html"); |
| GURL icon_url = embedded_test_server()->GetURL("/favicon/icon.png"); |
| |
| TestURLLoaderInterceptor url_loader_interceptor; |
| |
| PendingTaskWaiter waiter(web_contents()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| #if defined(OS_ANDROID) |
| EXPECT_TRUE(url_loader_interceptor.was_loaded(icon_url)); |
| EXPECT_NE(nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kWebManifestIcon) |
| .bitmap_data); |
| #else |
| EXPECT_FALSE(url_loader_interceptor.was_loaded(icon_url)); |
| #endif |
| } |
| |
| // Test that a page which uses a meta refresh tag to redirect gets associated |
| // to the favicons listed in the landing page. |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| AssociateIconWithInitialPageDespiteMetaRefreshTag) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/favicon/page_with_meta_refresh_tag.html"); |
| GURL landing_url = |
| embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(landing_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| EXPECT_NE(nullptr, |
| GetFaviconForPageURL(landing_url, favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| } |
| |
| // Test that a page which uses a meta refresh tag to redirect gets associated |
| // to the favicons listed in the landing page, for the case that the landing |
| // page has been visited earlier (favicon cached). Similar behavior is expected |
| // for server-side redirects although not covered in this test. |
| IN_PROC_BROWSER_TEST_F( |
| ContentFaviconDriverTest, |
| AssociateIconWithInitialPageDespiteMetaRefreshTagAndLandingPageCached) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_with_meta_refresh_tag = embedded_test_server()->GetURL( |
| "/favicon/page_with_meta_refresh_tag.html"); |
| GURL landing_url = |
| embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); |
| |
| // Initial visit in order to populate the cache. |
| { |
| PendingTaskWaiter waiter(web_contents()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), landing_url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| } |
| ASSERT_NE(nullptr, |
| GetFaviconForPageURL(landing_url, favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(landing_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url_with_meta_refresh_tag, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE(nullptr, GetFaviconForPageURL(url_with_meta_refresh_tag, |
| favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| EXPECT_NE(nullptr, |
| GetFaviconForPageURL(landing_url, favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| } |
| |
| // Test that a page gets a server-side redirect followed by a meta refresh tag |
| // gets associated to the favicons listed in the landing page. |
| IN_PROC_BROWSER_TEST_F( |
| ContentFaviconDriverTest, |
| AssociateIconWithInitialPageDespite300ResponseAndMetaRefreshTag) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_with_meta_refresh_tag = embedded_test_server()->GetURL( |
| "/favicon/page_with_meta_refresh_tag.html"); |
| GURL url = embedded_test_server()->GetURL("/server-redirect?" + |
| url_with_meta_refresh_tag.spec()); |
| GURL landing_url = |
| embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(landing_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| EXPECT_NE(nullptr, |
| GetFaviconForPageURL(landing_url, favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| } |
| |
| // Test that a page gets a server-side redirect, followed by a meta refresh tag, |
| // followed by a server-side redirect gets associated to the favicons listed in |
| // the landing page. |
| IN_PROC_BROWSER_TEST_F( |
| ContentFaviconDriverTest, |
| AssociateIconWithInitialPageDespite300ResponseAndMetaRefreshTagTo300) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_with_meta_refresh_tag = embedded_test_server()->GetURL( |
| "/favicon/page_with_meta_refresh_tag_to_server_redirect.html"); |
| GURL url = embedded_test_server()->GetURL("/server-redirect?" + |
| url_with_meta_refresh_tag.spec()); |
| GURL landing_url = |
| embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(landing_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| EXPECT_NE(nullptr, |
| GetFaviconForPageURL(landing_url, favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| } |
| |
| // Test that a page which uses JavaScript document.location.replace() to |
| // navigate within the page gets associated favicons. |
| IN_PROC_BROWSER_TEST_F( |
| ContentFaviconDriverTest, |
| AssociateIconWithInitialPageDespiteLocationOverrideWithinPage) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/favicon/page_with_location_override_within_page.html"); |
| GURL landing_url = embedded_test_server()->GetURL( |
| "/favicon/page_with_location_override_within_page.html#foo"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(landing_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| EXPECT_NE(nullptr, |
| GetFaviconForPageURL(landing_url, favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| } |
| |
| // Test that a page which uses JavaScript document.location.replace() to |
| // navigate to a different landing page gets associated favicons listed in the |
| // landing page. |
| IN_PROC_BROWSER_TEST_F( |
| ContentFaviconDriverTest, |
| AssociateIconWithInitialPageDespiteLocationOverrideToOtherPage) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/favicon/page_with_location_override_to_other_page.html"); |
| GURL landing_url = |
| embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(landing_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| EXPECT_NE(nullptr, |
| GetFaviconForPageURL(landing_url, favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| } |
| |
| // Test that a page which uses JavaScript's history.replaceState() to update |
| // the URL in the omnibox (and history) gets associated favicons. |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| AssociateIconWithInitialPageIconDespiteReplaceState) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/favicon/replacestate_with_favicon.html"); |
| GURL replacestate_url = embedded_test_server()->GetURL( |
| "/favicon/replacestate_with_favicon_replaced.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(replacestate_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| EXPECT_NE(nullptr, GetFaviconForPageURL(replacestate_url, |
| favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| } |
| |
| // Test that a page which uses JavaScript's history.pushState() to update |
| // the URL in the omnibox (and history) gets associated favicons. |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| AssociateIconWithInitialPageIconDespitePushState) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/favicon/pushstate_with_favicon.html"); |
| GURL pushstate_url = embedded_test_server()->GetURL( |
| "/favicon/pushstate_with_favicon_pushed.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(pushstate_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| EXPECT_NE(nullptr, GetFaviconForPageURL(pushstate_url, |
| favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| } |
| |
| // Test that a page which uses JavaScript to navigate to a different page |
| // by overriding window.location.href (similar behavior as clicking on a link) |
| // does *not* get associated favicons from the landing page. |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| DoNotAssociateIconWithInitialPageAfterHrefAssign) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/favicon/page_with_location_href_assign.html"); |
| GURL landing_url = |
| embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(landing_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| ASSERT_NE(nullptr, |
| GetFaviconForPageURL(landing_url, favicon_base::IconType::kFavicon) |
| .bitmap_data); |
| EXPECT_EQ( |
| nullptr, |
| GetFaviconForPageURL(url, favicon_base::IconType::kFavicon).bitmap_data); |
| } |
| |
| #if defined(OS_ANDROID) |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| LoadIconFromWebManifestDespitePushState) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/favicon/pushstate_with_manifest.html"); |
| GURL pushstate_url = embedded_test_server()->GetURL( |
| "/favicon/pushstate_with_manifest.html#pushState"); |
| |
| PendingTaskWaiter waiter(web_contents()); |
| waiter.AlsoRequireUrl(pushstate_url); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| waiter.Wait(); |
| |
| EXPECT_NE(nullptr, |
| GetFaviconForPageURL(pushstate_url, |
| {favicon_base::IconType::kWebManifestIcon}) |
| .bitmap_data); |
| } |
| #endif |
| |
| // Checks that a favicon loaded over HTTP is blocked on a secure page. |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| MixedContentInsecureFaviconBlocked) { |
| net::EmbeddedTestServer ssl_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| ssl_server.AddDefaultHandlers(base::FilePath(kDocRoot)); |
| ASSERT_TRUE(ssl_server.Start()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const GURL favicon_url = |
| embedded_test_server()->GetURL("example.test", "/favicon/icon.png"); |
| const GURL favicon_page = ssl_server.GetURL( |
| "example.test", |
| "/favicon/page_with_favicon_by_url.html?url=" + favicon_url.spec()); |
| |
| // Observe the message for a blocked favicon. |
| content::ConsoleObserverDelegate console_observer(web_contents(), |
| "*icon.png*"); |
| web_contents()->SetDelegate(&console_observer); |
| |
| // Observe if the favicon URL is requested. |
| TestURLLoaderInterceptor url_interceptor; |
| |
| PendingTaskWaiter waiter(web_contents()); |
| ui_test_utils::NavigateToURL(browser(), favicon_page); |
| console_observer.Wait(); |
| waiter.Wait(); |
| |
| EXPECT_TRUE( |
| base::MatchPattern(console_observer.message(), "*insecure favicon*")); |
| EXPECT_TRUE(base::MatchPattern(console_observer.message(), |
| "*request has been blocked*")); |
| EXPECT_FALSE(url_interceptor.was_loaded(favicon_url)); |
| } |
| |
| // Checks that a favicon loaded over HTTPS is allowed on a secure page. |
| IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, |
| MixedContentSecureFaviconAllowed) { |
| net::EmbeddedTestServer ssl_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| ssl_server.AddDefaultHandlers(base::FilePath(kDocRoot)); |
| ASSERT_TRUE(ssl_server.Start()); |
| |
| const GURL favicon_url = |
| ssl_server.GetURL("example.test", "/favicon/icon.png"); |
| const GURL favicon_page = ssl_server.GetURL( |
| "example.test", |
| "/favicon/page_with_favicon_by_url.html?url=" + favicon_url.spec()); |
| |
| // Observe if the favicon URL is requested. |
| TestURLLoaderInterceptor url_interceptor; |
| |
| PendingTaskWaiter waiter(web_contents()); |
| ui_test_utils::NavigateToURL(browser(), favicon_page); |
| waiter.Wait(); |
| |
| EXPECT_TRUE(url_interceptor.was_loaded(favicon_url)); |
| } |