blob: 07f3dbb7990529decff670f7d5bde54e4532a7db [file] [log] [blame]
// 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));
}