blob: a7ae1b516116f23e81262abb41d037853e295f3f [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/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "components/favicon/core/favicon_client.h"
#include "components/favicon/core/favicon_handler.h"
#include "components/favicon/core/test/favicon_driver_impl_test_helper.h"
#include "components/favicon/core/test/mock_favicon_service.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/navigation_simulator.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/blink/public/mojom/favicon/favicon_url.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/favicon_size.h"
namespace favicon {
namespace {
using testing::Return;
using testing::SizeIs;
using testing::_;
void TestFetchFaviconForPage(
content::WebContents* web_contents,
const GURL& page_url,
const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
ContentFaviconDriver* favicon_driver =
ContentFaviconDriver::FromWebContents(web_contents);
content::WebContentsTester::For(web_contents)->NavigateAndCommit(page_url);
static_cast<content::WebContentsObserver*>(favicon_driver)
->DidUpdateFaviconURL(web_contents->GetMainFrame(), candidates);
base::RunLoop().RunUntilIdle();
}
class ContentFaviconDriverTest : public content::RenderViewHostTestHarness {
protected:
const std::vector<gfx::Size> kEmptyIconSizes;
const std::vector<SkBitmap> kEmptyIcons;
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([](auto, auto, auto, auto,
favicon_base::FaviconResultsCallback callback,
base::CancelableTaskTracker* tracker) {
return tracker->PostTask(
base::ThreadTaskRunnerHandle::Get().get(), FROM_HERE,
base::BindOnce(
std::move(callback),
std::vector<favicon_base::FaviconRawBitmapResult>()));
});
ON_CALL(favicon_service_, GetFaviconForPageURL(_, _, _, _, _))
.WillByDefault([](auto, auto, auto,
favicon_base::FaviconResultsCallback callback,
base::CancelableTaskTracker* tracker) {
return tracker->PostTask(
base::ThreadTaskRunnerHandle::Get().get(), FROM_HERE,
base::BindOnce(
std::move(callback),
std::vector<favicon_base::FaviconRawBitmapResult>()));
});
}
~ContentFaviconDriverTest() override = default;
// content::RenderViewHostTestHarness:
void SetUp() override {
RenderViewHostTestHarness::SetUp();
ContentFaviconDriver::CreateForWebContents(web_contents(),
&favicon_service_);
}
content::WebContentsTester* web_contents_tester() {
return content::WebContentsTester::For(web_contents());
}
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.
std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
favicon_urls.push_back(blink::mojom::FaviconURL::New(
kIconURL, blink::mojom::FaviconIconType::kFavicon, kEmptyIconSizes));
TestFetchFaviconForPage(web_contents(), kPageURL, favicon_urls);
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());
auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
kPageURL, web_contents());
navigation->SetKeepLoading(true);
navigation->Commit();
std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
favicon_urls.push_back(blink::mojom::FaviconURL::New(
kIconURL, blink::mojom::FaviconIconType::kFavicon, kEmptyIconSizes));
static_cast<content::WebContentsObserver*>(favicon_driver)
->DidUpdateFaviconURL(web_contents()->GetMainFrame(), favicon_urls);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(web_contents_tester()->HasPendingDownloadImage(kIconURL));
// Nevertheless, we expect the list exposed via favicon_urls().
EXPECT_THAT(favicon_driver->favicon_urls(), SizeIs(1));
}
// 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.
std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
favicon_urls.push_back(blink::mojom::FaviconURL::New(
kIconURL, blink::mojom::FaviconIconType::kFavicon, kEmptyIconSizes));
TestFetchFaviconForPage(web_contents(), kPageURL, favicon_urls);
// 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.
std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
favicon_urls.push_back(blink::mojom::FaviconURL::New(
kIconURL, blink::mojom::FaviconIconType::kFavicon, kEmptyIconSizes));
favicon_urls.push_back(blink::mojom::FaviconURL::New(
kOtherIconURL, blink::mojom::FaviconIconType::kFavicon, kEmptyIconSizes));
TestFetchFaviconForPage(web_contents(), kPageURL, favicon_urls);
// 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<blink::mojom::FaviconURLPtr> favicon_urls;
favicon_urls.push_back(blink::mojom::FaviconURL::New(
GURL("http://www.google.ca/favicon.ico"),
blink::mojom::FaviconIconType::kFavicon, kEmptyIconSizes));
favicon::ContentFaviconDriver* driver =
favicon::ContentFaviconDriver::FromWebContents(web_contents());
static_cast<content::WebContentsObserver*>(driver)->DidUpdateFaviconURL(
web_contents()->GetMainFrame(), favicon_urls);
// Test that ContentFaviconDriver ignored the favicon url update.
EXPECT_TRUE(driver->favicon_urls().empty());
}
using ContentFaviconDriverTestNoFaviconService =
content::RenderViewHostTestHarness;
// This test verifies a crash doesn't happen during deletion of the
// WebContents. The crash occurred because ~WebContentsImpl would trigger
// running callbacks for manifests. This mean FaviconHandler would be called
// while ContentFaviconDriver::web_contents() was null, which is unexpected and
// crashed. See https://crbug.com/1114237 for more.
TEST_F(ContentFaviconDriverTestNoFaviconService,
WebContentsDeletedWithInProgressManifestRequest) {
ContentFaviconDriver::CreateForWebContents(web_contents(), nullptr);
// Manifests are only downloaded with TOUCH_LARGEST. Force creating this
// handler so code path is exercised on all platforms.
favicon::ContentFaviconDriver* driver =
favicon::ContentFaviconDriver::FromWebContents(web_contents());
FaviconDriverImplTestHelper::RecreateHandlerForType(
driver, FaviconDriverObserver::TOUCH_LARGEST);
// Mimic a page load.
std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
favicon_urls.push_back(blink::mojom::FaviconURL::New(
GURL("http://www.google.com/favicon.ico"),
blink::mojom::FaviconIconType::kTouchIcon, std::vector<gfx::Size>()));
TestFetchFaviconForPage(web_contents(), GURL("http://www.google.com/"),
favicon_urls);
// Trigger downloading a manifest.
static_cast<content::WebContentsObserver*>(driver)->DidUpdateWebManifestURL(
web_contents()->GetMainFrame(), GURL("http://bad.manifest.com"));
// The request for the manifest is still pending, delete the WebContents,
// which should trigger notifying the callback for the manifest and *not*
// crash.
DeleteContents();
}
} // namespace
} // namespace favicon