Commit 948e7a88 authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

weblayer: initial cut at favicon support (C++ side only)

There are still some TODOS (most notably incognito is not supported),
but this can largely be used outside of that.

Embedder usage is as follows:
. embedders interested in favicons call Tab::CreateFaviconFetcher() supplying
  a delegate. At this time embedders don't get the ability to specify a
  size (I wouldn't be surprised if we need this later). The actual size
  requested depends upon the platform.
. The delegate is notified any time the favicon of the current navigation
  changes (not including when a new navigation starts).
. The current favicon can be requested from the FaviconFetcher returned from
  CreateFaviconFetcher.

Implementation wise the following pieces are used:
. FaviconServiceImpl: implementation of favicon::CoreFaviconService that
  is used on the main thread to interact with WebLayerFaviconBackend.
. WebLayerFaviconBackend: owns the database. All methods largely delegate to
  the database (same database used in chrome).
. FaviconTabHelper: responsible for creating favicon::ContentFaviconDriver
  as necessary. ContentFaviconDriver is ultimately responsible for getting
  the favicon for the current navigation. ContentFaviconDriver is only
  created when necessary (meaning CreateFaviconFetcher() was called) as it
  actively downloads favicons.
. FaviconFetcherImpl: created via Tab::CreateFaviconFetcher(), uses
  FaviconTabHelper to handle notifying delegate and maintaining favicon.

BUG=1076463
TEST=covered by weblayer_browsertests

Change-Id: I74bce59a6a46c38000b37b028a2d3e7797680e33
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2337275
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarEvan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#795110}
parent a1fee262
......@@ -171,6 +171,17 @@ source_set("weblayer_lib_base") {
"browser/download_impl.h",
"browser/download_manager_delegate_impl.cc",
"browser/download_manager_delegate_impl.h",
"browser/favicon/favicon_backend_wrapper.cc",
"browser/favicon/favicon_backend_wrapper.h",
"browser/favicon/favicon_fetcher_impl.cc",
"browser/favicon/favicon_fetcher_impl.h",
"browser/favicon/favicon_service_impl.cc",
"browser/favicon/favicon_service_impl.h",
"browser/favicon/favicon_service_impl_factory.cc",
"browser/favicon/favicon_service_impl_factory.h",
"browser/favicon/favicon_service_impl_observer.h",
"browser/favicon/favicon_tab_helper.cc",
"browser/favicon/favicon_tab_helper.h",
"browser/feature_list_creator.cc",
"browser/feature_list_creator.h",
"browser/file_select_helper.cc",
......@@ -280,6 +291,8 @@ source_set("weblayer_lib_base") {
"public/download_delegate.h",
"public/error_page.h",
"public/error_page_delegate.h",
"public/favicon_fetcher.h",
"public/favicon_fetcher_delegate.h",
"public/fullscreen_delegate.h",
"public/google_accounts_delegate.h",
"public/js_communication/web_message.cc",
......@@ -344,6 +357,10 @@ source_set("weblayer_lib_base") {
"//components/embedder_support/origin_trials",
"//components/error_page/common",
"//components/error_page/content/browser",
"//components/favicon/content",
"//components/favicon/core",
"//components/favicon/core:database",
"//components/favicon_base",
"//components/find_in_page",
"//components/infobars/core",
"//components/js_injection/browser",
......
......@@ -19,6 +19,8 @@ include_rules = [
"+components/download/public/common",
"+components/embedder_support",
"+components/error_page/content/browser",
"+components/favicon_base",
"+components/favicon",
"+components/find_in_page",
"+components/infobars/android",
"+components/infobars/content",
......@@ -75,6 +77,7 @@ include_rules = [
"+storage/browser/quota",
"+third_party/blink/public/common",
"+third_party/blink/public/mojom",
"+third_party/skia",
"+ui/aura",
"+ui/android",
"+ui/base",
......
// Copyright 2020 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 "weblayer/browser/favicon/favicon_backend_wrapper.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/sequenced_task_runner.h"
#include "components/favicon/core/favicon_backend.h"
#include "components/favicon/core/favicon_database.h"
namespace weblayer {
FaviconBackendWrapper::FaviconBackendWrapper(
scoped_refptr<base::SequencedTaskRunner> task_runner)
: base::RefCountedDeleteOnSequence<FaviconBackendWrapper>(task_runner),
task_runner_(task_runner) {}
void FaviconBackendWrapper::Init(const base::FilePath& db_path) {
favicon_backend_ = favicon::FaviconBackend::Create(db_path, this);
if (!favicon_backend_) {
LOG(WARNING) << "Could not initialize the favicon database.";
// The favicon db is not critical. On failure initializing try deleting
// the file and repeating. Note that FaviconDatabase already tries to
// initialize twice.
base::DeleteFile(db_path);
favicon_backend_ = favicon::FaviconBackend::Create(db_path, this);
if (!favicon_backend_) {
LOG(WARNING) << "Could not initialize db second time, giving up.";
return;
}
}
}
void FaviconBackendWrapper::Shutdown() {
// Ensures there isn't a reference to this in the task runner (by way of the
// task the timer posts).
commit_timer_.Stop();
}
std::vector<favicon_base::FaviconRawBitmapResult>
FaviconBackendWrapper::GetFaviconsForUrl(
const GURL& page_url,
const favicon_base::IconTypeSet& icon_types,
const std::vector<int>& desired_sizes) {
if (!favicon_backend_)
return {};
return favicon_backend_->GetFaviconsForUrl(page_url, icon_types,
desired_sizes,
/* fallback_to_host */ false);
}
void FaviconBackendWrapper::SetFaviconsOutOfDateForPage(const GURL& page_url) {
if (favicon_backend_)
favicon_backend_->SetFaviconsOutOfDateForPage(page_url);
}
void FaviconBackendWrapper::SetFavicons(const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps) {
if (favicon_backend_) {
favicon_backend_->SetFavicons(page_urls, icon_type, icon_url, bitmaps,
favicon::FaviconBitmapType::ON_VISIT);
}
}
void FaviconBackendWrapper::CloneFaviconMappingsForPages(
const GURL& page_url_to_read,
const favicon_base::IconTypeSet& icon_types,
const base::flat_set<GURL>& page_urls_to_write) {
if (!favicon_backend_)
return;
std::set<GURL> changed_urls = favicon_backend_->CloneFaviconMappingsForPages(
{page_url_to_read}, icon_types, page_urls_to_write);
if (!changed_urls.empty())
ScheduleCommitForFavicons();
}
std::vector<favicon_base::FaviconRawBitmapResult>
FaviconBackendWrapper::GetFavicon(const GURL& icon_url,
favicon_base::IconType icon_type,
const std::vector<int>& desired_sizes) {
return UpdateFaviconMappingsAndFetch({}, icon_url, icon_type, desired_sizes);
}
std::vector<favicon_base::FaviconRawBitmapResult>
FaviconBackendWrapper::UpdateFaviconMappingsAndFetch(
const base::flat_set<GURL>& page_urls,
const GURL& icon_url,
favicon_base::IconType icon_type,
const std::vector<int>& desired_sizes) {
if (!favicon_backend_)
return {};
return favicon_backend_->UpdateFaviconMappingsAndFetch(
page_urls, icon_url, icon_type, desired_sizes);
}
void FaviconBackendWrapper::DeleteFaviconMappings(
const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type) {
if (!favicon_backend_)
favicon_backend_->DeleteFaviconMappings(page_urls, icon_type);
}
void FaviconBackendWrapper::ScheduleCommitForFavicons() {
if (!commit_timer_.IsRunning()) {
// 10 seconds matches that of HistoryBackend.
commit_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(10), this,
&FaviconBackendWrapper::Commit);
}
}
std::vector<GURL> FaviconBackendWrapper::GetCachedRecentRedirectsForPage(
const GURL& page_url) {
// By only returning |page_url| this code won't set the favicon on redirects.
// If that becomes necessary, we would need this class to know about
// redirects. Chrome does this by way of HistoryService remembering redirects
// for recent pages. See |HistoryBackend::recent_redirects_|.
return {page_url};
}
void FaviconBackendWrapper::OnFaviconChangedForPageAndRedirects(
const GURL& page_url) {
// Nothing to do here as WebLayer doesn't notify of favicon changes through
// this code path.
}
FaviconBackendWrapper::~FaviconBackendWrapper() = default;
void FaviconBackendWrapper::Commit() {
if (favicon_backend_)
favicon_backend_->Commit();
}
} // namespace weblayer
// Copyright 2020 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.
#ifndef WEBLAYER_BROWSER_FAVICON_FAVICON_BACKEND_WRAPPER_H_
#define WEBLAYER_BROWSER_FAVICON_FAVICON_BACKEND_WRAPPER_H_
#include <memory>
#include <vector>
#include "base/memory/ref_counted.h"
#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/timer/timer.h"
#include "components/favicon/core/favicon_backend_delegate.h"
#include "components/favicon_base/favicon_types.h"
class GURL;
namespace base {
class FilePath;
class SequencedTaskRunner;
} // namespace base
namespace favicon {
class FaviconBackend;
}
namespace weblayer {
// FaviconBackendWrapper runs on a background task-runner and owns the database
// side of favicons. This class largely delegates to favicon::FaviconBackend
// and has very little logic.
class FaviconBackendWrapper
: public base::RefCountedDeleteOnSequence<FaviconBackendWrapper>,
public favicon::FaviconBackendDelegate {
public:
explicit FaviconBackendWrapper(
scoped_refptr<base::SequencedTaskRunner> task_runner);
FaviconBackendWrapper(const FaviconBackendWrapper&) = delete;
FaviconBackendWrapper& operator=(const FaviconBackendWrapper&) = delete;
void Init(const base::FilePath& db_path);
void Shutdown();
// All of these functions are called by the FaviconServiceImpl. They call
// through to |favicon_backend_|.
std::vector<favicon_base::FaviconRawBitmapResult> GetFaviconsForUrl(
const GURL& page_url,
const favicon_base::IconTypeSet& icon_types,
const std::vector<int>& desired_sizes);
void SetFaviconsOutOfDateForPage(const GURL& page_url);
void SetFavicons(const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type,
const GURL& icon_url,
const std::vector<SkBitmap>& bitmaps);
void CloneFaviconMappingsForPages(
const GURL& page_url_to_read,
const favicon_base::IconTypeSet& icon_types,
const base::flat_set<GURL>& page_urls_to_write);
std::vector<favicon_base::FaviconRawBitmapResult> GetFavicon(
const GURL& icon_url,
favicon_base::IconType icon_type,
const std::vector<int>& desired_sizes);
std::vector<favicon_base::FaviconRawBitmapResult>
UpdateFaviconMappingsAndFetch(const base::flat_set<GURL>& page_urls,
const GURL& icon_url,
favicon_base::IconType icon_type,
const std::vector<int>& desired_sizes);
void DeleteFaviconMappings(const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type);
// favicon::FaviconBackendDelegate:
void ScheduleCommitForFavicons() override;
std::vector<GURL> GetCachedRecentRedirectsForPage(
const GURL& page_url) override;
void OnFaviconChangedForPageAndRedirects(const GURL& page_url) override;
private:
friend class base::RefCountedDeleteOnSequence<FaviconBackendWrapper>;
friend class base::DeleteHelper<FaviconBackendWrapper>;
~FaviconBackendWrapper() override;
void Commit();
scoped_refptr<base::SequencedTaskRunner> task_runner_;
// Timer used to delay commits for a short amount of time. This done to
// batch commits.
base::OneShotTimer commit_timer_;
// The real implementation of the backend. Is there is a problem initializing
// the database this will be null.
std::unique_ptr<favicon::FaviconBackend> favicon_backend_;
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_BACKEND_WRAPPER_H_
// Copyright 2020 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 "weblayer/browser/favicon/favicon_fetcher_impl.h"
#include "base/run_loop.h"
#include "components/favicon/content/content_favicon_driver.h"
#include "ui/gfx/image/image.h"
#include "weblayer/browser/favicon/favicon_fetcher_impl.h"
#include "weblayer/browser/favicon/favicon_service_impl.h"
#include "weblayer/browser/favicon/favicon_service_impl_factory.h"
#include "weblayer/browser/favicon/favicon_service_impl_observer.h"
#include "weblayer/browser/tab_impl.h"
#include "weblayer/public/favicon_fetcher_delegate.h"
#include "weblayer/public/navigation_controller.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/test_navigation_observer.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
namespace weblayer {
namespace {
// Records calls to OnFaviconChanged().
class FaviconFetcherDelegateImpl : public FaviconFetcherDelegate {
public:
void WaitForFavicon() {
ASSERT_EQ(nullptr, run_loop_.get());
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
run_loop_.reset();
}
void ClearLastImage() {
last_image_ = gfx::Image();
on_favicon_changed_call_count_ = 0;
}
const gfx::Image& last_image() const { return last_image_; }
int on_favicon_changed_call_count() const {
return on_favicon_changed_call_count_;
}
// FaviconFetcherDelegate:
void OnFaviconChanged(const gfx::Image& image) override {
last_image_ = image;
++on_favicon_changed_call_count_;
if (run_loop_)
run_loop_->Quit();
}
private:
std::unique_ptr<base::RunLoop> run_loop_;
gfx::Image last_image_;
int on_favicon_changed_call_count_ = 0;
};
// FaviconServiceImplObserver used to wait for download to fail.
class TestFaviconServiceImplObserver : public FaviconServiceImplObserver {
public:
void Wait() {
ASSERT_EQ(nullptr, run_loop_.get());
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
run_loop_.reset();
}
// FaviconServiceImplObserver:
void OnUnableToDownloadFavicon() override {
if (run_loop_)
run_loop_->Quit();
}
private:
std::unique_ptr<base::RunLoop> run_loop_;
};
} // namespace
using FaviconFetcherBrowserTest = WebLayerBrowserTest;
IN_PROC_BROWSER_TEST_F(FaviconFetcherBrowserTest, Basic) {
ASSERT_TRUE(embedded_test_server()->Start());
FaviconFetcherDelegateImpl fetcher_delegate;
auto fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate);
NavigateAndWaitForCompletion(
embedded_test_server()->GetURL("/simple_page_with_favicon.html"),
shell());
fetcher_delegate.WaitForFavicon();
EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty());
EXPECT_EQ(fetcher_delegate.last_image(), fetcher->GetFavicon());
EXPECT_EQ(1, fetcher_delegate.on_favicon_changed_call_count());
fetcher_delegate.ClearLastImage();
const GURL url2 =
embedded_test_server()->GetURL("/simple_page_with_favicon2.html");
shell()->tab()->GetNavigationController()->Navigate(url2);
// Favicon doesn't change immediately on navigation.
EXPECT_FALSE(fetcher->GetFavicon().IsEmpty());
// Favicon does change once start is received.
TestNavigationObserver test_observer(
url2, TestNavigationObserver::NavigationEvent::kStart, shell());
test_observer.Wait();
EXPECT_TRUE(fetcher_delegate.last_image().IsEmpty());
// Wait for new favicon.
fetcher_delegate.WaitForFavicon();
EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty());
EXPECT_EQ(fetcher_delegate.last_image(), fetcher->GetFavicon());
EXPECT_EQ(1, fetcher_delegate.on_favicon_changed_call_count());
}
IN_PROC_BROWSER_TEST_F(FaviconFetcherBrowserTest, NavigateToPageWithNoFavicon) {
ASSERT_TRUE(embedded_test_server()->Start());
FaviconFetcherDelegateImpl fetcher_delegate;
auto fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate);
NavigateAndWaitForCompletion(
embedded_test_server()->GetURL("/simple_page_with_favicon.html"),
shell());
fetcher_delegate.WaitForFavicon();
fetcher_delegate.ClearLastImage();
TestFaviconServiceImplObserver test_observer;
FaviconServiceImplFactory::GetForProfile(
static_cast<TabImpl*>(shell()->tab())->profile())
->set_observer(&test_observer);
const GURL url2 = embedded_test_server()->GetURL("/simple_page.html");
shell()->tab()->GetNavigationController()->Navigate(url2);
EXPECT_TRUE(fetcher_delegate.last_image().IsEmpty());
// Wait for the image load to fail.
test_observer.Wait();
EXPECT_TRUE(fetcher_delegate.last_image().IsEmpty());
EXPECT_EQ(0, fetcher_delegate.on_favicon_changed_call_count());
}
IN_PROC_BROWSER_TEST_F(FaviconFetcherBrowserTest,
ContentFaviconDriverLifetime) {
ASSERT_TRUE(embedded_test_server()->Start());
content::WebContents* web_contents =
static_cast<TabImpl*>(shell()->tab())->web_contents();
// Initially there should be no driver (because favicons haven't been
// requested).
EXPECT_EQ(nullptr,
favicon::ContentFaviconDriver::FromWebContents(web_contents));
// Request a fetcher, which should trigger creating ContentFaviconDriver.
FaviconFetcherDelegateImpl fetcher_delegate;
auto fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate);
EXPECT_NE(nullptr,
favicon::ContentFaviconDriver::FromWebContents(web_contents));
// Destroy the fetcher, which should destroy ContentFaviconDriver.
fetcher.reset();
EXPECT_EQ(nullptr,
favicon::ContentFaviconDriver::FromWebContents(web_contents));
// One more time, and this time navigate.
fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate);
NavigateAndWaitForCompletion(
embedded_test_server()->GetURL("/simple_page_with_favicon.html"),
shell());
fetcher_delegate.WaitForFavicon();
EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty());
}
} // namespace weblayer
// Copyright 2020 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 "weblayer/browser/favicon/favicon_fetcher_impl.h"
#include "ui/gfx/image/image.h"
#include "weblayer/browser/favicon/favicon_tab_helper.h"
#include "weblayer/public/favicon_fetcher_delegate.h"
#include "base/logging.h"
namespace weblayer {
FaviconFetcherImpl::FaviconFetcherImpl(content::WebContents* web_contents,
FaviconFetcherDelegate* delegate)
: web_contents_(web_contents),
observer_subscription_(FaviconTabHelper::FromWebContents(web_contents)
->RegisterFaviconFetcherDelegate(delegate)) {}
FaviconFetcherImpl::~FaviconFetcherImpl() = default;
gfx::Image FaviconFetcherImpl::GetFavicon() {
return FaviconTabHelper::FromWebContents(web_contents_)->favicon();
}
} // namespace weblayer
// Copyright 2020 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.
#ifndef WEBLAYER_BROWSER_FAVICON_FAVICON_FETCHER_IMPL_H_
#define WEBLAYER_BROWSER_FAVICON_FAVICON_FETCHER_IMPL_H_
#include <memory>
#include "weblayer/browser/favicon/favicon_tab_helper.h"
#include "weblayer/public/favicon_fetcher.h"
namespace content {
class WebContents;
}
namespace weblayer {
class FaviconFetcherDelegate;
// FaviconFetcher implementation that largely delegates to FaviconTabHelper
// for the real implementation.
class FaviconFetcherImpl : public FaviconFetcher {
public:
FaviconFetcherImpl(content::WebContents* web_contents,
FaviconFetcherDelegate* delegate);
FaviconFetcherImpl(const FaviconFetcherImpl&) = delete;
FaviconFetcherImpl& operator=(const FaviconFetcherImpl&) = delete;
~FaviconFetcherImpl() override;
// FaviconFetcher:
gfx::Image GetFavicon() override;
private:
content::WebContents* web_contents_;
std::unique_ptr<FaviconTabHelper::ObserverSubscription>
observer_subscription_;
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_FETCHER_IMPL_H_
// Copyright 2020 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 "weblayer/browser/favicon/favicon_service_impl.h"
#include <stddef.h>
#include <vector>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/hash/hash.h"
#include "base/stl_util.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "components/favicon_base/favicon_util.h"
#include "components/favicon_base/select_favicon_frames.h"
#include "content/public/common/url_constants.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
#include "weblayer/browser/favicon/favicon_backend_wrapper.h"
#include "weblayer/browser/favicon/favicon_service_impl_observer.h"
namespace weblayer {
namespace {
// TODO(sky): refactor this to common place (also in
// favicon::FaviconServiceImpl).
// Returns a vector of pixel edge sizes from |size_in_dip| and
// favicon_base::GetFaviconScales().
std::vector<int> GetPixelSizesForFaviconScales(int size_in_dip) {
// NOTE: GetFaviconScales() always returns 1x on android.
std::vector<float> scales = favicon_base::GetFaviconScales();
std::vector<int> sizes_in_pixel;
for (float scale : scales)
sizes_in_pixel.push_back(std::ceil(size_in_dip * scale));
return sizes_in_pixel;
}
// TODO(sky): refactor this to common place (also in
// favicon::FaviconServiceImpl).
std::vector<SkBitmap> ExtractSkBitmapsToStore(const gfx::Image& image) {
gfx::ImageSkia image_skia = image.AsImageSkia();
image_skia.EnsureRepsForSupportedScales();
std::vector<SkBitmap> bitmaps;
const std::vector<float> favicon_scales = favicon_base::GetFaviconScales();
for (const gfx::ImageSkiaRep& rep : image_skia.image_reps()) {
// Don't save if the scale isn't one of supported favicon scales.
if (!base::Contains(favicon_scales, rep.scale()))
continue;
bitmaps.push_back(rep.GetBitmap());
}
return bitmaps;
}
bool CanAddUrl(const GURL& url) {
if (!url.is_valid())
return false;
if (url.SchemeIs(url::kJavaScriptScheme) || url.SchemeIs(url::kAboutScheme) ||
url.SchemeIs(url::kContentScheme) ||
url.SchemeIs(content::kChromeDevToolsScheme) ||
url.SchemeIs(content::kChromeUIScheme) ||
url.SchemeIs(content::kViewSourceScheme)) {
return false;
}
return true;
}
} // namespace
FaviconServiceImpl::FaviconServiceImpl() = default;
FaviconServiceImpl::~FaviconServiceImpl() {
backend_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FaviconBackendWrapper::Shutdown, std::move(backend_)));
}
void FaviconServiceImpl::Init(const base::FilePath& db_path) {
if (!backend_task_runner_) {
// BLOCK_SHUTDOWN matches that of HistoryService. It's done in hopes of
// preventing database corruption.
backend_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::WithBaseSyncPrimitives(),
base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
}
backend_ = base::MakeRefCounted<FaviconBackendWrapper>(backend_task_runner_);
backend_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FaviconBackendWrapper::Init, backend_, db_path));
}
base::CancelableTaskTracker::TaskId FaviconServiceImpl::GetFaviconForPageURL(
const GURL& page_url,
const favicon_base::IconTypeSet& icon_types,
int desired_size_in_dip,
favicon_base::FaviconResultsCallback callback,
base::CancelableTaskTracker* tracker) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return tracker->PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&FaviconBackendWrapper::GetFaviconsForUrl, backend_,
page_url, icon_types,
GetPixelSizesForFaviconScales(desired_size_in_dip)),
std::move(callback));
}
void FaviconServiceImpl::SetFaviconOutOfDateForPage(const GURL& page_url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
backend_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FaviconBackendWrapper::SetFaviconsOutOfDateForPage,
backend_, page_url));
}
void FaviconServiceImpl::SetFavicons(const base::flat_set<GURL>& page_urls,
const GURL& icon_url,
favicon_base::IconType icon_type,
const gfx::Image& image) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::flat_set<GURL> page_urls_to_save;
page_urls_to_save.reserve(page_urls.capacity());
for (const GURL& page_url : page_urls) {
if (CanAddUrl(page_url))
page_urls_to_save.insert(page_url);
}
if (page_urls_to_save.empty())
return;
backend_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&FaviconBackendWrapper::SetFavicons, backend_,
page_urls_to_save, icon_type, icon_url,
ExtractSkBitmapsToStore(image)));
}
void FaviconServiceImpl::CloneFaviconMappingsForPages(
const GURL& page_url_to_read,
const favicon_base::IconTypeSet& icon_types,
const base::flat_set<GURL>& page_urls_to_write) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
backend_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&FaviconBackendWrapper::CloneFaviconMappingsForPages,
backend_, page_url_to_read, icon_types,
page_urls_to_write));
}
base::CancelableTaskTracker::TaskId FaviconServiceImpl::GetFavicon(
const GURL& icon_url,
favicon_base::IconType icon_type,
int desired_size_in_dip,
favicon_base::FaviconResultsCallback callback,
base::CancelableTaskTracker* tracker) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return tracker->PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&FaviconBackendWrapper::GetFavicon, backend_, icon_url,
icon_type,
GetPixelSizesForFaviconScales(desired_size_in_dip)),
std::move(callback));
}
base::CancelableTaskTracker::TaskId
FaviconServiceImpl::UpdateFaviconMappingsAndFetch(
const base::flat_set<GURL>& page_urls,
const GURL& icon_url,
favicon_base::IconType icon_type,
int desired_size_in_dip,
favicon_base::FaviconResultsCallback callback,
base::CancelableTaskTracker* tracker) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return tracker->PostTaskAndReplyWithResult(
backend_task_runner_.get(), FROM_HERE,
base::BindOnce(&FaviconBackendWrapper::UpdateFaviconMappingsAndFetch,
backend_, page_urls, icon_url, icon_type,
GetPixelSizesForFaviconScales(desired_size_in_dip)),
std::move(callback));
}
void FaviconServiceImpl::DeleteFaviconMappings(
const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
backend_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&FaviconBackendWrapper::DeleteFaviconMappings,
backend_, page_urls, icon_type));
}
void FaviconServiceImpl::UnableToDownloadFavicon(const GURL& icon_url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
missing_favicon_urls_.insert(base::FastHash(icon_url.spec()));
if (observer_)
observer_->OnUnableToDownloadFavicon();
}
void FaviconServiceImpl::ClearUnableToDownloadFavicons() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
missing_favicon_urls_.clear();
}
bool FaviconServiceImpl::WasUnableToDownloadFavicon(
const GURL& icon_url) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
MissingFaviconUrlHash url_hash = base::FastHash(icon_url.spec());
return missing_favicon_urls_.find(url_hash) != missing_favicon_urls_.end();
}
} // namespace weblayer
// Copyright 2020 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.
#ifndef WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_H_
#define WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_H_
#include <unordered_set>
#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "components/favicon/core/core_favicon_service.h"
namespace base {
class FilePath;
}
namespace weblayer {
class FaviconBackendWrapper;
class FaviconServiceImplObserver;
// FaviconServiceImpl provides the front end (ui side) access to the favicon
// database. Most functions are processed async on the backend task-runner.
class FaviconServiceImpl : public favicon::CoreFaviconService {
public:
FaviconServiceImpl();
FaviconServiceImpl(const FaviconServiceImpl&) = delete;
FaviconServiceImpl& operator=(const FaviconServiceImpl&) = delete;
~FaviconServiceImpl() override;
void Init(const base::FilePath& db_path);
void set_observer(FaviconServiceImplObserver* observer) {
observer_ = observer;
}
// favicon::CoreFaviconService:
base::CancelableTaskTracker::TaskId GetFaviconForPageURL(
const GURL& page_url,
const favicon_base::IconTypeSet& icon_types,
int desired_size_in_dip,
favicon_base::FaviconResultsCallback callback,
base::CancelableTaskTracker* tracker) override;
void SetFaviconOutOfDateForPage(const GURL& page_url) override;
void SetFavicons(const base::flat_set<GURL>& page_urls,
const GURL& icon_url,
favicon_base::IconType icon_type,
const gfx::Image& image) override;
void CloneFaviconMappingsForPages(
const GURL& page_url_to_read,
const favicon_base::IconTypeSet& icon_types,
const base::flat_set<GURL>& page_urls_to_write) override;
base::CancelableTaskTracker::TaskId GetFavicon(
const GURL& icon_url,
favicon_base::IconType icon_type,
int desired_size_in_dip,
favicon_base::FaviconResultsCallback callback,
base::CancelableTaskTracker* tracker) override;
base::CancelableTaskTracker::TaskId UpdateFaviconMappingsAndFetch(
const base::flat_set<GURL>& page_urls,
const GURL& icon_url,
favicon_base::IconType icon_type,
int desired_size_in_dip,
favicon_base::FaviconResultsCallback callback,
base::CancelableTaskTracker* tracker) override;
void DeleteFaviconMappings(const base::flat_set<GURL>& page_urls,
favicon_base::IconType icon_type) override;
void UnableToDownloadFavicon(const GURL& icon_url) override;
void ClearUnableToDownloadFavicons() override;
bool WasUnableToDownloadFavicon(const GURL& icon_url) const override;
private:
using MissingFaviconUrlHash = size_t;
SEQUENCE_CHECKER(sequence_checker_);
// The TaskRunner to which FaviconServiceBackend tasks are posted. Nullptr
// once Cleanup() is called.
scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
scoped_refptr<FaviconBackendWrapper> backend_;
// Hashes of the favicon urls that were unable to be downloaded.
std::unordered_set<MissingFaviconUrlHash> missing_favicon_urls_;
// This is only used in tests, where only a single observer is necessary.
FaviconServiceImplObserver* observer_ = nullptr;
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_H_
// Copyright 2020 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 "weblayer/browser/favicon/favicon_service_impl_factory.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "weblayer/browser/browser_context_impl.h"
#include "weblayer/browser/favicon/favicon_service_impl.h"
#include "weblayer/browser/profile_impl.h"
namespace weblayer {
// static
FaviconServiceImpl* FaviconServiceImplFactory::GetForProfile(
ProfileImpl* profile) {
if (!profile->GetBrowserContext()->IsOffTheRecord()) {
return static_cast<FaviconServiceImpl*>(
GetInstance()->GetServiceForBrowserContext(profile->GetBrowserContext(),
true));
}
return nullptr;
}
// static
FaviconServiceImplFactory* FaviconServiceImplFactory::GetInstance() {
static base::NoDestructor<FaviconServiceImplFactory> factory;
return factory.get();
}
FaviconServiceImplFactory::FaviconServiceImplFactory()
: BrowserContextKeyedServiceFactory(
"FaviconServiceImpl",
BrowserContextDependencyManager::GetInstance()) {}
FaviconServiceImplFactory::~FaviconServiceImplFactory() = default;
KeyedService* FaviconServiceImplFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
// TODO(sky): figure out best way to support incognito.
DCHECK(!context->IsOffTheRecord());
std::unique_ptr<FaviconServiceImpl> service =
std::make_unique<FaviconServiceImpl>();
service->Init(static_cast<BrowserContextImpl*>(context)
->profile_impl()
->data_path()
.AppendASCII("Favicons"));
return service.release();
}
bool FaviconServiceImplFactory::ServiceIsNULLWhileTesting() const {
return true;
}
} // namespace weblayer
// Copyright 2020 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.
#ifndef WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_FACTORY_H_
#define WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_FACTORY_H_
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace weblayer {
class FaviconServiceImpl;
class ProfileImpl;
// BrowserContextKeyedServiceFactory for getting the FaviconServiceImpl.
class FaviconServiceImplFactory : public BrowserContextKeyedServiceFactory {
public:
FaviconServiceImplFactory(const FaviconServiceImplFactory&) = delete;
FaviconServiceImplFactory& operator=(const FaviconServiceImplFactory&) =
delete;
static FaviconServiceImpl* GetForProfile(ProfileImpl* profile);
// Returns the FaviconServiceFactory singleton.
static FaviconServiceImplFactory* GetInstance();
private:
friend class base::NoDestructor<FaviconServiceImplFactory>;
FaviconServiceImplFactory();
~FaviconServiceImplFactory() override;
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
bool ServiceIsNULLWhileTesting() const override;
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_FACTORY_H_
// Copyright 2020 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.
#ifndef WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_OBSERVER_H_
#define WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_OBSERVER_H_
namespace weblayer {
class FaviconServiceImplObserver {
public:
// Called from FaviconServiceImpl::UnableToDownloadFavicon.
virtual void OnUnableToDownloadFavicon() {}
protected:
virtual ~FaviconServiceImplObserver() = default;
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_OBSERVER_H_
// Copyright 2020 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 "weblayer/browser/favicon/favicon_tab_helper.h"
#include "components/favicon/content/content_favicon_driver.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "weblayer/browser/favicon/favicon_service_impl.h"
#include "weblayer/browser/favicon/favicon_service_impl_factory.h"
#include "weblayer/browser/profile_impl.h"
#include "weblayer/public/favicon_fetcher_delegate.h"
namespace weblayer {
FaviconTabHelper::ObserverSubscription::ObserverSubscription(
FaviconTabHelper* helper,
FaviconFetcherDelegate* delegate)
: helper_(helper), delegate_(delegate) {
helper_->AddDelegate(delegate_);
}
FaviconTabHelper::ObserverSubscription::~ObserverSubscription() {
helper_->RemoveDelegate(delegate_);
}
FaviconTabHelper::~FaviconTabHelper() {
// All of the ObserverSubscriptions should have been destroyed before this.
DCHECK_EQ(0, observer_count_);
}
std::unique_ptr<FaviconTabHelper::ObserverSubscription>
FaviconTabHelper::RegisterFaviconFetcherDelegate(
FaviconFetcherDelegate* delegate) {
// WrapUnique as constructor is private.
return base::WrapUnique(new ObserverSubscription(this, delegate));
}
FaviconTabHelper::FaviconTabHelper(content::WebContents* contents)
: WebContentsObserver(contents) {
// This code relies on the ability to get a Profile for the BrowserContext.
DCHECK(ProfileImpl::FromBrowserContext(web_contents()->GetBrowserContext()));
}
void FaviconTabHelper::AddDelegate(FaviconFetcherDelegate* delegate) {
delegates_.AddObserver(delegate);
if (++observer_count_ == 1) {
ProfileImpl* profile =
ProfileImpl::FromBrowserContext(web_contents()->GetBrowserContext());
FaviconServiceImpl* favicon_service =
FaviconServiceImplFactory::GetForProfile(profile);
favicon::ContentFaviconDriver::CreateForWebContents(web_contents(),
favicon_service);
favicon::ContentFaviconDriver::FromWebContents(web_contents())
->AddObserver(this);
}
}
void FaviconTabHelper::RemoveDelegate(FaviconFetcherDelegate* delegate) {
delegates_.RemoveObserver(delegate);
--observer_count_;
DCHECK_GE(observer_count_, 0);
if (observer_count_ == 0) {
favicon::ContentFaviconDriver::FromWebContents(web_contents())
->RemoveObserver(this);
// ContentFaviconDriver downloads images, if there are no observers there
// is no need to keep it around. This triggers deleting it.
web_contents()->SetUserData(favicon::ContentFaviconDriver::UserDataKey(),
nullptr);
favicon_ = gfx::Image();
}
}
void FaviconTabHelper::OnFaviconUpdated(
favicon::FaviconDriver* favicon_driver,
NotificationIconType notification_icon_type,
const GURL& icon_url,
bool icon_url_changed,
const gfx::Image& image) {
favicon_ = image;
for (FaviconFetcherDelegate& delegate : delegates_)
delegate.OnFaviconChanged(favicon_);
}
void FaviconTabHelper::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
!navigation_handle->HasCommitted() || navigation_handle->IsErrorPage() ||
navigation_handle->IsSameDocument()) {
return;
}
favicon_ = gfx::Image();
// Don't send notification in this case as it's assumed a new navigation
// triggers resetting the favicon.
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(FaviconTabHelper)
} // namespace weblayer
// Copyright 2020 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.
#ifndef WEBLAYER_BROWSER_FAVICON_FAVICON_TAB_HELPER_H_
#define WEBLAYER_BROWSER_FAVICON_FAVICON_TAB_HELPER_H_
#include <memory>
#include "base/observer_list.h"
#include "components/favicon/core/favicon_driver_observer.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/gfx/image/image.h"
namespace weblayer {
class FaviconFetcherDelegate;
// FaviconTabHelper is responsible for creating favicon::ContentFaviconDriver
// when necessary. FaviconTabHelper is used by FaviconFetcherImpl and notifies
// FaviconFetcherDelegate when the favicon changes.
class FaviconTabHelper : public content::WebContentsUserData<FaviconTabHelper>,
public content::WebContentsObserver,
public favicon::FaviconDriverObserver {
public:
// Used to track calls to RegisterFaviconFetcherDelegate(). When destroyed
// the FaviconFetcherDelegate is removed.
class ObserverSubscription {
public:
ObserverSubscription(const ObserverSubscription&) = delete;
ObserverSubscription& operator=(const ObserverSubscription&) = delete;
~ObserverSubscription();
private:
friend class FaviconTabHelper;
ObserverSubscription(FaviconTabHelper* helper,
FaviconFetcherDelegate* delegate);
FaviconTabHelper* helper_;
FaviconFetcherDelegate* delegate_;
};
FaviconTabHelper(const FaviconTabHelper&) = delete;
FaviconTabHelper& operator=(const FaviconTabHelper&) = delete;
~FaviconTabHelper() override;
// Called when FaviconFetcherImpl is created. This ensures the necessary
// wiring is in place and notifies |delegate| when the favicon changes.
std::unique_ptr<ObserverSubscription> RegisterFaviconFetcherDelegate(
FaviconFetcherDelegate* delegate);
// Returns the favicon for the current navigation.
const gfx::Image& favicon() const { return favicon_; }
private:
friend class content::WebContentsUserData<FaviconTabHelper>;
explicit FaviconTabHelper(content::WebContents* contents);
void AddDelegate(FaviconFetcherDelegate* delegate);
void RemoveDelegate(FaviconFetcherDelegate* delegate);
// favicon::FaviconDriverObserver:
void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
NotificationIconType notification_icon_type,
const GURL& icon_url,
bool icon_url_changed,
const gfx::Image& image) override;
// content::WebContentsObserver:
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
content::WebContents* web_contents_;
// Number of observers attached.
int observer_count_ = 0;
base::ObserverList<FaviconFetcherDelegate> delegates_;
gfx::Image favicon_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_TAB_HELPER_H_
......@@ -54,6 +54,8 @@
#include "weblayer/browser/browser_impl.h"
#include "weblayer/browser/browser_process.h"
#include "weblayer/browser/content_browser_client_impl.h"
#include "weblayer/browser/favicon/favicon_fetcher_impl.h"
#include "weblayer/browser/favicon/favicon_tab_helper.h"
#include "weblayer/browser/file_select_helper.h"
#include "weblayer/browser/host_content_settings_map_factory.h"
#include "weblayer/browser/i18n_util.h"
......@@ -261,6 +263,11 @@ TabImpl::TabImpl(ProfileImpl* profile,
// only be possible from the same profile.
DCHECK_EQ(profile_->GetBrowserContext(), web_contents_->GetBrowserContext());
// FaviconTabHelper adds a WebContentsObserver. Create FaviconTabHelper
// before |this| observes the WebContents to ensure favicons are reset before
// notifying weblayer observers of changes.
FaviconTabHelper::CreateForWebContents(web_contents_.get());
// By default renderer initiated navigations inherit the user-agent override
// of the current NavigationEntry. For WebLayer, the user-agent override is
// set on a per NavigationEntry entry basis.
......@@ -471,6 +478,11 @@ void TabImpl::ExecuteScriptWithUserGestureForTests(
script);
}
std::unique_ptr<FaviconFetcher> TabImpl::CreateFaviconFetcher(
FaviconFetcherDelegate* delegate) {
return std::make_unique<FaviconFetcherImpl>(web_contents_.get(), delegate);
}
#if !defined(OS_ANDROID)
void TabImpl::AttachToView(views::WebView* web_view) {
web_view->SetWebContents(web_contents_.get());
......
......@@ -224,6 +224,8 @@ class TabImpl : public Tab,
const std::vector<std::string>& js_origins) override;
void RemoveWebMessageHostFactory(
const base::string16& js_object_name) override;
std::unique_ptr<FaviconFetcher> CreateFaviconFetcher(
FaviconFetcherDelegate* delegate) override;
#if !defined(OS_ANDROID)
void AttachToView(views::WebView* web_view) override;
#endif
......
// Copyright 2020 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.
#ifndef WEBLAYER_PUBLIC_FAVICON_FETCHER_H_
#define WEBLAYER_PUBLIC_FAVICON_FETCHER_H_
namespace gfx {
class Image;
}
namespace weblayer {
// FaviconFetcher is responsible for downloading a favicon for the current
// navigation. FaviconFetcher caches favicons, updating the cache every so
// often to ensure the cache is up to date.
class FaviconFetcher {
public:
virtual ~FaviconFetcher() = default;
// Returns the favicon for the current navigation, which may be empty.
virtual gfx::Image GetFavicon() = 0;
protected:
FaviconFetcher() = default;
};
} // namespace weblayer
#endif // WEBLAYER_PUBLIC_FAVICON_FETCHER_H_
// Copyright 2020 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.
#ifndef WEBLAYER_PUBLIC_FAVICON_FETCHER_DELEGATE_H_
#define WEBLAYER_PUBLIC_FAVICON_FETCHER_DELEGATE_H_
#include "base/observer_list.h"
namespace gfx {
class Image;
}
namespace weblayer {
// Notified of interesting events related to FaviconFetcher.
class FaviconFetcherDelegate : public base::CheckedObserver {
public:
// Called when the favicon of the current navigation has changed. This may be
// called multiple times for the same navigation. This is *not* immediately
// called with an empty image when a navigation starts. It is assumed
// consuming code asks for the favicon when the navigation changes.
virtual void OnFaviconChanged(const gfx::Image& image) = 0;
protected:
~FaviconFetcherDelegate() override = default;
};
} // namespace weblayer
#endif // WEBLAYER_PUBLIC_FAVICON_FETCHER_DELEGATE_H_
......@@ -26,6 +26,8 @@ class WebView;
namespace weblayer {
class ErrorPageDelegate;
class FaviconFetcher;
class FaviconFetcherDelegate;
class FullscreenDelegate;
class GoogleAccountsDelegate;
class NavigationController;
......@@ -108,6 +110,17 @@ class Tab {
virtual void RemoveWebMessageHostFactory(
const base::string16& js_object_name) = 0;
// Creates a FaviconFetcher that notifies a FaviconFetcherDelegate when
// the favicon changes.
// A page may provide any number of favicons. The preferred image size
// used depends upon the platform. If a previously cached icon is available,
// it is used, otherwise the icon is downloaded.
// |delegate| may be called multiple times for the same navigation. This
// happens when the page dynamically updates the favicon, but may also happen
// if a cached icon is determined to be out of date.
virtual std::unique_ptr<FaviconFetcher> CreateFaviconFetcher(
FaviconFetcherDelegate* delegate) = 0;
#if !defined(OS_ANDROID)
// TODO: this isn't a stable API, so use it now for expediency in the C++ API,
// but if we ever want to have backward or forward compatibility in C++ this
......
......@@ -98,6 +98,7 @@ test("weblayer_browsertests") {
"//components/blocked_content",
"//components/content_settings/core/browser",
"//components/error_page/content/browser",
"//components/favicon/content",
"//components/network_session_configurator/common",
"//components/network_time",
"//components/page_load_metrics/browser:browser",
......@@ -129,6 +130,7 @@ test("weblayer_browsertests") {
"../browser/cookie_manager_browsertest.cc",
"../browser/download_browsertest.cc",
"../browser/errorpage_browsertest.cc",
"../browser/favicon/favicon_fetcher_browsertest.cc",
"../browser/google_accounts_browsertest.cc",
"../browser/js_communication/web_message_browsertest.cc",
"../browser/navigation_browsertest.cc",
......
<html>
<head>
<title>OK</title>
<link rel="icon" type="image/png" href="favicon.png"/>
</head>
<body>
Basic html test.
</body>
</html>
<html>
<head>
<title>OK</title>
<link rel="icon" type="image/png" href="favicon.png"/>
</head>
<body>
Basic html test.
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment