diff options
author | avi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-07 22:51:41 +0000 |
---|---|---|
committer | avi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-07 22:51:41 +0000 |
commit | 0150cfcb238a357ce4daaa9d9c6d5a4516f9a8eb (patch) | |
tree | 0a21f40d09ea0394e75f4c34d99ac07c5a74de8d /chrome/browser/favicon_helper.cc | |
parent | 9017a7808e576c7faac46695ab7845ec30434197 (diff) | |
download | chromium_src-0150cfcb238a357ce4daaa9d9c6d5a4516f9a8eb.zip chromium_src-0150cfcb238a357ce4daaa9d9c6d5a4516f9a8eb.tar.gz chromium_src-0150cfcb238a357ce4daaa9d9c6d5a4516f9a8eb.tar.bz2 |
Revert 80519 - Move favicon from TabContents to TabContentsWrapper.
BUG=78732
Original BUG=71097
Original TEST=no visible change
Original Review URL: http://codereview.chromium.org/6735042
TBR=avi@chromium.org
Review URL: http://codereview.chromium.org/6814029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80860 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/favicon_helper.cc')
-rw-r--r-- | chrome/browser/favicon_helper.cc | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/chrome/browser/favicon_helper.cc b/chrome/browser/favicon_helper.cc new file mode 100644 index 0000000..1e5206b --- /dev/null +++ b/chrome/browser/favicon_helper.cc @@ -0,0 +1,315 @@ +// Copyright (c) 2011 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 "chrome/browser/favicon_helper.h" + +#include "build/build_config.h" + +#include <vector> + +#include "base/callback.h" +#include "base/memory/ref_counted_memory.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/render_messages.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "content/browser/tab_contents/navigation_controller.h" +#include "content/browser/tab_contents/navigation_entry.h" +#include "content/browser/tab_contents/tab_contents_delegate.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "skia/ext/image_operations.h" +#include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/favicon_size.h" + +FaviconHelper::FaviconHelper(TabContents* tab_contents) + : TabContentsObserver(tab_contents), + got_favicon_url_(false), + got_favicon_from_history_(false), + favicon_expired_(false) { +} + +FaviconHelper::~FaviconHelper() { + SkBitmap empty_image; + + // Call pending download callbacks with error to allow caller to clean up. + for (DownloadRequests::iterator i = download_requests_.begin(); + i != download_requests_.end(); ++i) { + if (i->second.callback) { + i->second.callback->Run(i->first, true, empty_image); + } + } +} + +void FaviconHelper::FetchFavicon(const GURL& url) { + cancelable_consumer_.CancelAllRequests(); + + url_ = url; + + favicon_expired_ = got_favicon_from_history_ = got_favicon_url_ = false; + + // Request the favicon from the history service. In parallel to this the + // renderer is going to notify us (well TabContents) when the favicon url is + // available. + if (GetFaviconService()) { + GetFaviconService()->GetFaviconForURL(url_, history::FAVICON, + &cancelable_consumer_, + NewCallback(this, &FaviconHelper::OnFaviconDataForInitialURL)); + } +} + +int FaviconHelper::DownloadImage(const GURL& image_url, + int image_size, + ImageDownloadCallback* callback) { + DCHECK(callback); // Must provide a callback. + return ScheduleDownload(GURL(), image_url, image_size, callback); +} + +Profile* FaviconHelper::profile() { + return tab_contents()->profile(); +} + +FaviconService* FaviconHelper::GetFaviconService() { + return profile()->GetFaviconService(Profile::EXPLICIT_ACCESS); +} + +void FaviconHelper::SetFavicon( + const GURL& url, + const GURL& image_url, + const SkBitmap& image) { + const SkBitmap& sized_image = + (image.width() == kFaviconSize && image.height() == kFaviconSize) + ? image : ConvertToFaviconSize(image); + + if (GetFaviconService() && ShouldSaveFavicon(url)) { + std::vector<unsigned char> image_data; + gfx::PNGCodec::EncodeBGRASkBitmap(sized_image, false, &image_data); + GetFaviconService()->SetFavicon(url, image_url, image_data, + history::FAVICON); + } + + if (url == url_) { + NavigationEntry* entry = GetEntry(); + if (entry) + UpdateFavicon(entry, sized_image); + } +} + +void FaviconHelper::UpdateFavicon(NavigationEntry* entry, + scoped_refptr<RefCountedMemory> data) { + SkBitmap image; + gfx::PNGCodec::Decode(data->front(), data->size(), &image); + UpdateFavicon(entry, image); +} + +void FaviconHelper::UpdateFavicon(NavigationEntry* entry, + const SkBitmap& image) { + // No matter what happens, we need to mark the favicon as being set. + entry->favicon().set_is_valid(true); + + if (image.empty()) + return; + + entry->favicon().set_bitmap(image); + tab_contents()->NotifyNavigationStateChanged(TabContents::INVALIDATE_TAB); +} + +void FaviconHelper::OnUpdateFaviconURL(int32 page_id, const GURL& icon_url) { + // TODO(davemoore) Should clear on empty url. Currently we ignore it. + // This appears to be what FF does as well. + if (icon_url.is_empty()) + return; + + NavigationEntry* entry = GetEntry(); + if (!entry) + return; + + got_favicon_url_ = true; + + if (!GetFaviconService()) + return; + + if (!favicon_expired_ && entry->favicon().is_valid() && + entry->favicon().url() == icon_url) { + // We already have the icon, no need to proceed. + return; + } + + entry->favicon().set_url(icon_url); + + if (got_favicon_from_history_) + DownloadFaviconOrAskHistory(entry); +} + +bool FaviconHelper::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(FaviconHelper, message) + IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateFaviconURL, OnUpdateFaviconURL) + IPC_MESSAGE_HANDLER(ViewHostMsg_DidDownloadFavicon, OnDidDownloadFavicon) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void FaviconHelper::OnDidDownloadFavicon(int id, + const GURL& image_url, + bool errored, + const SkBitmap& image) { + DownloadRequests::iterator i = download_requests_.find(id); + if (i == download_requests_.end()) { + // Currently TabContents notifies us of ANY downloads so that it is + // possible to get here. + return; + } + + if (i->second.callback) { + i->second.callback->Run(id, errored, image); + } else if (!errored) { + SetFavicon(i->second.url, image_url, image); + } + + download_requests_.erase(i); +} + +NavigationEntry* FaviconHelper::GetEntry() { + NavigationEntry* entry = tab_contents()->controller().GetActiveEntry(); + if (entry && entry->url() == url_ && + tab_contents()->IsActiveEntry(entry->page_id())) { + return entry; + } + // If the URL has changed out from under us (as will happen with redirects) + // return NULL. + return NULL; +} + +void FaviconHelper::OnFaviconDataForInitialURL( + FaviconService::Handle handle, + history::FaviconData favicon) { + NavigationEntry* entry = GetEntry(); + if (!entry) + return; + + got_favicon_from_history_ = true; + + favicon_expired_ = (favicon.known_icon && favicon.expired); + + if (favicon.known_icon && !entry->favicon().is_valid() && + (!got_favicon_url_ || entry->favicon().url() == favicon.icon_url)) { + // The db knows the favicon (although it may be out of date) and the entry + // doesn't have an icon. Set the favicon now, and if the favicon turns out + // to be expired (or the wrong url) we'll fetch later on. This way the + // user doesn't see a flash of the default favicon. + entry->favicon().set_url(favicon.icon_url); + if (favicon.is_valid()) + UpdateFavicon(entry, favicon.image_data); + entry->favicon().set_is_valid(true); + } + + if (favicon.known_icon && !favicon.expired) { + if (got_favicon_url_ && entry->favicon().url() != favicon.icon_url) { + // Mapping in the database is wrong. DownloadFaviconOrAskHistory will + // update the mapping for this url and download the favicon if we don't + // already have it. + DownloadFaviconOrAskHistory(entry); + } + } else if (got_favicon_url_) { + // We know the official url for the favicon, by either don't have the + // favicon or its expired. Continue on to DownloadFaviconOrAskHistory to + // either download or check history again. + DownloadFaviconOrAskHistory(entry); + } + // else we haven't got the icon url. When we get it we'll ask the + // renderer to download the icon. +} + +void FaviconHelper::DownloadFaviconOrAskHistory(NavigationEntry* entry) { + DCHECK(entry); // We should only get here if entry is valid. + if (favicon_expired_) { + // We have the mapping, but the favicon is out of date. Download it now. + ScheduleDownload(entry->url(), entry->favicon().url(), kFaviconSize, NULL); + } else if (GetFaviconService()) { + // We don't know the favicon, but we may have previously downloaded the + // favicon for another page that shares the same favicon. Ask for the + // favicon given the favicon URL. + if (profile()->IsOffTheRecord()) { + GetFaviconService()->GetFavicon( + entry->favicon().url(), + history::FAVICON, + &cancelable_consumer_, + NewCallback(this, &FaviconHelper::OnFaviconData)); + } else { + // Ask the history service for the icon. This does two things: + // 1. Attempts to fetch the favicon data from the database. + // 2. If the favicon exists in the database, this updates the database to + // include the mapping between the page url and the favicon url. + // This is asynchronous. The history service will call back when done. + // Issue the request and associate the current page ID with it. + GetFaviconService()->UpdateFaviconMappingAndFetch( + entry->url(), + entry->favicon().url(), + history::FAVICON, + &cancelable_consumer_, + NewCallback(this, &FaviconHelper::OnFaviconData)); + } + } +} + +void FaviconHelper::OnFaviconData( + FaviconService::Handle handle, + history::FaviconData favicon) { + NavigationEntry* entry = GetEntry(); + if (!entry) + return; + + // No need to update the favicon url. By the time we get here + // UpdateFaviconURL will have set the favicon url. + + if (favicon.is_valid()) { + // There is a favicon, set it now. If expired we'll download the current + // one again, but at least the user will get some icon instead of the + // default and most likely the current one is fine anyway. + UpdateFavicon(entry, favicon.image_data); + } + + if (!favicon.known_icon || favicon.expired) { + // We don't know the favicon, or it is out of date. Request the current one. + ScheduleDownload(entry->url(), entry->favicon().url(), kFaviconSize, NULL); + } +} + +int FaviconHelper::ScheduleDownload(const GURL& url, + const GURL& image_url, + int image_size, + ImageDownloadCallback* callback) { + const int download_id = tab_contents()->render_view_host()->DownloadFavicon( + image_url, image_size); + + if (download_id) { + // Download ids should be unique. + DCHECK(download_requests_.find(download_id) == download_requests_.end()); + download_requests_[download_id] = DownloadRequest(url, image_url, callback); + } + + return download_id; +} + +SkBitmap FaviconHelper::ConvertToFaviconSize(const SkBitmap& image) { + int width = image.width(); + int height = image.height(); + if (width > 0 && height > 0) { + calc_favicon_target_size(&width, &height); + return skia::ImageOperations::Resize( + image, skia::ImageOperations::RESIZE_LANCZOS3, + width, height); + } + return image; +} + +bool FaviconHelper::ShouldSaveFavicon(const GURL& url) { + if (!profile()->IsOffTheRecord()) + return true; + + // Otherwise store the favicon if the page is bookmarked. + BookmarkModel* bookmark_model = profile()->GetBookmarkModel(); + return bookmark_model && bookmark_model->IsBookmarked(url); +} |