1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Places.
17 * The Initial Developer of the Original Code is
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
23 * Brett Wilson <brettw@gmail.com> (original author)
24 * Ehsan Akhgari <ehsan.akhgari@gmail.com>
25 * Shawn Wilsher <me@shawnwilsher.com>
26 * Marco Bonardo <mak77@bonardo.net>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
43 * This is the favicon service, which stores favicons for web pages with your
44 * history as you browse. It is also used to save the favicons for bookmarks.
46 * DANGER: The history query system makes assumptions about the favicon storage
47 * so that icons can be quickly generated for history/bookmark result sets. If
48 * you change the database layout at all, you will have to update both services.
51 #include "nsFaviconService.h"
53 #include "nsPlacesTables.h"
54 #include "nsPlacesMacros.h"
56 #include "AsyncFaviconHelpers.h"
58 #include "nsNavBookmarks.h"
59 #include "nsNavHistory.h"
60 #include "nsIPrefService.h"
62 #include "nsNetUtil.h"
63 #include "nsReadableUtils.h"
64 #include "nsStreamUtils.h"
65 #include "nsStringStream.h"
68 // For large favicons optimization.
69 #include "imgITools.h"
70 #include "imgIContainer.h"
72 // Default value for mOptimizedIconDimension
73 #define OPTIMIZED_FAVICON_DIMENSION 16
75 #define MAX_FAVICON_CACHE_SIZE 256
76 #define FAVICON_CACHE_REDUCE_COUNT 64
79 * The maximum time we will keep a favicon around. We always ask the cache, if
80 * we can, but default to this value if we do not get a time back, or the time
81 * is more in the future than this.
82 * Currently set to one week from now.
84 #define MAX_FAVICON_EXPIRATION ((PRTime)7 * 24 * 60 * 60 * PR_USEC_PER_SEC)
86 // The MIME type of the default favicon and favicons created by
87 // OptimizeFaviconImage.
88 #define DEFAULT_MIME_TYPE "image/png"
90 using namespace mozilla::places
;
93 * Used to notify a topic to system observers on async execute completion.
94 * Will throw on error.
96 class ExpireFaviconsStatementCallbackNotifier
: public AsyncStatementCallback
99 ExpireFaviconsStatementCallbackNotifier(bool* aFaviconsExpirationRunning
);
100 NS_IMETHOD
HandleCompletion(PRUint16 aReason
);
103 bool* mFaviconsExpirationRunning
;
107 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsFaviconService
, gFaviconService
)
114 nsFaviconService::nsFaviconService()
115 : mSyncStatements(mDBConn
)
116 , mFaviconsExpirationRunning(false)
117 , mOptimizedIconDimension(OPTIMIZED_FAVICON_DIMENSION
)
118 , mFailedFaviconSerial(0)
119 , mShuttingDown(false)
121 NS_ASSERTION(!gFaviconService
,
122 "Attempting to create two instances of the service!");
123 gFaviconService
= this;
127 nsFaviconService::~nsFaviconService()
129 NS_ASSERTION(gFaviconService
== this,
130 "Deleting a non-singleton instance of the service");
131 if (gFaviconService
== this)
132 gFaviconService
= nsnull
;
137 nsFaviconService::Init()
139 // Creation of history service will call InitTables.
140 nsNavHistory
* historyService
= nsNavHistory::GetHistoryService();
141 NS_ENSURE_TRUE(historyService
, NS_ERROR_OUT_OF_MEMORY
);
142 mDBConn
= historyService
->GetStorageConnection();
143 NS_ENSURE_TRUE(mDBConn
, NS_ERROR_FAILURE
);
145 // Init failed favicon cache.
146 if (!mFailedFavicons
.Init(MAX_FAVICON_CACHE_SIZE
))
147 return NS_ERROR_OUT_OF_MEMORY
;
149 nsCOMPtr
<nsIPrefBranch
> pb
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
151 (void)pb
->GetIntPref("places.favicons.optimizeToDimension",
152 &mOptimizedIconDimension
);
159 mozIStorageStatement
*
160 nsFaviconService::GetStatement(const nsCOMPtr
<mozIStorageStatement
>& aStmt
)
165 RETURN_IF_STMT(mDBGetIconInfo
, NS_LITERAL_CSTRING(
166 "SELECT id, length(data), expiration FROM moz_favicons "
167 "WHERE url = :icon_url"));
169 RETURN_IF_STMT(mDBGetURL
, NS_LITERAL_CSTRING(
170 "SELECT f.id, f.url, length(f.data), f.expiration "
172 "JOIN moz_favicons f ON h.favicon_id = f.id "
173 "WHERE h.url = :page_url "
176 RETURN_IF_STMT(mDBGetData
, NS_LITERAL_CSTRING(
177 "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url"));
179 RETURN_IF_STMT(mDBInsertIcon
, NS_LITERAL_CSTRING(
180 "INSERT OR REPLACE INTO moz_favicons (id, url, data, mime_type, expiration) "
181 "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration)"));
183 RETURN_IF_STMT(mDBUpdateIcon
, NS_LITERAL_CSTRING(
184 "UPDATE moz_favicons SET data = :data, mime_type = :mime_type, "
185 "expiration = :expiration "
186 "WHERE id = :icon_id"));
188 RETURN_IF_STMT(mDBSetPageFavicon
, NS_LITERAL_CSTRING(
189 "UPDATE moz_places SET favicon_id = :icon_id WHERE id = :page_id"));
191 RETURN_IF_STMT(mDBRemoveOnDiskReferences
, NS_LITERAL_CSTRING(
193 "SET favicon_id = NULL "
194 "WHERE favicon_id NOT NULL"));
196 RETURN_IF_STMT(mDBRemoveAllFavicons
, NS_LITERAL_CSTRING(
197 "DELETE FROM moz_favicons WHERE id NOT IN ("
198 "SELECT favicon_id FROM moz_places WHERE favicon_id NOT NULL "
205 // nsFaviconService::InitTables
207 // Called by the history service to create the favicon table. The history
208 // service uses this table in its queries, so it must exist even if
209 // nobody has called the favicon service.
212 nsFaviconService::InitTables(mozIStorageConnection
* aDBConn
)
215 PRBool exists
= PR_FALSE
;
216 aDBConn
->TableExists(NS_LITERAL_CSTRING("moz_favicons"), &exists
);
218 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_MOZ_FAVICONS
);
219 NS_ENSURE_SUCCESS(rv
, rv
);
227 nsFaviconService::ExpireAllFavicons()
229 mFaviconsExpirationRunning
= true;
231 mozIStorageBaseStatement
*stmts
[] = {
232 GetStatement(mDBRemoveOnDiskReferences
),
233 GetStatement(mDBRemoveAllFavicons
),
235 NS_ENSURE_STATE(stmts
[0] && stmts
[1]);
236 nsCOMPtr
<mozIStoragePendingStatement
> ps
;
237 nsCOMPtr
<ExpireFaviconsStatementCallbackNotifier
> callback
=
238 new ExpireFaviconsStatementCallbackNotifier(&mFaviconsExpirationRunning
);
239 nsresult rv
= mDBConn
->ExecuteAsync(stmts
, NS_ARRAY_LENGTH(stmts
), callback
,
241 NS_ENSURE_SUCCESS(rv
, rv
);
247 ////////////////////////////////////////////////////////////////////////////////
248 //// nsIFaviconService
251 nsFaviconService::SetFaviconUrlForPage(nsIURI
* aPageURI
, nsIURI
* aFaviconURI
)
253 NS_ENSURE_ARG(aPageURI
);
254 NS_ENSURE_ARG(aFaviconURI
);
256 if (mFaviconsExpirationRunning
)
260 nsresult rv
= SetFaviconUrlForPageInternal(aPageURI
, aFaviconURI
, &hasData
);
261 NS_ENSURE_SUCCESS(rv
, rv
);
263 // send favicon change notifications if the URL has any data
265 SendFaviconNotifications(aPageURI
, aFaviconURI
);
271 nsFaviconService::GetDefaultFavicon(nsIURI
** _retval
)
273 NS_ENSURE_ARG_POINTER(_retval
);
275 // not found, use default
277 nsresult rv
= NS_NewURI(getter_AddRefs(mDefaultIcon
),
278 NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL
));
279 NS_ENSURE_SUCCESS(rv
, rv
);
281 return mDefaultIcon
->Clone(_retval
);
285 // nsFaviconService::SetFaviconUrlForPageInternal
287 // This creates a new entry in the favicon table if necessary and tells the
288 // history service to associate the given favicon ID with the given URI. We
289 // don't want to update the history table directly since that may involve
290 // creating a new row in the history table, which should only be done by
293 // This sets aHasData if there was already icon data for this favicon. Used
294 // to know if we should try reloading.
296 // Does NOT send out notifications. Caller should send out notifications
297 // if the favicon has data.
300 nsFaviconService::SetFaviconUrlForPageInternal(nsIURI
* aPageURI
,
306 *aHasData
= PR_FALSE
;
308 nsNavHistory
* historyService
= nsNavHistory::GetHistoryService();
309 NS_ENSURE_TRUE(historyService
, NS_ERROR_OUT_OF_MEMORY
);
311 if (historyService
->InPrivateBrowsingMode())
314 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
316 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetIconInfo
);
317 rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("icon_url"), aFaviconURI
);
318 NS_ENSURE_SUCCESS(rv
, rv
);
320 PRBool hasResult
= PR_FALSE
;
321 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
322 // We already have an entry for this icon, just get the stats
323 rv
= stmt
->GetInt64(0, &iconId
);
324 NS_ENSURE_SUCCESS(rv
, rv
);
326 // see if this icon has data already
328 rv
= stmt
->GetInt32(1, &dataSize
);
329 NS_ENSURE_SUCCESS(rv
, rv
);
336 // We did not find any entry, so create a new one
337 // not-binded params are automatically nullified by mozStorage
338 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBInsertIcon
);
339 rv
= stmt
->BindNullByName(NS_LITERAL_CSTRING("icon_id"));
340 NS_ENSURE_SUCCESS(rv
, rv
);
341 rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("icon_url"), aFaviconURI
);
342 NS_ENSURE_SUCCESS(rv
, rv
);
343 rv
= stmt
->Execute();
344 NS_ENSURE_SUCCESS(rv
, rv
);
347 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(getInfoStmt
, mDBGetIconInfo
);
348 rv
= URIBinder::Bind(getInfoStmt
, NS_LITERAL_CSTRING("icon_url"), aFaviconURI
);
349 NS_ENSURE_SUCCESS(rv
, rv
);
352 rv
= getInfoStmt
->ExecuteStep(&hasResult
);
353 NS_ENSURE_SUCCESS(rv
, rv
);
354 NS_ASSERTION(hasResult
, "hasResult is false but the call succeeded?");
355 iconId
= getInfoStmt
->AsInt64(0);
359 // now link our icon entry with the page
361 rv
= historyService
->GetUrlIdFor(aPageURI
, &pageId
, PR_TRUE
);
362 NS_ENSURE_SUCCESS(rv
, rv
);
364 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBSetPageFavicon
);
365 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId
);
366 NS_ENSURE_SUCCESS(rv
, rv
);
367 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), iconId
);
368 NS_ENSURE_SUCCESS(rv
, rv
);
369 rv
= stmt
->Execute();
370 NS_ENSURE_SUCCESS(rv
, rv
);
372 rv
= transaction
.Commit();
373 NS_ENSURE_SUCCESS(rv
, rv
);
378 // nsFaviconService::SendFaviconNotifications
380 // Call to send out favicon changed notifications. Should only be called
381 // when you know there is data loaded for the favicon.
384 nsFaviconService::SendFaviconNotifications(nsIURI
* aPageURI
,
387 nsCAutoString faviconSpec
;
388 nsNavHistory
* historyService
= nsNavHistory::GetHistoryService();
389 if (historyService
&& NS_SUCCEEDED(aFaviconURI
->GetSpec(faviconSpec
))) {
390 historyService
->SendPageChangedNotification(aPageURI
,
391 nsINavHistoryObserver::ATTRIBUTE_FAVICON
,
392 NS_ConvertUTF8toUTF16(faviconSpec
));
398 nsFaviconService::SetAndLoadFaviconForPage(nsIURI
* aPageURI
,
401 nsIFaviconDataCallback
* aCallback
)
403 NS_ENSURE_ARG(aPageURI
);
404 NS_ENSURE_ARG(aFaviconURI
);
406 if (mFaviconsExpirationRunning
)
409 // If a favicon is in the failed cache, only load it during a forced reload.
410 PRBool previouslyFailed
;
411 nsresult rv
= IsFailedFavicon(aFaviconURI
, &previouslyFailed
);
412 NS_ENSURE_SUCCESS(rv
, rv
);
413 if (previouslyFailed
) {
415 RemoveFailedFavicon(aFaviconURI
);
420 // Check if the icon already exists and fetch it from the network, if needed.
421 // Finally associate the icon to the requested page if not yet associated.
422 rv
= AsyncFetchAndSetIconForPage::start(
423 aFaviconURI
, aPageURI
, aForceReload
? FETCH_ALWAYS
: FETCH_IF_MISSING
,
426 NS_ENSURE_SUCCESS(rv
, rv
);
428 // DB will be updated and observers notified when data has finished loading.
433 // nsFaviconService::SetFaviconData
435 // See the IDL for this function for lots of info. Note from there: we don't
436 // send out notifications.
439 nsFaviconService::SetFaviconData(nsIURI
* aFaviconURI
, const PRUint8
* aData
,
440 PRUint32 aDataLen
, const nsACString
& aMimeType
,
443 NS_ENSURE_ARG(aFaviconURI
);
445 if (mFaviconsExpirationRunning
)
449 PRUint32 dataLen
= aDataLen
;
450 const PRUint8
* data
= aData
;
451 const nsACString
* mimeType
= &aMimeType
;
452 nsCString newData
, newMimeType
;
454 // If the page provided a large image for the favicon (eg, a highres image
455 // or a multiresolution .ico file), we don't want to store more data than
457 if (aDataLen
> MAX_ICON_FILESIZE(mOptimizedIconDimension
)) {
458 rv
= OptimizeFaviconImage(aData
, aDataLen
, aMimeType
, newData
, newMimeType
);
459 if (NS_SUCCEEDED(rv
) && newData
.Length() < aDataLen
) {
460 data
= reinterpret_cast<PRUint8
*>(const_cast<char*>(newData
.get())),
461 dataLen
= newData
.Length();
462 mimeType
= &newMimeType
;
464 else if (aDataLen
> MAX_FAVICON_SIZE
) {
465 // We cannot optimize this favicon size and we are over the maximum size
466 // allowed, so we will not save data to the db to avoid bloating it.
467 return NS_ERROR_FAILURE
;
471 mozIStorageStatement
* statement
;
473 // this block forces the scoper to reset our statement: necessary for the
475 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetIconInfo
);
476 rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("icon_url"), aFaviconURI
);
477 NS_ENSURE_SUCCESS(rv
, rv
);
480 rv
= stmt
->ExecuteStep(&hasResult
);
481 NS_ENSURE_SUCCESS(rv
, rv
);
484 // Get id of the old entry and update it.
486 rv
= stmt
->GetInt64(0, &id
);
487 NS_ENSURE_SUCCESS(rv
, rv
);
488 statement
= GetStatement(mDBUpdateIcon
);
489 NS_ENSURE_STATE(statement
);
490 rv
= statement
->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), id
);
491 NS_ENSURE_SUCCESS(rv
, rv
);
492 rv
= statement
->BindBlobByName(NS_LITERAL_CSTRING("data"), data
, dataLen
);
493 NS_ENSURE_SUCCESS(rv
, rv
);
494 rv
= statement
->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), *mimeType
);
495 NS_ENSURE_SUCCESS(rv
, rv
);
496 rv
= statement
->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aExpiration
);
497 NS_ENSURE_SUCCESS(rv
, rv
);
500 // Insert a new entry.
501 statement
= GetStatement(mDBInsertIcon
);
502 NS_ENSURE_STATE(statement
);
503 rv
= statement
->BindNullByName(NS_LITERAL_CSTRING("icon_id"));
504 NS_ENSURE_SUCCESS(rv
, rv
);
505 rv
= URIBinder::Bind(statement
, NS_LITERAL_CSTRING("icon_url"), aFaviconURI
);
506 NS_ENSURE_SUCCESS(rv
, rv
);
507 rv
= statement
->BindBlobByName(NS_LITERAL_CSTRING("data"), data
, dataLen
);
508 NS_ENSURE_SUCCESS(rv
, rv
);
509 rv
= statement
->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), *mimeType
);
510 NS_ENSURE_SUCCESS(rv
, rv
);
511 rv
= statement
->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aExpiration
);
512 NS_ENSURE_SUCCESS(rv
, rv
);
516 mozStorageStatementScoper
scoper(statement
);
518 rv
= statement
->Execute();
519 NS_ENSURE_SUCCESS(rv
, rv
);
526 nsFaviconService::SetFaviconDataFromDataURL(nsIURI
* aFaviconURI
,
527 const nsAString
& aDataURL
,
530 NS_ENSURE_ARG(aFaviconURI
);
531 if (mFaviconsExpirationRunning
)
534 nsCOMPtr
<nsIURI
> dataURI
;
535 nsresult rv
= NS_NewURI(getter_AddRefs(dataURI
), aDataURL
);
536 NS_ENSURE_SUCCESS(rv
, rv
);
538 // use the data: protocol handler to convert the data
539 nsCOMPtr
<nsIIOService
> ioService
= do_GetIOService(&rv
);
540 NS_ENSURE_SUCCESS(rv
, rv
);
541 nsCOMPtr
<nsIProtocolHandler
> protocolHandler
;
542 rv
= ioService
->GetProtocolHandler("data", getter_AddRefs(protocolHandler
));
543 NS_ENSURE_SUCCESS(rv
, rv
);
545 nsCOMPtr
<nsIChannel
> channel
;
546 rv
= protocolHandler
->NewChannel(dataURI
, getter_AddRefs(channel
));
547 NS_ENSURE_SUCCESS(rv
, rv
);
549 // blocking stream is OK for data URIs
550 nsCOMPtr
<nsIInputStream
> stream
;
551 rv
= channel
->Open(getter_AddRefs(stream
));
552 NS_ENSURE_SUCCESS(rv
, rv
);
555 rv
= stream
->Available(&available
);
556 NS_ENSURE_SUCCESS(rv
, rv
);
558 return NS_ERROR_FAILURE
;
560 // read all the decoded data
561 PRUint8
* buffer
= static_cast<PRUint8
*>
562 (nsMemory::Alloc(sizeof(PRUint8
) * available
));
564 return NS_ERROR_OUT_OF_MEMORY
;
566 rv
= stream
->Read(reinterpret_cast<char*>(buffer
), available
, &numRead
);
567 if (NS_FAILED(rv
) || numRead
!= available
) {
568 nsMemory::Free(buffer
);
572 nsCAutoString mimeType
;
573 rv
= channel
->GetContentType(mimeType
);
574 NS_ENSURE_SUCCESS(rv
, rv
);
576 // SetFaviconData can now do the dirty work
577 rv
= SetFaviconData(aFaviconURI
, buffer
, available
, mimeType
, aExpiration
);
578 nsMemory::Free(buffer
);
579 NS_ENSURE_SUCCESS(rv
, rv
);
586 nsFaviconService::GetFaviconData(nsIURI
* aFaviconURI
, nsACString
& aMimeType
,
587 PRUint32
* aDataLen
, PRUint8
** aData
)
589 NS_ENSURE_ARG(aFaviconURI
);
590 NS_ENSURE_ARG_POINTER(aDataLen
);
591 NS_ENSURE_ARG_POINTER(aData
);
593 nsCOMPtr
<nsIURI
> defaultFaviconURI
;
594 nsresult rv
= GetDefaultFavicon(getter_AddRefs(defaultFaviconURI
));
595 NS_ENSURE_SUCCESS(rv
, rv
);
597 PRBool isDefaultFavicon
= PR_FALSE
;
598 rv
= defaultFaviconURI
->Equals(aFaviconURI
, &isDefaultFavicon
);
599 NS_ENSURE_SUCCESS(rv
, rv
);
601 // If we're getting the default favicon, we need to handle it separately since
602 // it's not in the database.
603 if (isDefaultFavicon
) {
604 nsCAutoString defaultData
;
605 rv
= GetDefaultFaviconData(defaultData
);
606 NS_ENSURE_SUCCESS(rv
, rv
);
608 PRUint8
* bytes
= reinterpret_cast<PRUint8
*>(ToNewCString(defaultData
));
609 NS_ENSURE_STATE(bytes
);
612 *aDataLen
= defaultData
.Length();
613 aMimeType
.AssignLiteral(DEFAULT_MIME_TYPE
);
618 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetData
);
619 rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("icon_url"), aFaviconURI
);
620 NS_ENSURE_SUCCESS(rv
, rv
);
622 PRBool hasResult
= PR_FALSE
;
623 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
624 rv
= stmt
->GetUTF8String(1, aMimeType
);
625 NS_ENSURE_SUCCESS(rv
, rv
);
627 return stmt
->GetBlob(0, aDataLen
, aData
);
629 return NS_ERROR_NOT_AVAILABLE
;
634 nsFaviconService::GetDefaultFaviconData(nsCString
& byteStr
)
636 if (mDefaultFaviconData
.IsEmpty()) {
637 nsCOMPtr
<nsIURI
> defaultFaviconURI
;
638 nsresult rv
= GetDefaultFavicon(getter_AddRefs(defaultFaviconURI
));
639 NS_ENSURE_SUCCESS(rv
, rv
);
641 nsCOMPtr
<nsIInputStream
> istream
;
642 rv
= NS_OpenURI(getter_AddRefs(istream
), defaultFaviconURI
);
643 NS_ENSURE_SUCCESS(rv
, rv
);
645 rv
= NS_ConsumeStream(istream
, PR_UINT32_MAX
, mDefaultFaviconData
);
646 NS_ENSURE_SUCCESS(rv
, rv
);
648 rv
= istream
->Close();
649 NS_ENSURE_SUCCESS(rv
, rv
);
651 if (mDefaultFaviconData
.IsEmpty())
652 return NS_ERROR_UNEXPECTED
;
655 byteStr
.Assign(mDefaultFaviconData
);
661 nsFaviconService::GetFaviconDataAsDataURL(nsIURI
* aFaviconURI
,
664 NS_ENSURE_ARG(aFaviconURI
);
668 nsCAutoString mimeType
;
670 nsresult rv
= GetFaviconData(aFaviconURI
, mimeType
, &dataLen
, &data
);
671 NS_ENSURE_SUCCESS(rv
, rv
);
674 aDataURL
.SetIsVoid(PR_TRUE
);
678 char* encoded
= PL_Base64Encode(reinterpret_cast<const char*>(data
),
680 nsMemory::Free(data
);
683 return NS_ERROR_OUT_OF_MEMORY
;
685 aDataURL
.AssignLiteral("data:");
686 AppendUTF8toUTF16(mimeType
, aDataURL
);
687 aDataURL
.AppendLiteral(";base64,");
688 AppendUTF8toUTF16(encoded
, aDataURL
);
690 nsMemory::Free(encoded
);
696 nsFaviconService::GetFaviconForPage(nsIURI
* aPageURI
, nsIURI
** _retval
)
698 NS_ENSURE_ARG(aPageURI
);
699 NS_ENSURE_ARG_POINTER(_retval
);
701 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetURL
);
702 nsresult rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("page_url"), aPageURI
);
703 NS_ENSURE_SUCCESS(rv
, rv
);
706 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
708 rv
= stmt
->GetUTF8String(1, url
);
709 NS_ENSURE_SUCCESS(rv
, rv
);
711 return NS_NewURI(_retval
, url
);
713 return NS_ERROR_NOT_AVAILABLE
;
718 nsFaviconService::GetFaviconImageForPage(nsIURI
* aPageURI
, nsIURI
** _retval
)
720 NS_ENSURE_ARG(aPageURI
);
721 NS_ENSURE_ARG_POINTER(_retval
);
723 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetURL
);
724 nsresult rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("page_url"), aPageURI
);
725 NS_ENSURE_SUCCESS(rv
, rv
);
728 nsCOMPtr
<nsIURI
> faviconURI
;
729 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
731 rv
= stmt
->GetInt32(2, &dataLen
);
732 NS_ENSURE_SUCCESS(rv
, rv
);
734 // this page has a favicon entry with data
735 nsCAutoString favIconUri
;
736 rv
= stmt
->GetUTF8String(1, favIconUri
);
737 NS_ENSURE_SUCCESS(rv
, rv
);
739 return GetFaviconLinkForIconString(favIconUri
, _retval
);
743 // not found, use default
744 return GetDefaultFavicon(_retval
);
749 nsFaviconService::GetFaviconLinkForIcon(nsIURI
* aFaviconURI
,
752 NS_ENSURE_ARG(aFaviconURI
);
753 NS_ENSURE_ARG_POINTER(aOutputURI
);
757 nsresult rv
= aFaviconURI
->GetSpec(spec
);
758 NS_ENSURE_SUCCESS(rv
, rv
);
760 return GetFaviconLinkForIconString(spec
, aOutputURI
);
764 static PLDHashOperator
765 ExpireFailedFaviconsCallback(nsCStringHashKey::KeyType aKey
,
769 PRUint32
* threshold
= reinterpret_cast<PRUint32
*>(userArg
);
770 if (aData
< *threshold
)
771 return PL_DHASH_REMOVE
;
772 return PL_DHASH_NEXT
;
777 nsFaviconService::AddFailedFavicon(nsIURI
* aFaviconURI
)
779 NS_ENSURE_ARG(aFaviconURI
);
782 nsresult rv
= aFaviconURI
->GetSpec(spec
);
783 NS_ENSURE_SUCCESS(rv
, rv
);
785 if (! mFailedFavicons
.Put(spec
, mFailedFaviconSerial
))
786 return NS_ERROR_OUT_OF_MEMORY
;
787 mFailedFaviconSerial
++;
789 if (mFailedFavicons
.Count() > MAX_FAVICON_CACHE_SIZE
) {
790 // need to expire some entries, delete the FAVICON_CACHE_REDUCE_COUNT number
791 // of items that are the oldest
792 PRUint32 threshold
= mFailedFaviconSerial
-
793 MAX_FAVICON_CACHE_SIZE
+ FAVICON_CACHE_REDUCE_COUNT
;
794 mFailedFavicons
.Enumerate(ExpireFailedFaviconsCallback
, &threshold
);
801 nsFaviconService::RemoveFailedFavicon(nsIURI
* aFaviconURI
)
803 NS_ENSURE_ARG(aFaviconURI
);
806 nsresult rv
= aFaviconURI
->GetSpec(spec
);
807 NS_ENSURE_SUCCESS(rv
, rv
);
809 // we silently do nothing and succeed if the icon is not in the cache
810 mFailedFavicons
.Remove(spec
);
816 nsFaviconService::IsFailedFavicon(nsIURI
* aFaviconURI
, PRBool
* _retval
)
818 NS_ENSURE_ARG(aFaviconURI
);
820 nsresult rv
= aFaviconURI
->GetSpec(spec
);
821 NS_ENSURE_SUCCESS(rv
, rv
);
824 *_retval
= mFailedFavicons
.Get(spec
, &serial
);
829 // nsFaviconService::GetFaviconLinkForIconString
831 // This computes a favicon URL with string input and using the cached
832 // default one to minimize parsing.
835 nsFaviconService::GetFaviconLinkForIconString(const nsCString
& aSpec
,
838 if (aSpec
.IsEmpty()) {
839 // default icon for empty strings
840 if (! mDefaultIcon
) {
841 nsresult rv
= NS_NewURI(getter_AddRefs(mDefaultIcon
),
842 NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL
));
843 NS_ENSURE_SUCCESS(rv
, rv
);
845 return mDefaultIcon
->Clone(aOutput
);
848 if (StringBeginsWith(aSpec
, NS_LITERAL_CSTRING("chrome:"))) {
849 // pass through for chrome URLs, since they can be referenced without
851 return NS_NewURI(aOutput
, aSpec
);
854 nsCAutoString annoUri
;
855 annoUri
.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME
":");
857 return NS_NewURI(aOutput
, annoUri
);
861 // nsFaviconService::GetFaviconSpecForIconString
863 // This computes a favicon spec for when you don't want a URI object (as in
864 // the tree view implementation), sparing all parsing and normalization.
866 nsFaviconService::GetFaviconSpecForIconString(const nsCString
& aSpec
,
869 if (aSpec
.IsEmpty()) {
870 aOutput
.AssignLiteral(FAVICON_DEFAULT_URL
);
871 } else if (StringBeginsWith(aSpec
, NS_LITERAL_CSTRING("chrome:"))) {
874 aOutput
.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME
":");
880 // nsFaviconService::OptimizeFaviconImage
882 // Given a blob of data (a image file already read into a buffer), optimize
883 // its size by recompressing it as a 16x16 PNG.
885 nsFaviconService::OptimizeFaviconImage(const PRUint8
* aData
, PRUint32 aDataLen
,
886 const nsACString
& aMimeType
,
887 nsACString
& aNewData
,
888 nsACString
& aNewMimeType
)
892 nsCOMPtr
<imgITools
> imgtool
= do_CreateInstance("@mozilla.org/image/tools;1");
894 nsCOMPtr
<nsIInputStream
> stream
;
895 rv
= NS_NewByteInputStream(getter_AddRefs(stream
),
896 reinterpret_cast<const char*>(aData
), aDataLen
,
897 NS_ASSIGNMENT_DEPEND
);
898 NS_ENSURE_SUCCESS(rv
, rv
);
901 nsCOMPtr
<imgIContainer
> container
;
902 rv
= imgtool
->DecodeImageData(stream
, aMimeType
, getter_AddRefs(container
));
903 NS_ENSURE_SUCCESS(rv
, rv
);
905 aNewMimeType
.AssignLiteral(DEFAULT_MIME_TYPE
);
907 // scale and recompress
908 nsCOMPtr
<nsIInputStream
> iconStream
;
909 rv
= imgtool
->EncodeScaledImage(container
, aNewMimeType
,
910 mOptimizedIconDimension
,
911 mOptimizedIconDimension
,
912 getter_AddRefs(iconStream
));
913 NS_ENSURE_SUCCESS(rv
, rv
);
915 // Read the stream into a new buffer.
916 rv
= NS_ConsumeStream(iconStream
, PR_UINT32_MAX
, aNewData
);
917 NS_ENSURE_SUCCESS(rv
, rv
);
924 nsFaviconService::FinalizeStatements() {
925 mShuttingDown
= true;
927 mozIStorageStatement
* stmts
[] = {
934 mDBRemoveOnDiskReferences
,
935 mDBRemoveAllFavicons
,
938 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(stmts
); i
++) {
939 nsresult rv
= nsNavHistory::FinalizeStatement(stmts
[i
]);
940 NS_ENSURE_SUCCESS(rv
, rv
);
943 // Finalize the statementCache on the correct thread.
944 nsRefPtr
<FinalizeStatementCacheProxy
<mozIStorageStatement
> > event
=
945 new FinalizeStatementCacheProxy
<mozIStorageStatement
>(mSyncStatements
, this);
946 nsCOMPtr
<nsIEventTarget
> target
= do_GetInterface(mDBConn
);
947 NS_ENSURE_TRUE(target
, NS_ERROR_OUT_OF_MEMORY
);
948 nsresult rv
= target
->Dispatch(event
, NS_DISPATCH_NORMAL
);
949 NS_ENSURE_SUCCESS(rv
, rv
);
956 nsFaviconService::GetFaviconDataAsync(nsIURI
* aFaviconURI
,
957 mozIStorageStatementCallback
*aCallback
)
959 NS_ASSERTION(aCallback
, "Doesn't make sense to call this without a callback");
960 DECLARE_AND_ASSIGN_LAZY_STMT(stmt
, mDBGetData
);
961 nsresult rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("icon_url"), aFaviconURI
);
962 NS_ENSURE_SUCCESS(rv
, rv
);
964 nsCOMPtr
<mozIStoragePendingStatement
> pendingStatement
;
965 return stmt
->ExecuteAsync(aCallback
, getter_AddRefs(pendingStatement
));
968 ////////////////////////////////////////////////////////////////////////////////
969 //// ExpireFaviconsStatementCallbackNotifier
971 ExpireFaviconsStatementCallbackNotifier::ExpireFaviconsStatementCallbackNotifier(
972 bool* aFaviconsExpirationRunning
)
973 : mFaviconsExpirationRunning(aFaviconsExpirationRunning
)
975 NS_ASSERTION(mFaviconsExpirationRunning
, "Pointer to bool mFaviconsExpirationRunning can't be null");
980 ExpireFaviconsStatementCallbackNotifier::HandleCompletion(PRUint16 aReason
)
982 *mFaviconsExpirationRunning
= false;
984 // We should dispatch only if expiration has been successful.
985 if (aReason
!= mozIStorageStatementCallback::REASON_FINISHED
)
988 nsCOMPtr
<nsIObserverService
> observerService
=
989 mozilla::services::GetObserverService();
990 if (observerService
) {
991 (void)observerService
->NotifyObservers(nsnull
,
992 NS_PLACES_FAVICONS_EXPIRED_TOPIC_ID
,