bug 313956: expand installer .exe contents to make complete mar. r=ted.
[gecko.git] / toolkit / components / places / nsFaviconService.cpp
blob0b6cac36deeff226050e83594530cad0ab28c93a
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
13 * License.
15 * The Original Code is Places.
17 * The Initial Developer of the Original Code is
18 * Google Inc.
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
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 ***** */
42 /**
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"
55 #include "Helpers.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"
66 #include "plbase64.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
78 /**
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;
92 /**
93 * Used to notify a topic to system observers on async execute completion.
94 * Will throw on error.
96 class ExpireFaviconsStatementCallbackNotifier : public AsyncStatementCallback
98 public:
99 ExpireFaviconsStatementCallbackNotifier(bool* aFaviconsExpirationRunning);
100 NS_IMETHOD HandleCompletion(PRUint16 aReason);
102 private:
103 bool* mFaviconsExpirationRunning;
107 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsFaviconService, gFaviconService)
109 NS_IMPL_ISUPPORTS1(
110 nsFaviconService
111 , nsIFaviconService
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;
136 nsresult
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);
150 if (pb) {
151 (void)pb->GetIntPref("places.favicons.optimizeToDimension",
152 &mOptimizedIconDimension);
155 return NS_OK;
159 mozIStorageStatement*
160 nsFaviconService::GetStatement(const nsCOMPtr<mozIStorageStatement>& aStmt)
162 if (mShuttingDown)
163 return nsnull;
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 "
171 "FROM moz_places h "
172 "JOIN moz_favicons f ON h.favicon_id = f.id "
173 "WHERE h.url = :page_url "
174 "LIMIT 1"));
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(
192 "UPDATE moz_places "
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 "
199 ")"));
201 return nsnull;
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.
211 nsresult // static
212 nsFaviconService::InitTables(mozIStorageConnection* aDBConn)
214 nsresult rv;
215 PRBool exists = PR_FALSE;
216 aDBConn->TableExists(NS_LITERAL_CSTRING("moz_favicons"), &exists);
217 if (!exists) {
218 rv = aDBConn->ExecuteSimpleSQL(CREATE_MOZ_FAVICONS);
219 NS_ENSURE_SUCCESS(rv, rv);
222 return NS_OK;
226 NS_IMETHODIMP
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,
240 getter_AddRefs(ps));
241 NS_ENSURE_SUCCESS(rv, rv);
243 return NS_OK;
247 ////////////////////////////////////////////////////////////////////////////////
248 //// nsIFaviconService
250 NS_IMETHODIMP
251 nsFaviconService::SetFaviconUrlForPage(nsIURI* aPageURI, nsIURI* aFaviconURI)
253 NS_ENSURE_ARG(aPageURI);
254 NS_ENSURE_ARG(aFaviconURI);
256 if (mFaviconsExpirationRunning)
257 return NS_OK;
259 PRBool hasData;
260 nsresult rv = SetFaviconUrlForPageInternal(aPageURI, aFaviconURI, &hasData);
261 NS_ENSURE_SUCCESS(rv, rv);
263 // send favicon change notifications if the URL has any data
264 if (hasData)
265 SendFaviconNotifications(aPageURI, aFaviconURI);
266 return NS_OK;
270 NS_IMETHODIMP
271 nsFaviconService::GetDefaultFavicon(nsIURI** _retval)
273 NS_ENSURE_ARG_POINTER(_retval);
275 // not found, use default
276 if (!mDefaultIcon) {
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
291 // history.
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.
299 nsresult
300 nsFaviconService::SetFaviconUrlForPageInternal(nsIURI* aPageURI,
301 nsIURI* aFaviconURI,
302 PRBool* aHasData)
304 nsresult rv;
305 PRInt64 iconId = -1;
306 *aHasData = PR_FALSE;
308 nsNavHistory* historyService = nsNavHistory::GetHistoryService();
309 NS_ENSURE_TRUE(historyService, NS_ERROR_OUT_OF_MEMORY);
311 if (historyService->InPrivateBrowsingMode())
312 return NS_OK;
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
327 PRInt32 dataSize;
328 rv = stmt->GetInt32(1, &dataSize);
329 NS_ENSURE_SUCCESS(rv, rv);
330 if (dataSize > 0)
331 *aHasData = PR_TRUE;
335 if (iconId == -1) {
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);
351 PRBool hasResult;
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
360 PRInt64 pageId;
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);
374 return NS_OK;
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.
383 void
384 nsFaviconService::SendFaviconNotifications(nsIURI* aPageURI,
385 nsIURI* aFaviconURI)
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));
397 NS_IMETHODIMP
398 nsFaviconService::SetAndLoadFaviconForPage(nsIURI* aPageURI,
399 nsIURI* aFaviconURI,
400 PRBool aForceReload,
401 nsIFaviconDataCallback* aCallback)
403 NS_ENSURE_ARG(aPageURI);
404 NS_ENSURE_ARG(aFaviconURI);
406 if (mFaviconsExpirationRunning)
407 return NS_OK;
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) {
414 if (aForceReload)
415 RemoveFailedFavicon(aFaviconURI);
416 else
417 return NS_OK;
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,
424 mDBConn, aCallback
426 NS_ENSURE_SUCCESS(rv, rv);
428 // DB will be updated and observers notified when data has finished loading.
429 return NS_OK;
433 // nsFaviconService::SetFaviconData
435 // See the IDL for this function for lots of info. Note from there: we don't
436 // send out notifications.
438 NS_IMETHODIMP
439 nsFaviconService::SetFaviconData(nsIURI* aFaviconURI, const PRUint8* aData,
440 PRUint32 aDataLen, const nsACString& aMimeType,
441 PRTime aExpiration)
443 NS_ENSURE_ARG(aFaviconURI);
445 if (mFaviconsExpirationRunning)
446 return NS_OK;
448 nsresult rv;
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
456 // needed.
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
474 // next statement
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);
479 PRBool hasResult;
480 rv = stmt->ExecuteStep(&hasResult);
481 NS_ENSURE_SUCCESS(rv, rv);
483 if (hasResult) {
484 // Get id of the old entry and update it.
485 PRInt64 id;
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);
499 else {
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);
521 return NS_OK;
525 NS_IMETHODIMP
526 nsFaviconService::SetFaviconDataFromDataURL(nsIURI* aFaviconURI,
527 const nsAString& aDataURL,
528 PRTime aExpiration)
530 NS_ENSURE_ARG(aFaviconURI);
531 if (mFaviconsExpirationRunning)
532 return NS_OK;
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);
554 PRUint32 available;
555 rv = stream->Available(&available);
556 NS_ENSURE_SUCCESS(rv, rv);
557 if (available == 0)
558 return NS_ERROR_FAILURE;
560 // read all the decoded data
561 PRUint8* buffer = static_cast<PRUint8*>
562 (nsMemory::Alloc(sizeof(PRUint8) * available));
563 if (!buffer)
564 return NS_ERROR_OUT_OF_MEMORY;
565 PRUint32 numRead;
566 rv = stream->Read(reinterpret_cast<char*>(buffer), available, &numRead);
567 if (NS_FAILED(rv) || numRead != available) {
568 nsMemory::Free(buffer);
569 return rv;
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);
581 return NS_OK;
585 NS_IMETHODIMP
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);
611 *aData = bytes;
612 *aDataLen = defaultData.Length();
613 aMimeType.AssignLiteral(DEFAULT_MIME_TYPE);
615 return NS_OK;
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;
633 nsresult
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);
656 return NS_OK;
660 NS_IMETHODIMP
661 nsFaviconService::GetFaviconDataAsDataURL(nsIURI* aFaviconURI,
662 nsAString& aDataURL)
664 NS_ENSURE_ARG(aFaviconURI);
666 PRUint8* data;
667 PRUint32 dataLen;
668 nsCAutoString mimeType;
670 nsresult rv = GetFaviconData(aFaviconURI, mimeType, &dataLen, &data);
671 NS_ENSURE_SUCCESS(rv, rv);
673 if (!data) {
674 aDataURL.SetIsVoid(PR_TRUE);
675 return NS_OK;
678 char* encoded = PL_Base64Encode(reinterpret_cast<const char*>(data),
679 dataLen, nsnull);
680 nsMemory::Free(data);
682 if (!encoded)
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);
691 return NS_OK;
695 NS_IMETHODIMP
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);
705 PRBool hasResult;
706 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
707 nsCAutoString url;
708 rv = stmt->GetUTF8String(1, url);
709 NS_ENSURE_SUCCESS(rv, rv);
711 return NS_NewURI(_retval, url);
713 return NS_ERROR_NOT_AVAILABLE;
717 NS_IMETHODIMP
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);
727 PRBool hasResult;
728 nsCOMPtr<nsIURI> faviconURI;
729 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
730 PRInt32 dataLen;
731 rv = stmt->GetInt32(2, &dataLen);
732 NS_ENSURE_SUCCESS(rv, rv);
733 if (dataLen > 0) {
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);
748 nsresult
749 nsFaviconService::GetFaviconLinkForIcon(nsIURI* aFaviconURI,
750 nsIURI** aOutputURI)
752 NS_ENSURE_ARG(aFaviconURI);
753 NS_ENSURE_ARG_POINTER(aOutputURI);
755 nsCAutoString spec;
756 if (aFaviconURI) {
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,
766 PRUint32& aData,
767 void* userArg)
769 PRUint32* threshold = reinterpret_cast<PRUint32*>(userArg);
770 if (aData < *threshold)
771 return PL_DHASH_REMOVE;
772 return PL_DHASH_NEXT;
776 NS_IMETHODIMP
777 nsFaviconService::AddFailedFavicon(nsIURI* aFaviconURI)
779 NS_ENSURE_ARG(aFaviconURI);
781 nsCAutoString spec;
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);
796 return NS_OK;
800 NS_IMETHODIMP
801 nsFaviconService::RemoveFailedFavicon(nsIURI* aFaviconURI)
803 NS_ENSURE_ARG(aFaviconURI);
805 nsCAutoString spec;
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);
811 return NS_OK;
815 NS_IMETHODIMP
816 nsFaviconService::IsFailedFavicon(nsIURI* aFaviconURI, PRBool* _retval)
818 NS_ENSURE_ARG(aFaviconURI);
819 nsCAutoString spec;
820 nsresult rv = aFaviconURI->GetSpec(spec);
821 NS_ENSURE_SUCCESS(rv, rv);
823 PRUint32 serial;
824 *_retval = mFailedFavicons.Get(spec, &serial);
825 return NS_OK;
829 // nsFaviconService::GetFaviconLinkForIconString
831 // This computes a favicon URL with string input and using the cached
832 // default one to minimize parsing.
834 nsresult
835 nsFaviconService::GetFaviconLinkForIconString(const nsCString& aSpec,
836 nsIURI** aOutput)
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
850 // this service
851 return NS_NewURI(aOutput, aSpec);
854 nsCAutoString annoUri;
855 annoUri.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":");
856 annoUri += aSpec;
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.
865 void
866 nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec,
867 nsACString& aOutput)
869 if (aSpec.IsEmpty()) {
870 aOutput.AssignLiteral(FAVICON_DEFAULT_URL);
871 } else if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) {
872 aOutput = aSpec;
873 } else {
874 aOutput.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":");
875 aOutput += aSpec;
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.
884 nsresult
885 nsFaviconService::OptimizeFaviconImage(const PRUint8* aData, PRUint32 aDataLen,
886 const nsACString& aMimeType,
887 nsACString& aNewData,
888 nsACString& aNewMimeType)
890 nsresult rv;
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);
900 // decode image
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);
919 return NS_OK;
923 nsresult
924 nsFaviconService::FinalizeStatements() {
925 mShuttingDown = true;
927 mozIStorageStatement* stmts[] = {
928 mDBGetURL,
929 mDBGetData,
930 mDBGetIconInfo,
931 mDBInsertIcon,
932 mDBUpdateIcon,
933 mDBSetPageFavicon,
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);
951 return NS_OK;
955 nsresult
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");
979 NS_IMETHODIMP
980 ExpireFaviconsStatementCallbackNotifier::HandleCompletion(PRUint16 aReason)
982 *mFaviconsExpirationRunning = false;
984 // We should dispatch only if expiration has been successful.
985 if (aReason != mozIStorageStatementCallback::REASON_FINISHED)
986 return NS_OK;
988 nsCOMPtr<nsIObserverService> observerService =
989 mozilla::services::GetObserverService();
990 if (observerService) {
991 (void)observerService->NotifyObservers(nsnull,
992 NS_PLACES_FAVICONS_EXPIRED_TOPIC_ID,
993 nsnull);
996 return NS_OK;