Backed out changeset b5abe23a4ea5 (bug 1048048) Backed out changeset 4f91b10e8be0 (bug 1048048) Backed out changeset 450d4a13c90e (bug 1048048) Backed out changeset 6a727c40eb68 (bug 1048048) Backed out changeset 88c2333ff745 (bug 1048048) Backed out changeset 740ab1ecd079 (bug 1048048) Backed out changeset 02c6d6aef163 (bug 1048048)
1124 lines
33 KiB
C++
1124 lines
33 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "AsyncFaviconHelpers.h"
|
|
|
|
#include "nsICacheEntry.h"
|
|
#include "nsICachingChannel.h"
|
|
#include "nsIAsyncVerifyRedirectCallback.h"
|
|
|
|
#include "nsNavHistory.h"
|
|
#include "nsFaviconService.h"
|
|
#include "mozilla/storage.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsIPrivateBrowsingChannel.h"
|
|
#include "nsISupportsPriority.h"
|
|
#include "nsContentUtils.h"
|
|
#include <algorithm>
|
|
|
|
using namespace mozilla::places;
|
|
using namespace mozilla::storage;
|
|
|
|
namespace mozilla {
|
|
namespace places {
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* Fetches information on a page from the Places database.
|
|
*
|
|
* @param aDBConn
|
|
* Database connection to history tables.
|
|
* @param _page
|
|
* Page that should be fetched.
|
|
*/
|
|
nsresult
|
|
FetchPageInfo(nsRefPtr<Database>& aDB,
|
|
PageData& _page)
|
|
{
|
|
NS_PRECONDITION(_page.spec.Length(), "Must have a non-empty spec!");
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread");
|
|
|
|
// This query finds the bookmarked uri we want to set the icon for,
|
|
// walking up to two redirect levels.
|
|
nsCString query = nsPrintfCString(
|
|
"SELECT h.id, h.favicon_id, h.guid, ( "
|
|
"SELECT h.url FROM moz_bookmarks b WHERE b.fk = h.id "
|
|
"UNION ALL " // Union not directly bookmarked pages.
|
|
"SELECT url FROM moz_places WHERE id = ( "
|
|
"SELECT COALESCE(grandparent.place_id, parent.place_id) as r_place_id "
|
|
"FROM moz_historyvisits dest "
|
|
"LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit "
|
|
"AND dest.visit_type IN (%d, %d) "
|
|
"LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
|
|
"AND parent.visit_type IN (%d, %d) "
|
|
"WHERE dest.place_id = h.id "
|
|
"AND EXISTS(SELECT 1 FROM moz_bookmarks b WHERE b.fk = r_place_id) "
|
|
"LIMIT 1 "
|
|
") "
|
|
") FROM moz_places h WHERE h.url = :page_url",
|
|
nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
|
|
nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
|
|
nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
|
|
nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
|
|
);
|
|
|
|
nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(query);
|
|
NS_ENSURE_STATE(stmt);
|
|
mozStorageStatementScoper scoper(stmt);
|
|
|
|
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
|
|
_page.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool hasResult;
|
|
rv = stmt->ExecuteStep(&hasResult);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (!hasResult) {
|
|
// The page does not exist.
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
rv = stmt->GetInt64(0, &_page.id);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
bool isNull;
|
|
rv = stmt->GetIsNull(1, &isNull);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
// favicon_id can be nullptr.
|
|
if (!isNull) {
|
|
rv = stmt->GetInt64(1, &_page.iconId);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
rv = stmt->GetUTF8String(2, _page.guid);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = stmt->GetIsNull(3, &isNull);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
// The page could not be bookmarked.
|
|
if (!isNull) {
|
|
rv = stmt->GetUTF8String(3, _page.bookmarkedSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (!_page.canAddToHistory) {
|
|
// Either history is disabled or the scheme is not supported. In such a
|
|
// case we want to update the icon only if the page is bookmarked.
|
|
|
|
if (_page.bookmarkedSpec.IsEmpty()) {
|
|
// The page is not bookmarked. Since updating the icon with a disabled
|
|
// history would be a privacy leak, bail out as if the page did not exist.
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
else {
|
|
// The page, or a redirect to it, is bookmarked. If the bookmarked spec
|
|
// is different from the requested one, use it.
|
|
if (!_page.bookmarkedSpec.Equals(_page.spec)) {
|
|
_page.spec = _page.bookmarkedSpec;
|
|
rv = FetchPageInfo(aDB, _page);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* Stores information on a icon in the database.
|
|
*
|
|
* @param aDBConn
|
|
* Database connection to history tables.
|
|
* @param aIcon
|
|
* Icon that should be stored.
|
|
*/
|
|
nsresult
|
|
SetIconInfo(nsRefPtr<Database>& aDB,
|
|
IconData& aIcon)
|
|
{
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread");
|
|
|
|
nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
|
|
"INSERT OR REPLACE INTO moz_favicons "
|
|
"(id, url, data, mime_type, expiration) "
|
|
"VALUES ((SELECT id FROM moz_favicons WHERE url = :icon_url), "
|
|
":icon_url, :data, :mime_type, :expiration) "
|
|
);
|
|
NS_ENSURE_STATE(stmt);
|
|
mozStorageStatementScoper scoper(stmt);
|
|
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aIcon.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
|
|
TO_INTBUFFER(aIcon.data), aIcon.data.Length());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), aIcon.mimeType);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aIcon.expiration);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = stmt->Execute();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* Fetches information on a icon from the Places database.
|
|
*
|
|
* @param aDBConn
|
|
* Database connection to history tables.
|
|
* @param _icon
|
|
* Icon that should be fetched.
|
|
*/
|
|
nsresult
|
|
FetchIconInfo(nsRefPtr<Database>& aDB,
|
|
IconData& _icon)
|
|
{
|
|
NS_PRECONDITION(_icon.spec.Length(), "Must have a non-empty spec!");
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread");
|
|
|
|
if (_icon.status & ICON_STATUS_CACHED) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
|
|
"SELECT id, expiration, data, mime_type "
|
|
"FROM moz_favicons WHERE url = :icon_url"
|
|
);
|
|
NS_ENSURE_STATE(stmt);
|
|
mozStorageStatementScoper scoper(stmt);
|
|
|
|
DebugOnly<nsresult> rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"),
|
|
_icon.spec);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
bool hasResult;
|
|
rv = stmt->ExecuteStep(&hasResult);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
if (!hasResult) {
|
|
// The icon does not exist yet, bail out.
|
|
return NS_OK;
|
|
}
|
|
|
|
rv = stmt->GetInt64(0, &_icon.id);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
|
|
// Expiration can be nullptr.
|
|
bool isNull;
|
|
rv = stmt->GetIsNull(1, &isNull);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
if (!isNull) {
|
|
rv = stmt->GetInt64(1, reinterpret_cast<int64_t*>(&_icon.expiration));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
}
|
|
|
|
// Data can be nullptr.
|
|
rv = stmt->GetIsNull(2, &isNull);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
if (!isNull) {
|
|
uint8_t* data;
|
|
uint32_t dataLen = 0;
|
|
rv = stmt->GetBlob(2, &dataLen, &data);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
_icon.data.Adopt(TO_CHARBUFFER(data), dataLen);
|
|
// Read mime only if we have data.
|
|
rv = stmt->GetUTF8String(3, _icon.mimeType);
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
FetchIconURL(nsRefPtr<Database>& aDB,
|
|
const nsACString& aPageSpec,
|
|
nsACString& aIconSpec)
|
|
{
|
|
NS_PRECONDITION(!aPageSpec.IsEmpty(), "Page spec must not be empty.");
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread.");
|
|
|
|
aIconSpec.Truncate();
|
|
|
|
nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
|
|
"SELECT f.url "
|
|
"FROM moz_places h "
|
|
"JOIN moz_favicons f ON h.favicon_id = f.id "
|
|
"WHERE h.url = :page_url"
|
|
);
|
|
NS_ENSURE_STATE(stmt);
|
|
mozStorageStatementScoper scoper(stmt);
|
|
|
|
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
|
|
aPageSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool hasResult;
|
|
if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
|
rv = stmt->GetUTF8String(0, aIconSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* Tries to compute the expiration time for a icon from the channel.
|
|
*
|
|
* @param aChannel
|
|
* The network channel used to fetch the icon.
|
|
* @return a valid expiration value for the fetched icon.
|
|
*/
|
|
PRTime
|
|
GetExpirationTimeFromChannel(nsIChannel* aChannel)
|
|
{
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread");
|
|
|
|
// Attempt to get an expiration time from the cache. If this fails, we'll
|
|
// make one up.
|
|
PRTime expiration = -1;
|
|
nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aChannel);
|
|
if (cachingChannel) {
|
|
nsCOMPtr<nsISupports> cacheToken;
|
|
nsresult rv = cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsICacheEntry> cacheEntry = do_QueryInterface(cacheToken);
|
|
uint32_t seconds;
|
|
rv = cacheEntry->GetExpirationTime(&seconds);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// Set the expiration, but make sure we honor our cap.
|
|
expiration = PR_Now() + std::min((PRTime)seconds * PR_USEC_PER_SEC,
|
|
MAX_FAVICON_EXPIRATION);
|
|
}
|
|
}
|
|
}
|
|
// If we did not obtain a time from the cache, use the cap value.
|
|
return expiration < 0 ? PR_Now() + MAX_FAVICON_EXPIRATION
|
|
: expiration;
|
|
}
|
|
|
|
/**
|
|
* Checks the icon and evaluates if it needs to be optimized. In such a case it
|
|
* will try to reduce its size through OptimizeFaviconImage method of the
|
|
* favicons service.
|
|
*
|
|
* @param aIcon
|
|
* The icon to be evaluated.
|
|
* @param aFaviconSvc
|
|
* Pointer to the favicons service.
|
|
*/
|
|
nsresult
|
|
OptimizeIconSize(IconData& aIcon,
|
|
nsFaviconService* aFaviconSvc)
|
|
{
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread");
|
|
|
|
// Even if the page provides a large image for the favicon (eg, a highres
|
|
// image or a multiresolution .ico file), don't try to store more data than
|
|
// needed.
|
|
nsAutoCString newData, newMimeType;
|
|
if (aIcon.data.Length() > MAX_ICON_FILESIZE(aFaviconSvc->GetOptimizedIconDimension())) {
|
|
nsresult rv = aFaviconSvc->OptimizeFaviconImage(TO_INTBUFFER(aIcon.data),
|
|
aIcon.data.Length(),
|
|
aIcon.mimeType,
|
|
newData,
|
|
newMimeType);
|
|
if (NS_SUCCEEDED(rv) && newData.Length() < aIcon.data.Length()) {
|
|
aIcon.data = newData;
|
|
aIcon.mimeType = newMimeType;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// AsyncFaviconHelperBase
|
|
|
|
AsyncFaviconHelperBase::AsyncFaviconHelperBase(
|
|
nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
)
|
|
{
|
|
// Don't AddRef or Release in runnables for thread-safety.
|
|
mCallback.swap(aCallback);
|
|
}
|
|
|
|
AsyncFaviconHelperBase::~AsyncFaviconHelperBase()
|
|
{
|
|
nsCOMPtr<nsIThread> thread;
|
|
(void)NS_GetMainThread(getter_AddRefs(thread));
|
|
if (mCallback) {
|
|
(void)NS_ProxyRelease(thread, mCallback, true);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// AsyncFetchAndSetIconForPage
|
|
|
|
// static
|
|
nsresult
|
|
AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI,
|
|
nsIURI* aPageURI,
|
|
enum AsyncFaviconFetchMode aFetchMode,
|
|
uint32_t aFaviconLoadType,
|
|
nsIFaviconDataCallback* aCallback)
|
|
{
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread");
|
|
|
|
PageData page;
|
|
nsresult rv = aPageURI->GetSpec(page.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
// URIs can arguably miss a host.
|
|
(void)GetReversedHostname(aPageURI, page.revHost);
|
|
bool canAddToHistory;
|
|
nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
|
|
NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
|
|
rv = navHistory->CanAddURI(aPageURI, &canAddToHistory);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
page.canAddToHistory = !!canAddToHistory && aFaviconLoadType != nsIFaviconService::FAVICON_LOAD_PRIVATE;
|
|
|
|
IconData icon;
|
|
|
|
nsFaviconService* favicons = nsFaviconService::GetFaviconService();
|
|
NS_ENSURE_STATE(favicons);
|
|
|
|
UnassociatedIconHashKey* iconKey =
|
|
favicons->mUnassociatedIcons.GetEntry(aFaviconURI);
|
|
|
|
if (iconKey) {
|
|
icon = iconKey->iconData;
|
|
favicons->mUnassociatedIcons.RemoveEntry(aFaviconURI);
|
|
} else {
|
|
icon.fetchMode = aFetchMode;
|
|
rv = aFaviconURI->GetSpec(icon.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// If the page url points to an image, the icon's url will be the same.
|
|
// In future evaluate to store a resample of the image. For now avoid that
|
|
// for database size concerns.
|
|
// Don't store favicons for error pages too.
|
|
if (icon.spec.Equals(page.spec) ||
|
|
icon.spec.Equals(FAVICON_ERRORPAGE_URL)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// The event will swap owning pointers, thus we need a new pointer.
|
|
nsCOMPtr<nsIFaviconDataCallback> callback(aCallback);
|
|
nsRefPtr<AsyncFetchAndSetIconForPage> event =
|
|
new AsyncFetchAndSetIconForPage(icon, page, aFaviconLoadType, callback);
|
|
|
|
// Get the target thread and start the work.
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
DB->DispatchToAsyncThread(event);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
AsyncFetchAndSetIconForPage::AsyncFetchAndSetIconForPage(
|
|
IconData& aIcon
|
|
, PageData& aPage
|
|
, uint32_t aFaviconLoadType
|
|
, nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
) : AsyncFaviconHelperBase(aCallback)
|
|
, mIcon(aIcon)
|
|
, mPage(aPage)
|
|
, mFaviconLoadPrivate(aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE)
|
|
{
|
|
}
|
|
|
|
AsyncFetchAndSetIconForPage::~AsyncFetchAndSetIconForPage()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncFetchAndSetIconForPage::Run()
|
|
{
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread");
|
|
|
|
// Try to fetch the icon from the database.
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
nsresult rv = FetchIconInfo(DB, mIcon);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool isInvalidIcon = mIcon.data.IsEmpty() ||
|
|
(mIcon.expiration && PR_Now() > mIcon.expiration);
|
|
bool fetchIconFromNetwork = mIcon.fetchMode == FETCH_ALWAYS ||
|
|
(mIcon.fetchMode == FETCH_IF_MISSING && isInvalidIcon);
|
|
|
|
if (!fetchIconFromNetwork) {
|
|
// There is already a valid icon or we don't want to fetch a new one,
|
|
// directly proceed with association.
|
|
nsRefPtr<AsyncAssociateIconToPage> event =
|
|
new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
|
|
DB->DispatchToAsyncThread(event);
|
|
|
|
return NS_OK;
|
|
}
|
|
else {
|
|
// Fetch the icon from network. When done this will associate the
|
|
// icon to the page and notify.
|
|
nsRefPtr<AsyncFetchAndSetIconFromNetwork> event =
|
|
new AsyncFetchAndSetIconFromNetwork(mIcon, mPage, mFaviconLoadPrivate, mCallback);
|
|
|
|
// Start the work on the main thread.
|
|
rv = NS_DispatchToMainThread(event);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// AsyncFetchAndSetIconFromNetwork
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(
|
|
AsyncFetchAndSetIconFromNetwork
|
|
, nsRunnable
|
|
, nsIStreamListener
|
|
, nsIInterfaceRequestor
|
|
, nsIChannelEventSink
|
|
)
|
|
|
|
AsyncFetchAndSetIconFromNetwork::AsyncFetchAndSetIconFromNetwork(
|
|
IconData& aIcon
|
|
, PageData& aPage
|
|
, bool aFaviconLoadPrivate
|
|
, nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
)
|
|
: AsyncFaviconHelperBase(aCallback)
|
|
, mIcon(aIcon)
|
|
, mPage(aPage)
|
|
, mFaviconLoadPrivate(aFaviconLoadPrivate)
|
|
{
|
|
}
|
|
|
|
AsyncFetchAndSetIconFromNetwork::~AsyncFetchAndSetIconFromNetwork()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncFetchAndSetIconFromNetwork::Run()
|
|
{
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread");
|
|
|
|
// Ensure data is cleared, since it's going to be overwritten.
|
|
if (mIcon.data.Length() > 0) {
|
|
mIcon.data.Truncate(0);
|
|
mIcon.mimeType.Truncate(0);
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> iconURI;
|
|
nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsCOMPtr<nsIChannel> channel;
|
|
rv = NS_NewChannel(getter_AddRefs(channel),
|
|
iconURI,
|
|
nsContentUtils::GetSystemPrincipal(),
|
|
nsILoadInfo::SEC_NORMAL,
|
|
nsIContentPolicy::TYPE_INTERNAL_IMAGE);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsCOMPtr<nsIInterfaceRequestor> listenerRequestor =
|
|
do_QueryInterface(reinterpret_cast<nsISupports*>(this));
|
|
NS_ENSURE_STATE(listenerRequestor);
|
|
rv = channel->SetNotificationCallbacks(listenerRequestor);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
|
|
if (pbChannel) {
|
|
rv = pbChannel->SetPrivate(mFaviconLoadPrivate);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(channel);
|
|
if (priorityChannel) {
|
|
priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
|
|
}
|
|
|
|
return channel->AsyncOpen(this, nullptr);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncFetchAndSetIconFromNetwork::OnStartRequest(nsIRequest* aRequest,
|
|
nsISupports* aContext)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncFetchAndSetIconFromNetwork::OnDataAvailable(nsIRequest* aRequest,
|
|
nsISupports* aContext,
|
|
nsIInputStream* aInputStream,
|
|
uint64_t aOffset,
|
|
uint32_t aCount)
|
|
{
|
|
const size_t kMaxFaviconDownloadSize = 1 * 1024 * 1024;
|
|
if (mIcon.data.Length() + aCount > kMaxFaviconDownloadSize) {
|
|
mIcon.data.Truncate();
|
|
return NS_ERROR_FILE_TOO_BIG;
|
|
}
|
|
|
|
nsAutoCString buffer;
|
|
nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
|
|
if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
if (!mIcon.data.Append(buffer, fallible)) {
|
|
mIcon.data.Truncate();
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
AsyncFetchAndSetIconFromNetwork::GetInterface(const nsIID& uuid,
|
|
void** aResult)
|
|
{
|
|
return QueryInterface(uuid, aResult);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
AsyncFetchAndSetIconFromNetwork::AsyncOnChannelRedirect(
|
|
nsIChannel* oldChannel
|
|
, nsIChannel* newChannel
|
|
, uint32_t flags
|
|
, nsIAsyncVerifyRedirectCallback *cb
|
|
)
|
|
{
|
|
(void)cb->OnRedirectVerifyCallback(NS_OK);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest,
|
|
nsISupports* aContext,
|
|
nsresult aStatusCode)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
nsFaviconService* favicons = nsFaviconService::GetFaviconService();
|
|
NS_ENSURE_STATE(favicons);
|
|
|
|
nsresult rv;
|
|
|
|
// If fetching the icon failed, add it to the failed cache.
|
|
if (NS_FAILED(aStatusCode) || mIcon.data.Length() == 0) {
|
|
nsCOMPtr<nsIURI> iconURI;
|
|
rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = favicons->AddFailedFavicon(iconURI);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
|
// aRequest should always QI to nsIChannel.
|
|
// See AsyncFetchAndSetIconFromNetwork::Run()
|
|
MOZ_ASSERT(channel);
|
|
|
|
nsAutoCString contentType;
|
|
channel->GetContentType(contentType);
|
|
// Bug 366324 - can't sniff SVG yet, so rely on server-specified type
|
|
if (contentType.EqualsLiteral("image/svg+xml")) {
|
|
mIcon.mimeType.AssignLiteral("image/svg+xml");
|
|
} else {
|
|
NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, aRequest,
|
|
TO_INTBUFFER(mIcon.data), mIcon.data.Length(),
|
|
mIcon.mimeType);
|
|
}
|
|
|
|
// If the icon does not have a valid MIME type, add it to the failed cache.
|
|
if (mIcon.mimeType.IsEmpty()) {
|
|
nsCOMPtr<nsIURI> iconURI;
|
|
rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = favicons->AddFailedFavicon(iconURI);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
mIcon.expiration = GetExpirationTimeFromChannel(channel);
|
|
|
|
// Telemetry probes to measure the favicon file sizes for each different file type.
|
|
// This allow us to measure common file sizes while also observing each type popularity.
|
|
if (mIcon.mimeType.EqualsLiteral("image/png")) {
|
|
mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_PNG_SIZES, mIcon.data.Length());
|
|
}
|
|
else if (mIcon.mimeType.EqualsLiteral("image/x-icon") ||
|
|
mIcon.mimeType.EqualsLiteral("image/vnd.microsoft.icon")) {
|
|
mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_ICO_SIZES, mIcon.data.Length());
|
|
}
|
|
else if (mIcon.mimeType.EqualsLiteral("image/jpeg") ||
|
|
mIcon.mimeType.EqualsLiteral("image/pjpeg")) {
|
|
mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_JPEG_SIZES, mIcon.data.Length());
|
|
}
|
|
else if (mIcon.mimeType.EqualsLiteral("image/gif")) {
|
|
mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_GIF_SIZES, mIcon.data.Length());
|
|
}
|
|
else if (mIcon.mimeType.EqualsLiteral("image/bmp") ||
|
|
mIcon.mimeType.EqualsLiteral("image/x-windows-bmp")) {
|
|
mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_BMP_SIZES, mIcon.data.Length());
|
|
}
|
|
else if (mIcon.mimeType.EqualsLiteral("image/svg+xml")) {
|
|
mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_SVG_SIZES, mIcon.data.Length());
|
|
}
|
|
else {
|
|
mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_OTHER_SIZES, mIcon.data.Length());
|
|
}
|
|
|
|
rv = OptimizeIconSize(mIcon, favicons);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// If over the maximum size allowed, don't save data to the database to
|
|
// avoid bloating it.
|
|
if (mIcon.data.Length() > MAX_FAVICON_SIZE) {
|
|
return NS_OK;
|
|
}
|
|
|
|
mIcon.status = ICON_STATUS_CHANGED;
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
nsRefPtr<AsyncAssociateIconToPage> event =
|
|
new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
|
|
DB->DispatchToAsyncThread(event);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// AsyncAssociateIconToPage
|
|
|
|
AsyncAssociateIconToPage::AsyncAssociateIconToPage(
|
|
IconData& aIcon
|
|
, PageData& aPage
|
|
, nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
) : AsyncFaviconHelperBase(aCallback)
|
|
, mIcon(aIcon)
|
|
, mPage(aPage)
|
|
{
|
|
}
|
|
|
|
AsyncAssociateIconToPage::~AsyncAssociateIconToPage()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncAssociateIconToPage::Run()
|
|
{
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread");
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
nsresult rv = FetchPageInfo(DB, mPage);
|
|
if (rv == NS_ERROR_NOT_AVAILABLE){
|
|
// We have never seen this page. If we can add the page to history,
|
|
// we will try to do it later, otherwise just bail out.
|
|
if (!mPage.canAddToHistory) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
else {
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
mozStorageTransaction transaction(DB->MainConn(), false,
|
|
mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
|
|
|
// If there is no entry for this icon, or the entry is obsolete, replace it.
|
|
if (mIcon.id == 0 || (mIcon.status & ICON_STATUS_CHANGED)) {
|
|
rv = SetIconInfo(DB, mIcon);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Get the new icon id. Do this regardless mIcon.id, since other code
|
|
// could have added a entry before us. Indeed we interrupted the thread
|
|
// after the previous call to FetchIconInfo.
|
|
mIcon.status = (mIcon.status & ~(ICON_STATUS_CACHED)) | ICON_STATUS_SAVED;
|
|
rv = FetchIconInfo(DB, mIcon);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// If the page does not have an id, don't try to insert a new one, cause we
|
|
// don't know where the page comes from. Not doing so we may end adding
|
|
// a page that otherwise we'd explicitly ignore, like a POST or an error page.
|
|
if (mPage.id == 0) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Otherwise just associate the icon to the page, if needed.
|
|
if (mPage.iconId != mIcon.id) {
|
|
nsCOMPtr<mozIStorageStatement> stmt;
|
|
if (mPage.id) {
|
|
stmt = DB->GetStatement(
|
|
"UPDATE moz_places SET favicon_id = :icon_id WHERE id = :page_id"
|
|
);
|
|
NS_ENSURE_STATE(stmt);
|
|
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
else {
|
|
stmt = DB->GetStatement(
|
|
"UPDATE moz_places SET favicon_id = :icon_id WHERE url = :page_url"
|
|
);
|
|
NS_ENSURE_STATE(stmt);
|
|
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), mIcon.id);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mozStorageStatementScoper scoper(stmt);
|
|
rv = stmt->Execute();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mIcon.status |= ICON_STATUS_ASSOCIATED;
|
|
}
|
|
|
|
rv = transaction.Commit();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Finally, dispatch an event to the main thread to notify observers.
|
|
nsCOMPtr<nsIRunnable> event = new NotifyIconObservers(mIcon, mPage, mCallback);
|
|
rv = NS_DispatchToMainThread(event);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// AsyncGetFaviconURLForPage
|
|
|
|
// static
|
|
nsresult
|
|
AsyncGetFaviconURLForPage::start(nsIURI* aPageURI,
|
|
nsIFaviconDataCallback* aCallback)
|
|
{
|
|
NS_ENSURE_ARG(aCallback);
|
|
NS_ENSURE_ARG(aPageURI);
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread.");
|
|
|
|
nsAutoCString pageSpec;
|
|
nsresult rv = aPageURI->GetSpec(pageSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIFaviconDataCallback> callback = aCallback;
|
|
nsRefPtr<AsyncGetFaviconURLForPage> event =
|
|
new AsyncGetFaviconURLForPage(pageSpec, callback);
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
DB->DispatchToAsyncThread(event);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
AsyncGetFaviconURLForPage::AsyncGetFaviconURLForPage(
|
|
const nsACString& aPageSpec
|
|
, nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
) : AsyncFaviconHelperBase(aCallback)
|
|
{
|
|
mPageSpec.Assign(aPageSpec);
|
|
}
|
|
|
|
AsyncGetFaviconURLForPage::~AsyncGetFaviconURLForPage()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncGetFaviconURLForPage::Run()
|
|
{
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread.");
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
nsAutoCString iconSpec;
|
|
nsresult rv = FetchIconURL(DB, mPageSpec, iconSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Now notify our callback of the icon spec we retrieved, even if empty.
|
|
IconData iconData;
|
|
iconData.spec.Assign(iconSpec);
|
|
|
|
PageData pageData;
|
|
pageData.spec.Assign(mPageSpec);
|
|
|
|
nsCOMPtr<nsIRunnable> event =
|
|
new NotifyIconObservers(iconData, pageData, mCallback);
|
|
rv = NS_DispatchToMainThread(event);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// AsyncGetFaviconDataForPage
|
|
|
|
// static
|
|
nsresult
|
|
AsyncGetFaviconDataForPage::start(nsIURI* aPageURI,
|
|
nsIFaviconDataCallback* aCallback)
|
|
{
|
|
NS_ENSURE_ARG(aCallback);
|
|
NS_ENSURE_ARG(aPageURI);
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread.");
|
|
|
|
nsAutoCString pageSpec;
|
|
nsresult rv = aPageURI->GetSpec(pageSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIFaviconDataCallback> callback = aCallback;
|
|
nsRefPtr<AsyncGetFaviconDataForPage> event =
|
|
new AsyncGetFaviconDataForPage(pageSpec, callback);
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
DB->DispatchToAsyncThread(event);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
AsyncGetFaviconDataForPage::AsyncGetFaviconDataForPage(
|
|
const nsACString& aPageSpec
|
|
, nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
) : AsyncFaviconHelperBase(aCallback)
|
|
{
|
|
mPageSpec.Assign(aPageSpec);
|
|
}
|
|
|
|
AsyncGetFaviconDataForPage::~AsyncGetFaviconDataForPage()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncGetFaviconDataForPage::Run()
|
|
{
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread.");
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
nsAutoCString iconSpec;
|
|
nsresult rv = FetchIconURL(DB, mPageSpec, iconSpec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
IconData iconData;
|
|
iconData.spec.Assign(iconSpec);
|
|
|
|
PageData pageData;
|
|
pageData.spec.Assign(mPageSpec);
|
|
|
|
if (!iconSpec.IsEmpty()) {
|
|
rv = FetchIconInfo(DB, iconData);
|
|
if (NS_FAILED(rv)) {
|
|
iconData.spec.Truncate();
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIRunnable> event =
|
|
new NotifyIconObservers(iconData, pageData, mCallback);
|
|
rv = NS_DispatchToMainThread(event);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// AsyncReplaceFaviconData
|
|
|
|
// static
|
|
nsresult
|
|
AsyncReplaceFaviconData::start(IconData *aIcon)
|
|
{
|
|
NS_ENSURE_ARG(aIcon);
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread.");
|
|
|
|
nsCOMPtr<nsIFaviconDataCallback> callback;
|
|
nsRefPtr<AsyncReplaceFaviconData> event =
|
|
new AsyncReplaceFaviconData(*aIcon, callback);
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
DB->DispatchToAsyncThread(event);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
AsyncReplaceFaviconData::AsyncReplaceFaviconData(
|
|
IconData &aIcon
|
|
, nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
) : AsyncFaviconHelperBase(aCallback)
|
|
, mIcon(aIcon)
|
|
{
|
|
}
|
|
|
|
AsyncReplaceFaviconData::~AsyncReplaceFaviconData()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
AsyncReplaceFaviconData::Run()
|
|
{
|
|
NS_PRECONDITION(!NS_IsMainThread(),
|
|
"This should not be called on the main thread");
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
NS_ENSURE_STATE(DB);
|
|
IconData dbIcon;
|
|
dbIcon.spec.Assign(mIcon.spec);
|
|
nsresult rv = FetchIconInfo(DB, dbIcon);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!dbIcon.id) {
|
|
return NS_OK;
|
|
}
|
|
|
|
rv = SetIconInfo(DB, mIcon);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// We can invalidate the cache version since we now persist the icon.
|
|
nsCOMPtr<nsIRunnable> event = new RemoveIconDataCacheEntry(mIcon, mCallback);
|
|
rv = NS_DispatchToMainThread(event);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// RemoveIconDataCacheEntry
|
|
|
|
RemoveIconDataCacheEntry::RemoveIconDataCacheEntry(
|
|
IconData& aIcon
|
|
, nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
)
|
|
: AsyncFaviconHelperBase(aCallback)
|
|
, mIcon(aIcon)
|
|
{
|
|
}
|
|
|
|
RemoveIconDataCacheEntry::~RemoveIconDataCacheEntry()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
RemoveIconDataCacheEntry::Run()
|
|
{
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread");
|
|
|
|
nsCOMPtr<nsIURI> iconURI;
|
|
nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsFaviconService* favicons = nsFaviconService::GetFaviconService();
|
|
NS_ENSURE_STATE(favicons);
|
|
favicons->mUnassociatedIcons.RemoveEntry(iconURI);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//// NotifyIconObservers
|
|
|
|
NotifyIconObservers::NotifyIconObservers(
|
|
IconData& aIcon
|
|
, PageData& aPage
|
|
, nsCOMPtr<nsIFaviconDataCallback>& aCallback
|
|
)
|
|
: AsyncFaviconHelperBase(aCallback)
|
|
, mIcon(aIcon)
|
|
, mPage(aPage)
|
|
{
|
|
}
|
|
|
|
NotifyIconObservers::~NotifyIconObservers()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
NotifyIconObservers::Run()
|
|
{
|
|
NS_PRECONDITION(NS_IsMainThread(),
|
|
"This should be called on the main thread");
|
|
|
|
nsCOMPtr<nsIURI> iconURI;
|
|
if (!mIcon.spec.IsEmpty()) {
|
|
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_NewURI(getter_AddRefs(iconURI), mIcon.spec)));
|
|
if (iconURI)
|
|
{
|
|
// Notify observers only if something changed.
|
|
if (mIcon.status & ICON_STATUS_SAVED ||
|
|
mIcon.status & ICON_STATUS_ASSOCIATED) {
|
|
SendGlobalNotifications(iconURI);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mCallback) {
|
|
(void)mCallback->OnComplete(iconURI, mIcon.data.Length(),
|
|
TO_INTBUFFER(mIcon.data), mIcon.mimeType);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
NotifyIconObservers::SendGlobalNotifications(nsIURI* aIconURI)
|
|
{
|
|
nsCOMPtr<nsIURI> pageURI;
|
|
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pageURI), mPage.spec)));
|
|
if (pageURI) {
|
|
nsFaviconService* favicons = nsFaviconService::GetFaviconService();
|
|
MOZ_ASSERT(favicons);
|
|
if (favicons) {
|
|
(void)favicons->SendFaviconNotifications(pageURI, aIconURI, mPage.guid);
|
|
}
|
|
}
|
|
|
|
// If the page is bookmarked and the bookmarked url is different from the
|
|
// updated one, start a new task to update its icon as well.
|
|
if (!mPage.bookmarkedSpec.IsEmpty() &&
|
|
!mPage.bookmarkedSpec.Equals(mPage.spec)) {
|
|
// Create a new page struct to avoid polluting it with old data.
|
|
PageData bookmarkedPage;
|
|
bookmarkedPage.spec = mPage.bookmarkedSpec;
|
|
|
|
nsRefPtr<Database> DB = Database::GetDatabase();
|
|
if (!DB)
|
|
return;
|
|
// This will be silent, so be sure to not pass in the current callback.
|
|
nsCOMPtr<nsIFaviconDataCallback> nullCallback;
|
|
nsRefPtr<AsyncAssociateIconToPage> event =
|
|
new AsyncAssociateIconToPage(mIcon, bookmarkedPage, nullCallback);
|
|
DB->DispatchToAsyncThread(event);
|
|
}
|
|
}
|
|
|
|
} // namespace places
|
|
} // namespace mozilla
|