blob: 6dcc190d3ad414d18bc5d3cc299e56c3d148f4aa [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 <memory>
#include <vector>
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/histogram_tester.h"
#include "components/favicon/core/favicon_client.h"
#include "components/favicon/core/favicon_handler.h"
#include "components/favicon/core/test/mock_favicon_service.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/favicon_url.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/favicon_size.h"
namespace favicon {
namespace {
using testing::ElementsAre;
using testing::Return;
using testing::_;
class ContentFaviconDriverTest : public content::RenderViewHostTestHarness {
protected:
const std::vector<gfx::Size> kEmptyIconSizes;
const std::vector<SkBitmap> kEmptyIcons;
const std::vector<favicon_base::FaviconRawBitmapResult> kEmptyRawBitmapResult;
const GURL kPageURL = GURL("http://www.google.com/");
const GURL kIconURL = GURL("http://www.google.com/favicon.ico");
ContentFaviconDriverTest() {
ON_CALL(favicon_service_, UpdateFaviconMappingsAndFetch(_, _, _, _, _, _))
.WillByDefault(PostReply<6>(kEmptyRawBitmapResult));
ON_CALL(favicon_service_, GetFaviconForPageURL(_, _, _, _, _))
.WillByDefault(PostReply<5>(kEmptyRawBitmapResult));
}
~ContentFaviconDriverTest() override {}
// content::RenderViewHostTestHarness:
void SetUp() override {
RenderViewHostTestHarness::SetUp();
ContentFaviconDriver::CreateForWebContents(web_contents(),
&favicon_service_, nullptr);
}
content::WebContentsTester* web_contents_tester() {
return content::WebContentsTester::For(web_contents());
}
void TestFetchFaviconForPage(
const GURL& page_url,
const std::vector<content::FaviconURL>& candidates) {
ContentFaviconDriver* favicon_driver =
ContentFaviconDriver::FromWebContents(web_contents());
web_contents_tester()->NavigateAndCommit(page_url);
static_cast<content::WebContentsObserver*>(favicon_driver)
->DocumentOnLoadCompletedInMainFrame();
static_cast<content::WebContentsObserver*>(favicon_driver)
->DidUpdateFaviconURL(candidates);
base::RunLoop().RunUntilIdle();
}
testing::NiceMock<MockFaviconService> favicon_service_;
};
// Test that a download is initiated when there isn't a favicon in the database
// for either the page URL or the icon URL.
TEST_F(ContentFaviconDriverTest, ShouldCauseImageDownload) {
// Mimic a page load.
TestFetchFaviconForPage(
kPageURL,
{content::FaviconURL(kIconURL, content::FaviconURL::IconType::kFavicon,
kEmptyIconSizes)});
EXPECT_TRUE(web_contents_tester()->TestDidDownloadImage(
kIconURL, 200, kEmptyIcons, kEmptyIconSizes));
}
// Test that no download is initiated when DocumentOnLoadCompletedInMainFrame()
// is not triggered (e.g. user stopped an ongoing page load).
TEST_F(ContentFaviconDriverTest, ShouldNotCauseImageDownload) {
ContentFaviconDriver* favicon_driver =
ContentFaviconDriver::FromWebContents(web_contents());
web_contents_tester()->NavigateAndCommit(kPageURL);
static_cast<content::WebContentsObserver*>(favicon_driver)
->DidUpdateFaviconURL({content::FaviconURL(
kIconURL, content::FaviconURL::IconType::kFavicon, kEmptyIconSizes)});
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(web_contents_tester()->HasPendingDownloadImage(kIconURL));
}
// Test that Favicon is not requested repeatedly during the same session if
// the favicon is known to be unavailable (e.g. due to HTTP 404 status).
TEST_F(ContentFaviconDriverTest, ShouldNotRequestRepeatedlyIfUnavailable) {
ON_CALL(favicon_service_, WasUnableToDownloadFavicon(kIconURL))
.WillByDefault(Return(true));
// Mimic a page load.
TestFetchFaviconForPage(
kPageURL,
{content::FaviconURL(kIconURL, content::FaviconURL::IconType::kFavicon,
kEmptyIconSizes)});
// Verify that no download request is pending for the image.
EXPECT_FALSE(web_contents_tester()->HasPendingDownloadImage(kIconURL));
}
TEST_F(ContentFaviconDriverTest, ShouldDownloadSecondIfFirstUnavailable) {
const GURL kOtherIconURL = GURL("http://www.google.com/other-favicon.ico");
ON_CALL(favicon_service_, WasUnableToDownloadFavicon(kIconURL))
.WillByDefault(Return(true));
// Mimic a page load.
TestFetchFaviconForPage(
kPageURL,
{content::FaviconURL(kIconURL, content::FaviconURL::IconType::kFavicon,
kEmptyIconSizes),
content::FaviconURL(kOtherIconURL,
content::FaviconURL::IconType::kFavicon,
kEmptyIconSizes)});
// Verify a download request is pending for the second image.
EXPECT_FALSE(web_contents_tester()->HasPendingDownloadImage(kIconURL));
EXPECT_TRUE(web_contents_tester()->HasPendingDownloadImage(kOtherIconURL));
}
// Test that ContentFaviconDriver ignores updated favicon URLs if there is no
// last committed entry. This occurs when script is injected in about:blank.
// See crbug.com/520759 for more details
TEST_F(ContentFaviconDriverTest, FaviconUpdateNoLastCommittedEntry) {
ASSERT_EQ(nullptr, web_contents()->GetController().GetLastCommittedEntry());
std::vector<content::FaviconURL> favicon_urls;
favicon_urls.push_back(content::FaviconURL(
GURL("http://www.google.ca/favicon.ico"),
content::FaviconURL::IconType::kFavicon, kEmptyIconSizes));
favicon::ContentFaviconDriver* driver =
favicon::ContentFaviconDriver::FromWebContents(web_contents());
static_cast<content::WebContentsObserver*>(driver)
->DidUpdateFaviconURL(favicon_urls);
// Test that ContentFaviconDriver ignored the favicon url update.
EXPECT_TRUE(driver->favicon_urls().empty());
}
TEST_F(ContentFaviconDriverTest, RecordsHistorgramsForCandidates) {
const std::vector<gfx::Size> kSizes16x16and32x32({{16, 16}, {32, 32}});
base::HistogramTester tester;
// Navigation to a page updating one icon.
TestFetchFaviconForPage(
GURL("http://www.youtube.com"),
{content::FaviconURL(GURL("http://www.youtube.com/favicon.ico"),
content::FaviconURL::IconType::kFavicon,
kSizes16x16and32x32)});
EXPECT_THAT(tester.GetAllSamples("Favicons.CandidatesCount"),
ElementsAre(base::Bucket(/*min=*/1, /*count=*/1)));
EXPECT_THAT(tester.GetAllSamples("Favicons.CandidatesWithDefinedSizesCount"),
ElementsAre(base::Bucket(/*min=*/1, /*count=*/1)));
EXPECT_THAT(tester.GetAllSamples("Favicons.CandidatesWithTouchIconsCount"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/1)));
std::vector<content::FaviconURL> favicon_urls = {
content::FaviconURL(GURL("http://www.google.ca/favicon.ico"),
content::FaviconURL::IconType::kFavicon,
kSizes16x16and32x32),
content::FaviconURL(GURL("http://www.google.ca/precomposed_icon.png"),
content::FaviconURL::IconType::kTouchPrecomposedIcon,
kEmptyIconSizes),
content::FaviconURL(GURL("http://www.google.ca/touch_icon.png"),
content::FaviconURL::IconType::kTouchIcon,
kEmptyIconSizes)};
// Double navigation to a page with 3 different icons.
TestFetchFaviconForPage(GURL("http://www.google.ca"), favicon_urls);
TestFetchFaviconForPage(GURL("http://www.google.ca"), favicon_urls);
EXPECT_THAT(tester.GetAllSamples("Favicons.CandidatesCount"),
ElementsAre(base::Bucket(/*min=*/1, /*count=*/1),
base::Bucket(/*min=*/3, /*count=*/2)));
EXPECT_THAT(tester.GetAllSamples("Favicons.CandidatesWithDefinedSizesCount"),
ElementsAre(base::Bucket(/*min=*/1, /*count=*/3)));
EXPECT_THAT(tester.GetAllSamples("Favicons.CandidatesWithTouchIconsCount"),
ElementsAre(base::Bucket(/*min=*/0, /*count=*/1),
base::Bucket(/*min=*/2, /*count=*/2)));
}
} // namespace
} // namespace favicon