blob: 22443c6b2176a0a76058da6be2219ac1a79ac8fb [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 "base/bind.h"
#include "base/memory/ptr_util.h"
#include "components/favicon/content/favicon_url_util.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon/core/favicon_url.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/common/favicon_url.h"
#include "third_party/blink/public/common/manifest/manifest.h"
#include "ui/gfx/image/image.h"
namespace favicon {
namespace {
void ExtractManifestIcons(
ContentFaviconDriver::ManifestDownloadCallback callback,
const GURL& manifest_url,
const blink::Manifest& manifest) {
std::vector<FaviconURL> candidates;
for (const auto& icon : manifest.icons) {
candidates.emplace_back(icon.src, favicon_base::IconType::kWebManifestIcon,
icon.sizes);
}
std::move(callback).Run(candidates);
}
} // namespace
// static
void ContentFaviconDriver::CreateForWebContents(
content::WebContents* web_contents,
FaviconService* favicon_service) {
if (FromWebContents(web_contents))
return;
web_contents->SetUserData(UserDataKey(),
base::WrapUnique(new ContentFaviconDriver(
web_contents, favicon_service)));
}
void ContentFaviconDriver::SaveFaviconEvenIfInIncognito() {
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
if (!entry)
return;
// Make sure the page is in history, otherwise adding the favicon does
// nothing.
GURL page_url = entry->GetURL();
favicon_service()->AddPageNoVisitForBookmark(page_url, entry->GetTitle());
const content::FaviconStatus& favicon_status = entry->GetFavicon();
if (!favicon_service() || !favicon_status.valid ||
favicon_status.url.is_empty() || favicon_status.image.IsEmpty()) {
return;
}
favicon_service()->SetFavicons({page_url}, favicon_status.url,
favicon_base::IconType::kFavicon,
favicon_status.image);
}
gfx::Image ContentFaviconDriver::GetFavicon() const {
// Like GetTitle(), we also want to use the favicon for the last committed
// entry rather than a pending navigation entry.
content::NavigationController& controller = web_contents()->GetController();
content::NavigationEntry* entry = controller.GetTransientEntry();
if (entry)
return entry->GetFavicon().image;
entry = controller.GetLastCommittedEntry();
if (entry)
return entry->GetFavicon().image;
return gfx::Image();
}
bool ContentFaviconDriver::FaviconIsValid() const {
content::NavigationController& controller = web_contents()->GetController();
content::NavigationEntry* entry = controller.GetTransientEntry();
if (entry)
return entry->GetFavicon().valid;
entry = controller.GetLastCommittedEntry();
if (entry)
return entry->GetFavicon().valid;
return false;
}
GURL ContentFaviconDriver::GetActiveURL() {
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
return entry ? entry->GetURL() : GURL();
}
ContentFaviconDriver::ContentFaviconDriver(content::WebContents* web_contents,
FaviconService* favicon_service)
: content::WebContentsObserver(web_contents),
FaviconDriverImpl(favicon_service),
document_on_load_completed_(false) {}
ContentFaviconDriver::~ContentFaviconDriver() {
}
int ContentFaviconDriver::DownloadImage(const GURL& url,
int max_image_size,
ImageDownloadCallback callback) {
bool bypass_cache = (bypass_cache_page_url_ == GetActiveURL());
bypass_cache_page_url_ = GURL();
return web_contents()->DownloadImage(url, true, max_image_size, bypass_cache,
std::move(callback));
}
void ContentFaviconDriver::DownloadManifest(const GURL& url,
ManifestDownloadCallback callback) {
web_contents()->GetManifest(
base::BindOnce(&ExtractManifestIcons, std::move(callback)));
}
bool ContentFaviconDriver::IsOffTheRecord() {
DCHECK(web_contents());
return web_contents()->GetBrowserContext()->IsOffTheRecord();
}
void ContentFaviconDriver::OnFaviconUpdated(
const GURL& page_url,
FaviconDriverObserver::NotificationIconType notification_icon_type,
const GURL& icon_url,
bool icon_url_changed,
const gfx::Image& image) {
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
DCHECK(entry);
DCHECK_EQ(entry->GetURL(), page_url);
if (notification_icon_type == FaviconDriverObserver::NON_TOUCH_16_DIP) {
entry->GetFavicon().valid = true;
entry->GetFavicon().url = icon_url;
entry->GetFavicon().image = image;
web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
}
NotifyFaviconUpdatedObservers(notification_icon_type, icon_url,
icon_url_changed, image);
}
void ContentFaviconDriver::OnFaviconDeleted(
const GURL& page_url,
FaviconDriverObserver::NotificationIconType notification_icon_type) {
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
DCHECK(entry && entry->GetURL() == page_url);
if (notification_icon_type == FaviconDriverObserver::NON_TOUCH_16_DIP) {
entry->GetFavicon() = content::FaviconStatus();
web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
}
NotifyFaviconUpdatedObservers(notification_icon_type, /*icon_url=*/GURL(),
/*icon_url_changed=*/true,
content::FaviconStatus().image);
}
void ContentFaviconDriver::DidUpdateFaviconURL(
const std::vector<content::FaviconURL>& candidates) {
// Ignore the update if there is no last committed navigation entry. This can
// occur when loading an initially blank page.
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
if (!entry)
return;
// We update |favicon_urls_| even if the list is believed to be partial
// (checked below), because callers of our getter favicon_urls() expect so.
favicon_urls_ = candidates;
if (!document_on_load_completed_)
return;
OnUpdateCandidates(entry->GetURL(),
FaviconURLsFromContentFaviconURLs(candidates),
manifest_url_);
}
void ContentFaviconDriver::DidUpdateWebManifestURL(
const base::Optional<GURL>& manifest_url) {
// Ignore the update if there is no last committed navigation entry. This can
// occur when loading an initially blank page.
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
if (!entry || !document_on_load_completed_)
return;
manifest_url_ = manifest_url.value_or(GURL());
// On regular page loads, DidUpdateManifestURL() is guaranteed to be called
// before DidUpdateFaviconURL(). However, a page can update the favicons via
// javascript.
if (favicon_urls_.has_value()) {
OnUpdateCandidates(entry->GetURL(),
FaviconURLsFromContentFaviconURLs(*favicon_urls_),
manifest_url_);
}
}
void ContentFaviconDriver::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame())
return;
favicon_urls_.reset();
if (!navigation_handle->IsSameDocument()) {
document_on_load_completed_ = false;
manifest_url_ = GURL();
}
content::ReloadType reload_type = navigation_handle->GetReloadType();
if (reload_type == content::ReloadType::NONE || IsOffTheRecord())
return;
bypass_cache_page_url_ = navigation_handle->GetURL();
SetFaviconOutOfDateForPage(
navigation_handle->GetURL(),
reload_type == content::ReloadType::BYPASSING_CACHE);
}
void ContentFaviconDriver::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
!navigation_handle->HasCommitted() ||
navigation_handle->IsErrorPage()) {
return;
}
// Wait till the user navigates to a new URL to start checking the cache
// again. The cache may be ignored for non-reload navigations (e.g.
// history.replace() in-page navigation). This is allowed to increase the
// likelihood that "reloading a page ignoring the cache" redownloads the
// favicon. In particular, a page may do an in-page navigation before
// FaviconHandler has the time to determine that the favicon needs to be
// redownloaded.
GURL url = navigation_handle->GetURL();
if (url != bypass_cache_page_url_)
bypass_cache_page_url_ = GURL();
// Get the favicon, either from history or request it from the net.
FetchFavicon(url, navigation_handle->IsSameDocument());
}
void ContentFaviconDriver::DocumentOnLoadCompletedInMainFrame() {
document_on_load_completed_ = true;
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(ContentFaviconDriver)
} // namespace favicon