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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Places.
16 : *
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.
21 : *
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>
27 : * Richard Newman <rnewman@mozilla.com>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either the GNU General Public License Version 2 or later (the "GPL"), or
31 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : /**
44 : * This is the favicon service, which stores favicons for web pages with your
45 : * history as you browse. It is also used to save the favicons for bookmarks.
46 : *
47 : * DANGER: The history query system makes assumptions about the favicon storage
48 : * so that icons can be quickly generated for history/bookmark result sets. If
49 : * you change the database layout at all, you will have to update both services.
50 : */
51 :
52 : #include "nsFaviconService.h"
53 :
54 : #include "nsNavHistory.h"
55 : #include "nsPlacesMacros.h"
56 : #include "Helpers.h"
57 : #include "AsyncFaviconHelpers.h"
58 :
59 : #include "nsNetUtil.h"
60 : #include "nsReadableUtils.h"
61 : #include "nsStreamUtils.h"
62 : #include "nsStringStream.h"
63 : #include "plbase64.h"
64 : #include "nsIClassInfoImpl.h"
65 : #include "mozilla/Preferences.h"
66 : #include "mozilla/Util.h"
67 :
68 : // For large favicons optimization.
69 : #include "imgITools.h"
70 : #include "imgIContainer.h"
71 :
72 : // Default value for mOptimizedIconDimension
73 : #define OPTIMIZED_FAVICON_DIMENSION 16
74 :
75 : #define MAX_FAVICON_CACHE_SIZE 256
76 : #define FAVICON_CACHE_REDUCE_COUNT 64
77 :
78 : #define MAX_UNASSOCIATED_FAVICONS 64
79 :
80 : // When replaceFaviconData is called, we store the icons in an in-memory cache
81 : // instead of in storage. Icons in the cache are expired according to this
82 : // interval.
83 : #define UNASSOCIATED_ICON_EXPIRY_INTERVAL 60000
84 :
85 : // The MIME type of the default favicon and favicons created by
86 : // OptimizeFaviconImage.
87 : #define DEFAULT_MIME_TYPE "image/png"
88 :
89 : using namespace mozilla;
90 : using namespace mozilla::places;
91 :
92 : /**
93 : * Used to notify a topic to system observers on async execute completion.
94 : * Will throw on error.
95 : */
96 : class ExpireFaviconsStatementCallbackNotifier : public AsyncStatementCallback
97 4 : {
98 : public:
99 : ExpireFaviconsStatementCallbackNotifier(bool* aFaviconsExpirationRunning);
100 : NS_IMETHOD HandleCompletion(PRUint16 aReason);
101 :
102 : private:
103 : bool* mFaviconsExpirationRunning;
104 : };
105 :
106 :
107 96 : PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsFaviconService, gFaviconService)
108 :
109 : NS_IMPL_CLASSINFO(nsFaviconService, NULL, 0, NS_FAVICONSERVICE_CID)
110 1812 : NS_IMPL_ISUPPORTS3_CI(
111 : nsFaviconService
112 : , nsIFaviconService
113 : , mozIAsyncFavicons
114 : , nsITimerCallback
115 79 : )
116 :
117 48 : nsFaviconService::nsFaviconService()
118 : : mFaviconsExpirationRunning(false)
119 : , mOptimizedIconDimension(OPTIMIZED_FAVICON_DIMENSION)
120 48 : , mFailedFaviconSerial(0)
121 : {
122 48 : NS_ASSERTION(!gFaviconService,
123 : "Attempting to create two instances of the service!");
124 48 : gFaviconService = this;
125 48 : }
126 :
127 :
128 96 : nsFaviconService::~nsFaviconService()
129 : {
130 48 : NS_ASSERTION(gFaviconService == this,
131 : "Deleting a non-singleton instance of the service");
132 48 : if (gFaviconService == this)
133 48 : gFaviconService = nsnull;
134 48 : }
135 :
136 :
137 : nsresult
138 48 : nsFaviconService::Init()
139 : {
140 48 : mDB = Database::GetDatabase();
141 48 : NS_ENSURE_STATE(mDB);
142 :
143 : // Init failed favicon cache.
144 48 : if (!mFailedFavicons.Init(MAX_FAVICON_CACHE_SIZE))
145 0 : return NS_ERROR_OUT_OF_MEMORY;
146 :
147 48 : if (!mUnassociatedIcons.Init(MAX_UNASSOCIATED_FAVICONS))
148 0 : return NS_ERROR_OUT_OF_MEMORY;
149 :
150 : mOptimizedIconDimension = Preferences::GetInt(
151 : "places.favicons.optimizeToDimension", OPTIMIZED_FAVICON_DIMENSION
152 48 : );
153 :
154 48 : mExpireUnassociatedIconsTimer = do_CreateInstance("@mozilla.org/timer;1");
155 48 : NS_ENSURE_STATE(mExpireUnassociatedIconsTimer);
156 :
157 48 : return NS_OK;
158 : }
159 :
160 : NS_IMETHODIMP
161 1 : nsFaviconService::ExpireAllFavicons()
162 : {
163 1 : mFaviconsExpirationRunning = true;
164 : nsCOMPtr<mozIStorageAsyncStatement> unlinkIconsStmt = mDB->GetAsyncStatement(
165 : "UPDATE moz_places "
166 : "SET favicon_id = NULL "
167 : "WHERE favicon_id NOT NULL"
168 2 : );
169 1 : NS_ENSURE_STATE(unlinkIconsStmt);
170 : nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = mDB->GetAsyncStatement(
171 : "DELETE FROM moz_favicons WHERE id NOT IN ("
172 : "SELECT favicon_id FROM moz_places WHERE favicon_id NOT NULL "
173 : ")"
174 2 : );
175 1 : NS_ENSURE_STATE(removeIconsStmt);
176 :
177 : mozIStorageBaseStatement* stmts[] = {
178 1 : unlinkIconsStmt.get()
179 1 : , removeIconsStmt.get()
180 3 : };
181 2 : nsCOMPtr<mozIStoragePendingStatement> ps;
182 : nsRefPtr<ExpireFaviconsStatementCallbackNotifier> callback =
183 2 : new ExpireFaviconsStatementCallbackNotifier(&mFaviconsExpirationRunning);
184 1 : nsresult rv = mDB->MainConn()->ExecuteAsync(
185 1 : stmts, ArrayLength(stmts), callback, getter_AddRefs(ps)
186 1 : );
187 1 : NS_ENSURE_SUCCESS(rv, rv);
188 :
189 1 : return NS_OK;
190 : }
191 :
192 : ////////////////////////////////////////////////////////////////////////////////
193 : //// nsITimerCallback
194 :
195 : static PLDHashOperator
196 0 : ExpireNonrecentUnassociatedIconsEnumerator(
197 : UnassociatedIconHashKey* aIconKey,
198 : void* aNow)
199 : {
200 0 : PRTime now = *(reinterpret_cast<PRTime*>(aNow));
201 0 : if (now - aIconKey->created >= UNASSOCIATED_ICON_EXPIRY_INTERVAL) {
202 0 : return PL_DHASH_REMOVE;
203 : }
204 0 : return PL_DHASH_NEXT;
205 : }
206 :
207 : NS_IMETHODIMP
208 1 : nsFaviconService::Notify(nsITimer* timer)
209 : {
210 1 : if (timer != mExpireUnassociatedIconsTimer.get()) {
211 1 : return NS_ERROR_INVALID_ARG;
212 : }
213 :
214 0 : PRTime now = PR_Now();
215 : mUnassociatedIcons.EnumerateEntries(
216 0 : ExpireNonrecentUnassociatedIconsEnumerator, &now);
217 : // Re-init the expiry timer if the cache isn't empty.
218 0 : if (mUnassociatedIcons.Count() > 0) {
219 0 : mExpireUnassociatedIconsTimer->InitWithCallback(
220 0 : this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT);
221 : }
222 :
223 0 : return NS_OK;
224 : }
225 :
226 : ////////////////////////////////////////////////////////////////////////////////
227 : //// nsIFaviconService
228 :
229 : NS_IMETHODIMP
230 90 : nsFaviconService::SetFaviconUrlForPage(nsIURI* aPageURI, nsIURI* aFaviconURI)
231 : {
232 90 : NS_ENSURE_ARG(aPageURI);
233 89 : NS_ENSURE_ARG(aFaviconURI);
234 :
235 : // If we are about to expire all favicons, don't bother setting a new one.
236 89 : if (mFaviconsExpirationRunning) {
237 0 : return NS_OK;
238 : }
239 :
240 89 : nsNavHistory* history = nsNavHistory::GetHistoryService();
241 89 : NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
242 :
243 89 : if (history->InPrivateBrowsingMode()) {
244 0 : return NS_OK;
245 : }
246 :
247 : nsresult rv;
248 89 : PRInt64 iconId = -1;
249 89 : bool hasData = false;
250 : {
251 : nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
252 : "SELECT id, length(data), expiration FROM moz_favicons "
253 : "WHERE url = :icon_url"
254 178 : );
255 89 : NS_ENSURE_STATE(stmt);
256 178 : mozStorageStatementScoper scoper(stmt);
257 :
258 89 : rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
259 89 : NS_ENSURE_SUCCESS(rv, rv);
260 :
261 89 : bool hasResult = false;
262 89 : if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
263 : // We already have an entry for this icon, just get its stats.
264 84 : rv = stmt->GetInt64(0, &iconId);
265 84 : NS_ENSURE_SUCCESS(rv, rv);
266 : PRInt32 dataSize;
267 84 : rv = stmt->GetInt32(1, &dataSize);
268 84 : NS_ENSURE_SUCCESS(rv, rv);
269 84 : if (dataSize > 0) {
270 84 : hasData = true;
271 : }
272 : }
273 : }
274 :
275 178 : mozStorageTransaction transaction(mDB->MainConn(), false);
276 :
277 89 : if (iconId == -1) {
278 : // We did not find any entry for this icon, so create a new one.
279 : nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
280 : "INSERT INTO moz_favicons (id, url, data, mime_type, expiration, guid) "
281 : "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration, "
282 : "COALESCE(:guid, GENERATE_GUID()))"
283 10 : );
284 5 : NS_ENSURE_STATE(stmt);
285 10 : mozStorageStatementScoper scoper(stmt);
286 :
287 5 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("guid"));
288 5 : NS_ENSURE_SUCCESS(rv, rv);
289 5 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("icon_id"));
290 5 : NS_ENSURE_SUCCESS(rv, rv);
291 5 : rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
292 5 : NS_ENSURE_SUCCESS(rv, rv);
293 5 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("data"));
294 5 : NS_ENSURE_SUCCESS(rv, rv);
295 5 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("mime_type"));
296 5 : NS_ENSURE_SUCCESS(rv, rv);
297 5 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("expiration"));
298 5 : NS_ENSURE_SUCCESS(rv, rv);
299 5 : rv = stmt->Execute();
300 5 : NS_ENSURE_SUCCESS(rv, rv);
301 :
302 : {
303 : nsCOMPtr<mozIStorageStatement> getInfoStmt = mDB->GetStatement(
304 : "SELECT id, length(data), expiration FROM moz_favicons "
305 : "WHERE url = :icon_url"
306 10 : );
307 5 : NS_ENSURE_STATE(getInfoStmt);
308 10 : mozStorageStatementScoper scoper(getInfoStmt);
309 :
310 5 : rv = URIBinder::Bind(getInfoStmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
311 5 : NS_ENSURE_SUCCESS(rv, rv);
312 : bool hasResult;
313 5 : rv = getInfoStmt->ExecuteStep(&hasResult);
314 5 : NS_ENSURE_SUCCESS(rv, rv);
315 5 : NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
316 10 : iconId = getInfoStmt->AsInt64(0);
317 : }
318 : }
319 :
320 : // Now, link our icon entry with the page.
321 : PRInt64 pageId;
322 178 : nsCAutoString guid;
323 89 : rv = history->GetOrCreateIdForPage(aPageURI, &pageId, guid);
324 89 : NS_ENSURE_SUCCESS(rv, rv);
325 :
326 : nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
327 : "UPDATE moz_places SET favicon_id = :icon_id WHERE id = :page_id"
328 178 : );
329 89 : NS_ENSURE_STATE(stmt);
330 178 : mozStorageStatementScoper scoper(stmt);
331 :
332 89 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
333 89 : NS_ENSURE_SUCCESS(rv, rv);
334 89 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), iconId);
335 89 : NS_ENSURE_SUCCESS(rv, rv);
336 89 : rv = stmt->Execute();
337 89 : NS_ENSURE_SUCCESS(rv, rv);
338 :
339 89 : rv = transaction.Commit();
340 89 : NS_ENSURE_SUCCESS(rv, rv);
341 :
342 : // Send favicon change notifications only if the icon has any data.
343 89 : if (hasData) {
344 84 : SendFaviconNotifications(aPageURI, aFaviconURI, guid);
345 : }
346 :
347 89 : return NS_OK;
348 : }
349 :
350 :
351 : NS_IMETHODIMP
352 570 : nsFaviconService::GetDefaultFavicon(nsIURI** _retval)
353 : {
354 570 : NS_ENSURE_ARG_POINTER(_retval);
355 :
356 : // not found, use default
357 570 : if (!mDefaultIcon) {
358 32 : nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon),
359 64 : NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL));
360 32 : NS_ENSURE_SUCCESS(rv, rv);
361 : }
362 570 : return mDefaultIcon->Clone(_retval);
363 : }
364 :
365 : void
366 104 : nsFaviconService::SendFaviconNotifications(nsIURI* aPageURI,
367 : nsIURI* aFaviconURI,
368 : const nsACString& aGUID)
369 : {
370 208 : nsCAutoString faviconSpec;
371 104 : nsNavHistory* history = nsNavHistory::GetHistoryService();
372 104 : if (history && NS_SUCCEEDED(aFaviconURI->GetSpec(faviconSpec))) {
373 : history->SendPageChangedNotification(aPageURI,
374 : nsINavHistoryObserver::ATTRIBUTE_FAVICON,
375 104 : NS_ConvertUTF8toUTF16(faviconSpec),
376 104 : aGUID);
377 : }
378 104 : }
379 :
380 :
381 : NS_IMETHODIMP
382 28 : nsFaviconService::SetAndLoadFaviconForPage(nsIURI* aPageURI,
383 : nsIURI* aFaviconURI,
384 : bool aForceReload,
385 : nsIFaviconDataCallback* aCallback)
386 : {
387 28 : NS_ENSURE_ARG(aPageURI);
388 25 : NS_ENSURE_ARG(aFaviconURI);
389 :
390 24 : if (mFaviconsExpirationRunning)
391 0 : return NS_OK;
392 :
393 : // If a favicon is in the failed cache, only load it during a forced reload.
394 : bool previouslyFailed;
395 24 : nsresult rv = IsFailedFavicon(aFaviconURI, &previouslyFailed);
396 24 : NS_ENSURE_SUCCESS(rv, rv);
397 24 : if (previouslyFailed) {
398 0 : if (aForceReload)
399 0 : RemoveFailedFavicon(aFaviconURI);
400 : else
401 0 : return NS_OK;
402 : }
403 :
404 : // Check if the icon already exists and fetch it from the network, if needed.
405 : // Finally associate the icon to the requested page if not yet associated.
406 : rv = AsyncFetchAndSetIconForPage::start(
407 : aFaviconURI, aPageURI, aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING,
408 : aCallback
409 24 : );
410 24 : NS_ENSURE_SUCCESS(rv, rv);
411 :
412 : // DB will be updated and observers notified when data has finished loading.
413 24 : return NS_OK;
414 : }
415 :
416 : NS_IMETHODIMP
417 15 : nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI,
418 : nsIURI* aFaviconURI,
419 : bool aForceReload,
420 : nsIFaviconDataCallback* aCallback)
421 : {
422 : return SetAndLoadFaviconForPage(aPageURI, aFaviconURI,
423 15 : aForceReload, aCallback);
424 : }
425 :
426 : NS_IMETHODIMP
427 20 : nsFaviconService::ReplaceFaviconData(nsIURI* aFaviconURI,
428 : const PRUint8* aData,
429 : PRUint32 aDataLen,
430 : const nsACString& aMimeType,
431 : PRTime aExpiration)
432 : {
433 20 : NS_ENSURE_ARG(aFaviconURI);
434 18 : NS_ENSURE_ARG(aData);
435 17 : NS_ENSURE_TRUE(aDataLen > 0, NS_ERROR_INVALID_ARG);
436 17 : NS_ENSURE_TRUE(aMimeType.Length() > 0, NS_ERROR_INVALID_ARG);
437 16 : if (aExpiration == 0) {
438 8 : aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION;
439 : }
440 :
441 16 : if (mFaviconsExpirationRunning)
442 0 : return NS_OK;
443 :
444 16 : UnassociatedIconHashKey* iconKey = mUnassociatedIcons.PutEntry(aFaviconURI);
445 16 : if (!iconKey) {
446 0 : return NS_ERROR_OUT_OF_MEMORY;
447 : }
448 :
449 16 : iconKey->created = PR_Now();
450 :
451 : // If the cache contains unassociated icons, an expiry timer should already exist, otherwise
452 : // there may be a timer left hanging around, so make sure we fire a new one.
453 16 : PRInt32 unassociatedCount = mUnassociatedIcons.Count();
454 16 : if (unassociatedCount == 1) {
455 8 : mExpireUnassociatedIconsTimer->Cancel();
456 8 : mExpireUnassociatedIconsTimer->InitWithCallback(
457 8 : this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT);
458 : }
459 :
460 16 : IconData* iconData = &(iconKey->iconData);
461 16 : iconData->expiration = aExpiration;
462 16 : iconData->status = ICON_STATUS_CACHED;
463 16 : iconData->fetchMode = FETCH_NEVER;
464 16 : nsresult rv = aFaviconURI->GetSpec(iconData->spec);
465 16 : NS_ENSURE_SUCCESS(rv, rv);
466 :
467 : // If the page provided a large image for the favicon (eg, a highres image
468 : // or a multiresolution .ico file), we don't want to store more data than
469 : // needed.
470 16 : if (aDataLen > MAX_ICON_FILESIZE(mOptimizedIconDimension)) {
471 0 : rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, iconData->data, iconData->mimeType);
472 0 : NS_ENSURE_SUCCESS(rv, rv);
473 :
474 0 : if (iconData->data.Length() > MAX_FAVICON_SIZE) {
475 : // We cannot optimize this favicon size and we are over the maximum size
476 : // allowed, so we will not save data to the db to avoid bloating it.
477 0 : mUnassociatedIcons.RemoveEntry(aFaviconURI);
478 0 : return NS_ERROR_FAILURE;
479 : }
480 : } else {
481 16 : iconData->mimeType.Assign(aMimeType);
482 16 : iconData->data.Assign(TO_CHARBUFFER(aData), aDataLen);
483 : }
484 :
485 : // If the database contains an icon at the given url, we will update the
486 : // database immediately so that the associated pages are kept in sync.
487 : // Otherwise, do nothing and let the icon be picked up from the memory hash.
488 16 : rv = AsyncReplaceFaviconData::start(iconData);
489 16 : NS_ENSURE_SUCCESS(rv, rv);
490 :
491 16 : return NS_OK;
492 : }
493 :
494 : // nsFaviconService::SetFaviconData
495 : //
496 : // See the IDL for this function for lots of info. Note from there: we don't
497 : // send out notifications.
498 :
499 : NS_IMETHODIMP
500 104 : nsFaviconService::SetFaviconData(nsIURI* aFaviconURI, const PRUint8* aData,
501 : PRUint32 aDataLen, const nsACString& aMimeType,
502 : PRTime aExpiration)
503 : {
504 104 : NS_ENSURE_ARG(aFaviconURI);
505 :
506 103 : if (mFaviconsExpirationRunning)
507 0 : return NS_OK;
508 :
509 : nsresult rv;
510 103 : PRUint32 dataLen = aDataLen;
511 103 : const PRUint8* data = aData;
512 103 : const nsACString* mimeType = &aMimeType;
513 206 : nsCString newData, newMimeType;
514 :
515 : // If the page provided a large image for the favicon (eg, a highres image
516 : // or a multiresolution .ico file), we don't want to store more data than
517 : // needed.
518 103 : if (aDataLen > MAX_ICON_FILESIZE(mOptimizedIconDimension)) {
519 7 : rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, newData, newMimeType);
520 7 : if (NS_SUCCEEDED(rv) && newData.Length() < aDataLen) {
521 7 : data = reinterpret_cast<PRUint8*>(const_cast<char*>(newData.get())),
522 7 : dataLen = newData.Length();
523 7 : mimeType = &newMimeType;
524 : }
525 0 : else if (aDataLen > MAX_FAVICON_SIZE) {
526 : // We cannot optimize this favicon size and we are over the maximum size
527 : // allowed, so we will not save data to the db to avoid bloating it.
528 0 : return NS_ERROR_FAILURE;
529 : }
530 : }
531 :
532 206 : nsCOMPtr<mozIStorageStatement> statement;
533 : {
534 : // this block forces the scoper to reset our statement: necessary for the
535 : // next statement
536 : nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
537 : "SELECT id, length(data), expiration FROM moz_favicons "
538 : "WHERE url = :icon_url"
539 206 : );
540 103 : NS_ENSURE_STATE(stmt);
541 206 : mozStorageStatementScoper scoper(stmt);
542 :
543 103 : rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
544 103 : NS_ENSURE_SUCCESS(rv, rv);
545 :
546 : bool hasResult;
547 103 : rv = stmt->ExecuteStep(&hasResult);
548 103 : NS_ENSURE_SUCCESS(rv, rv);
549 :
550 103 : if (hasResult) {
551 : // Get id of the old entry and update it.
552 : PRInt64 id;
553 45 : rv = stmt->GetInt64(0, &id);
554 45 : NS_ENSURE_SUCCESS(rv, rv);
555 : statement = mDB->GetStatement(
556 : "UPDATE moz_favicons SET "
557 : "guid = COALESCE(:guid, guid), "
558 : "data = :data, "
559 : "mime_type = :mime_type, "
560 : "expiration = :expiration "
561 : "WHERE id = :icon_id"
562 45 : );
563 45 : NS_ENSURE_STATE(statement);
564 :
565 45 : rv = statement->BindNullByName(NS_LITERAL_CSTRING("guid"));
566 45 : NS_ENSURE_SUCCESS(rv, rv);
567 45 : rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), id);
568 45 : NS_ENSURE_SUCCESS(rv, rv);
569 45 : rv = statement->BindBlobByName(NS_LITERAL_CSTRING("data"), data, dataLen);
570 45 : NS_ENSURE_SUCCESS(rv, rv);
571 45 : rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), *mimeType);
572 45 : NS_ENSURE_SUCCESS(rv, rv);
573 45 : rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aExpiration);
574 45 : NS_ENSURE_SUCCESS(rv, rv);
575 : }
576 : else {
577 : // Insert a new entry.
578 : statement = mDB->GetStatement(
579 : "INSERT INTO moz_favicons (id, url, data, mime_type, expiration, guid) "
580 : "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration, "
581 58 : "COALESCE(:guid, GENERATE_GUID()))");
582 58 : NS_ENSURE_STATE(statement);
583 :
584 58 : rv = statement->BindNullByName(NS_LITERAL_CSTRING("icon_id"));
585 58 : NS_ENSURE_SUCCESS(rv, rv);
586 58 : rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
587 58 : NS_ENSURE_SUCCESS(rv, rv);
588 58 : rv = statement->BindBlobByName(NS_LITERAL_CSTRING("data"), data, dataLen);
589 58 : NS_ENSURE_SUCCESS(rv, rv);
590 58 : rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), *mimeType);
591 58 : NS_ENSURE_SUCCESS(rv, rv);
592 58 : rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aExpiration);
593 58 : NS_ENSURE_SUCCESS(rv, rv);
594 58 : rv = statement->BindNullByName(NS_LITERAL_CSTRING("guid"));
595 58 : NS_ENSURE_SUCCESS(rv, rv);
596 : }
597 : }
598 206 : mozStorageStatementScoper statementScoper(statement);
599 :
600 103 : rv = statement->Execute();
601 103 : NS_ENSURE_SUCCESS(rv, rv);
602 :
603 103 : return NS_OK;
604 : }
605 :
606 : NS_IMETHODIMP
607 11 : nsFaviconService::ReplaceFaviconDataFromDataURL(nsIURI* aFaviconURI,
608 : const nsAString& aDataURL,
609 : PRTime aExpiration)
610 : {
611 11 : NS_ENSURE_ARG(aFaviconURI);
612 9 : NS_ENSURE_TRUE(aDataURL.Length() > 0, NS_ERROR_INVALID_ARG);
613 8 : if (aExpiration == 0) {
614 8 : aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION;
615 : }
616 :
617 8 : if (mFaviconsExpirationRunning)
618 0 : return NS_OK;
619 :
620 16 : nsCOMPtr<nsIURI> dataURI;
621 8 : nsresult rv = NS_NewURI(getter_AddRefs(dataURI), aDataURL);
622 8 : NS_ENSURE_SUCCESS(rv, rv);
623 :
624 : // Use the data: protocol handler to convert the data.
625 16 : nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
626 8 : NS_ENSURE_SUCCESS(rv, rv);
627 16 : nsCOMPtr<nsIProtocolHandler> protocolHandler;
628 8 : rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler));
629 8 : NS_ENSURE_SUCCESS(rv, rv);
630 :
631 16 : nsCOMPtr<nsIChannel> channel;
632 8 : rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel));
633 8 : NS_ENSURE_SUCCESS(rv, rv);
634 :
635 : // Blocking stream is OK for data URIs.
636 16 : nsCOMPtr<nsIInputStream> stream;
637 8 : rv = channel->Open(getter_AddRefs(stream));
638 8 : NS_ENSURE_SUCCESS(rv, rv);
639 :
640 : PRUint32 available;
641 8 : rv = stream->Available(&available);
642 8 : NS_ENSURE_SUCCESS(rv, rv);
643 8 : if (available == 0)
644 0 : return NS_ERROR_FAILURE;
645 :
646 : // Read all the decoded data.
647 : PRUint8* buffer = static_cast<PRUint8*>
648 8 : (nsMemory::Alloc(sizeof(PRUint8) * available));
649 8 : if (!buffer)
650 0 : return NS_ERROR_OUT_OF_MEMORY;
651 : PRUint32 numRead;
652 8 : rv = stream->Read(TO_CHARBUFFER(buffer), available, &numRead);
653 8 : if (NS_FAILED(rv) || numRead != available) {
654 0 : nsMemory::Free(buffer);
655 0 : return rv;
656 : }
657 :
658 16 : nsCAutoString mimeType;
659 8 : rv = channel->GetContentType(mimeType);
660 8 : if (NS_FAILED(rv)) {
661 0 : nsMemory::Free(buffer);
662 0 : return rv;
663 : }
664 :
665 : // ReplaceFaviconData can now do the dirty work.
666 8 : rv = ReplaceFaviconData(aFaviconURI, buffer, available, mimeType, aExpiration);
667 8 : nsMemory::Free(buffer);
668 8 : NS_ENSURE_SUCCESS(rv, rv);
669 :
670 8 : return NS_OK;
671 : }
672 :
673 : NS_IMETHODIMP
674 91 : nsFaviconService::SetFaviconDataFromDataURL(nsIURI* aFaviconURI,
675 : const nsAString& aDataURL,
676 : PRTime aExpiration)
677 : {
678 91 : NS_ENSURE_ARG(aFaviconURI);
679 90 : if (mFaviconsExpirationRunning)
680 0 : return NS_OK;
681 :
682 180 : nsCOMPtr<nsIURI> dataURI;
683 90 : nsresult rv = NS_NewURI(getter_AddRefs(dataURI), aDataURL);
684 90 : NS_ENSURE_SUCCESS(rv, rv);
685 :
686 : // use the data: protocol handler to convert the data
687 178 : nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
688 89 : NS_ENSURE_SUCCESS(rv, rv);
689 178 : nsCOMPtr<nsIProtocolHandler> protocolHandler;
690 89 : rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler));
691 89 : NS_ENSURE_SUCCESS(rv, rv);
692 :
693 178 : nsCOMPtr<nsIChannel> channel;
694 89 : rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel));
695 89 : NS_ENSURE_SUCCESS(rv, rv);
696 :
697 : // blocking stream is OK for data URIs
698 178 : nsCOMPtr<nsIInputStream> stream;
699 89 : rv = channel->Open(getter_AddRefs(stream));
700 89 : NS_ENSURE_SUCCESS(rv, rv);
701 :
702 : PRUint32 available;
703 89 : rv = stream->Available(&available);
704 89 : NS_ENSURE_SUCCESS(rv, rv);
705 89 : if (available == 0)
706 0 : return NS_ERROR_FAILURE;
707 :
708 : // read all the decoded data
709 : PRUint8* buffer = static_cast<PRUint8*>
710 89 : (nsMemory::Alloc(sizeof(PRUint8) * available));
711 89 : if (!buffer)
712 0 : return NS_ERROR_OUT_OF_MEMORY;
713 : PRUint32 numRead;
714 89 : rv = stream->Read(reinterpret_cast<char*>(buffer), available, &numRead);
715 89 : if (NS_FAILED(rv) || numRead != available) {
716 0 : nsMemory::Free(buffer);
717 0 : return rv;
718 : }
719 :
720 178 : nsCAutoString mimeType;
721 89 : rv = channel->GetContentType(mimeType);
722 89 : NS_ENSURE_SUCCESS(rv, rv);
723 :
724 : // SetFaviconData can now do the dirty work.
725 89 : rv = SetFaviconData(aFaviconURI, buffer, available, mimeType, aExpiration);
726 89 : nsMemory::Free(buffer);
727 89 : NS_ENSURE_SUCCESS(rv, rv);
728 :
729 89 : return NS_OK;
730 : }
731 :
732 :
733 : NS_IMETHODIMP
734 99 : nsFaviconService::GetFaviconData(nsIURI* aFaviconURI, nsACString& aMimeType,
735 : PRUint32* aDataLen, PRUint8** aData)
736 : {
737 99 : NS_ENSURE_ARG(aFaviconURI);
738 98 : NS_ENSURE_ARG_POINTER(aDataLen);
739 98 : NS_ENSURE_ARG_POINTER(aData);
740 :
741 196 : nsCOMPtr<nsIURI> defaultFaviconURI;
742 98 : nsresult rv = GetDefaultFavicon(getter_AddRefs(defaultFaviconURI));
743 98 : NS_ENSURE_SUCCESS(rv, rv);
744 :
745 98 : bool isDefaultFavicon = false;
746 98 : rv = defaultFaviconURI->Equals(aFaviconURI, &isDefaultFavicon);
747 98 : NS_ENSURE_SUCCESS(rv, rv);
748 :
749 : // If we're getting the default favicon, we need to handle it separately since
750 : // it's not in the database.
751 98 : if (isDefaultFavicon) {
752 2 : nsCAutoString defaultData;
753 1 : rv = GetDefaultFaviconData(defaultData);
754 1 : NS_ENSURE_SUCCESS(rv, rv);
755 :
756 1 : PRUint8* bytes = reinterpret_cast<PRUint8*>(ToNewCString(defaultData));
757 1 : NS_ENSURE_STATE(bytes);
758 :
759 1 : *aData = bytes;
760 1 : *aDataLen = defaultData.Length();
761 1 : aMimeType.AssignLiteral(DEFAULT_MIME_TYPE);
762 :
763 1 : return NS_OK;
764 : }
765 :
766 : nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
767 : "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url"
768 194 : );
769 97 : NS_ENSURE_STATE(stmt);
770 194 : mozStorageStatementScoper scoper(stmt);
771 :
772 97 : rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
773 97 : NS_ENSURE_SUCCESS(rv, rv);
774 :
775 97 : bool hasResult = false;
776 97 : if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
777 97 : rv = stmt->GetUTF8String(1, aMimeType);
778 97 : NS_ENSURE_SUCCESS(rv, rv);
779 :
780 97 : return stmt->GetBlob(0, aDataLen, aData);
781 : }
782 0 : return NS_ERROR_NOT_AVAILABLE;
783 : }
784 :
785 :
786 : nsresult
787 1 : nsFaviconService::GetDefaultFaviconData(nsCString& byteStr)
788 : {
789 1 : if (mDefaultFaviconData.IsEmpty()) {
790 2 : nsCOMPtr<nsIURI> defaultFaviconURI;
791 1 : nsresult rv = GetDefaultFavicon(getter_AddRefs(defaultFaviconURI));
792 1 : NS_ENSURE_SUCCESS(rv, rv);
793 :
794 2 : nsCOMPtr<nsIInputStream> istream;
795 1 : rv = NS_OpenURI(getter_AddRefs(istream), defaultFaviconURI);
796 1 : NS_ENSURE_SUCCESS(rv, rv);
797 :
798 1 : rv = NS_ConsumeStream(istream, PR_UINT32_MAX, mDefaultFaviconData);
799 1 : NS_ENSURE_SUCCESS(rv, rv);
800 :
801 1 : rv = istream->Close();
802 1 : NS_ENSURE_SUCCESS(rv, rv);
803 :
804 1 : if (mDefaultFaviconData.IsEmpty())
805 0 : return NS_ERROR_UNEXPECTED;
806 : }
807 :
808 1 : byteStr.Assign(mDefaultFaviconData);
809 1 : return NS_OK;
810 : }
811 :
812 :
813 : NS_IMETHODIMP
814 68 : nsFaviconService::GetFaviconDataAsDataURL(nsIURI* aFaviconURI,
815 : nsAString& aDataURL)
816 : {
817 68 : NS_ENSURE_ARG(aFaviconURI);
818 :
819 : PRUint8* data;
820 : PRUint32 dataLen;
821 134 : nsCAutoString mimeType;
822 :
823 67 : nsresult rv = GetFaviconData(aFaviconURI, mimeType, &dataLen, &data);
824 67 : NS_ENSURE_SUCCESS(rv, rv);
825 :
826 67 : if (!data) {
827 1 : aDataURL.SetIsVoid(true);
828 1 : return NS_OK;
829 : }
830 :
831 : char* encoded = PL_Base64Encode(reinterpret_cast<const char*>(data),
832 66 : dataLen, nsnull);
833 66 : nsMemory::Free(data);
834 :
835 66 : if (!encoded)
836 0 : return NS_ERROR_OUT_OF_MEMORY;
837 :
838 66 : aDataURL.AssignLiteral("data:");
839 66 : AppendUTF8toUTF16(mimeType, aDataURL);
840 66 : aDataURL.AppendLiteral(";base64,");
841 66 : AppendUTF8toUTF16(encoded, aDataURL);
842 :
843 66 : nsMemory::Free(encoded);
844 66 : return NS_OK;
845 : }
846 :
847 :
848 : NS_IMETHODIMP
849 118 : nsFaviconService::GetFaviconForPage(nsIURI* aPageURI, nsIURI** _retval)
850 : {
851 118 : NS_ENSURE_ARG(aPageURI);
852 117 : NS_ENSURE_ARG_POINTER(_retval);
853 :
854 : nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
855 : "SELECT f.id, f.url, length(f.data), f.expiration "
856 : "FROM moz_places h "
857 : "JOIN moz_favicons f ON h.favicon_id = f.id "
858 : "WHERE h.url = :page_url "
859 : "LIMIT 1"
860 234 : );
861 117 : NS_ENSURE_STATE(stmt);
862 234 : mozStorageStatementScoper scoper(stmt);
863 :
864 117 : nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aPageURI);
865 117 : NS_ENSURE_SUCCESS(rv, rv);
866 :
867 : bool hasResult;
868 117 : if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
869 166 : nsCAutoString url;
870 83 : rv = stmt->GetUTF8String(1, url);
871 83 : NS_ENSURE_SUCCESS(rv, rv);
872 :
873 83 : return NS_NewURI(_retval, url);
874 : }
875 34 : return NS_ERROR_NOT_AVAILABLE;
876 : }
877 :
878 :
879 : NS_IMETHODIMP
880 2 : nsFaviconService::GetFaviconURLForPage(nsIURI *aPageURI,
881 : nsIFaviconDataCallback* aCallback)
882 : {
883 2 : NS_ENSURE_ARG(aPageURI);
884 1 : NS_ENSURE_ARG(aCallback);
885 :
886 1 : nsresult rv = AsyncGetFaviconURLForPage::start(aPageURI, aCallback);
887 1 : NS_ENSURE_SUCCESS(rv, rv);
888 1 : return NS_OK;
889 : }
890 :
891 : NS_IMETHODIMP
892 2 : nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI,
893 : nsIFaviconDataCallback* aCallback)
894 : {
895 2 : NS_ENSURE_ARG(aPageURI);
896 1 : NS_ENSURE_ARG(aCallback);
897 :
898 1 : nsresult rv = AsyncGetFaviconDataForPage::start(aPageURI, aCallback);
899 1 : NS_ENSURE_SUCCESS(rv, rv);
900 1 : return NS_OK;
901 : }
902 :
903 : NS_IMETHODIMP
904 2 : nsFaviconService::GetFaviconImageForPage(nsIURI* aPageURI, nsIURI** _retval)
905 : {
906 2 : NS_ENSURE_ARG(aPageURI);
907 1 : NS_ENSURE_ARG_POINTER(_retval);
908 :
909 : nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
910 : "SELECT f.id, f.url, length(f.data), f.expiration "
911 : "FROM moz_places h "
912 : "JOIN moz_favicons f ON h.favicon_id = f.id "
913 : "WHERE h.url = :page_url "
914 : "LIMIT 1"
915 2 : );
916 1 : NS_ENSURE_STATE(stmt);
917 2 : mozStorageStatementScoper scoper(stmt);
918 :
919 1 : nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aPageURI);
920 1 : NS_ENSURE_SUCCESS(rv, rv);
921 :
922 : bool hasResult;
923 2 : nsCOMPtr<nsIURI> faviconURI;
924 1 : if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
925 : PRInt32 dataLen;
926 1 : rv = stmt->GetInt32(2, &dataLen);
927 1 : NS_ENSURE_SUCCESS(rv, rv);
928 1 : if (dataLen > 0) {
929 : // this page has a favicon entry with data
930 2 : nsCAutoString favIconUri;
931 1 : rv = stmt->GetUTF8String(1, favIconUri);
932 1 : NS_ENSURE_SUCCESS(rv, rv);
933 :
934 1 : return GetFaviconLinkForIconString(favIconUri, _retval);
935 : }
936 : }
937 :
938 : // not found, use default
939 0 : return GetDefaultFavicon(_retval);
940 : }
941 :
942 :
943 : nsresult
944 5 : nsFaviconService::GetFaviconLinkForIcon(nsIURI* aFaviconURI,
945 : nsIURI** aOutputURI)
946 : {
947 5 : NS_ENSURE_ARG(aFaviconURI);
948 2 : NS_ENSURE_ARG_POINTER(aOutputURI);
949 :
950 4 : nsCAutoString spec;
951 2 : if (aFaviconURI) {
952 2 : nsresult rv = aFaviconURI->GetSpec(spec);
953 2 : NS_ENSURE_SUCCESS(rv, rv);
954 : }
955 2 : return GetFaviconLinkForIconString(spec, aOutputURI);
956 : }
957 :
958 :
959 : static PLDHashOperator
960 0 : ExpireFailedFaviconsCallback(nsCStringHashKey::KeyType aKey,
961 : PRUint32& aData,
962 : void* userArg)
963 : {
964 0 : PRUint32* threshold = reinterpret_cast<PRUint32*>(userArg);
965 0 : if (aData < *threshold)
966 0 : return PL_DHASH_REMOVE;
967 0 : return PL_DHASH_NEXT;
968 : }
969 :
970 :
971 : NS_IMETHODIMP
972 2 : nsFaviconService::AddFailedFavicon(nsIURI* aFaviconURI)
973 : {
974 2 : NS_ENSURE_ARG(aFaviconURI);
975 :
976 2 : nsCAutoString spec;
977 1 : nsresult rv = aFaviconURI->GetSpec(spec);
978 1 : NS_ENSURE_SUCCESS(rv, rv);
979 :
980 1 : if (! mFailedFavicons.Put(spec, mFailedFaviconSerial))
981 0 : return NS_ERROR_OUT_OF_MEMORY;
982 1 : mFailedFaviconSerial ++;
983 :
984 1 : if (mFailedFavicons.Count() > MAX_FAVICON_CACHE_SIZE) {
985 : // need to expire some entries, delete the FAVICON_CACHE_REDUCE_COUNT number
986 : // of items that are the oldest
987 : PRUint32 threshold = mFailedFaviconSerial -
988 0 : MAX_FAVICON_CACHE_SIZE + FAVICON_CACHE_REDUCE_COUNT;
989 0 : mFailedFavicons.Enumerate(ExpireFailedFaviconsCallback, &threshold);
990 : }
991 1 : return NS_OK;
992 : }
993 :
994 :
995 : NS_IMETHODIMP
996 2 : nsFaviconService::RemoveFailedFavicon(nsIURI* aFaviconURI)
997 : {
998 2 : NS_ENSURE_ARG(aFaviconURI);
999 :
1000 2 : nsCAutoString spec;
1001 1 : nsresult rv = aFaviconURI->GetSpec(spec);
1002 1 : NS_ENSURE_SUCCESS(rv, rv);
1003 :
1004 : // we silently do nothing and succeed if the icon is not in the cache
1005 1 : mFailedFavicons.Remove(spec);
1006 1 : return NS_OK;
1007 : }
1008 :
1009 :
1010 : NS_IMETHODIMP
1011 27 : nsFaviconService::IsFailedFavicon(nsIURI* aFaviconURI, bool* _retval)
1012 : {
1013 27 : NS_ENSURE_ARG(aFaviconURI);
1014 52 : nsCAutoString spec;
1015 26 : nsresult rv = aFaviconURI->GetSpec(spec);
1016 26 : NS_ENSURE_SUCCESS(rv, rv);
1017 :
1018 : PRUint32 serial;
1019 26 : *_retval = mFailedFavicons.Get(spec, &serial);
1020 26 : return NS_OK;
1021 : }
1022 :
1023 :
1024 : // nsFaviconService::GetFaviconLinkForIconString
1025 : //
1026 : // This computes a favicon URL with string input and using the cached
1027 : // default one to minimize parsing.
1028 :
1029 : nsresult
1030 3 : nsFaviconService::GetFaviconLinkForIconString(const nsCString& aSpec,
1031 : nsIURI** aOutput)
1032 : {
1033 3 : if (aSpec.IsEmpty()) {
1034 : // default icon for empty strings
1035 0 : if (! mDefaultIcon) {
1036 0 : nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon),
1037 0 : NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL));
1038 0 : NS_ENSURE_SUCCESS(rv, rv);
1039 : }
1040 0 : return mDefaultIcon->Clone(aOutput);
1041 : }
1042 :
1043 3 : if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) {
1044 : // pass through for chrome URLs, since they can be referenced without
1045 : // this service
1046 0 : return NS_NewURI(aOutput, aSpec);
1047 : }
1048 :
1049 6 : nsCAutoString annoUri;
1050 3 : annoUri.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":");
1051 3 : annoUri += aSpec;
1052 3 : return NS_NewURI(aOutput, annoUri);
1053 : }
1054 :
1055 :
1056 : // nsFaviconService::GetFaviconSpecForIconString
1057 : //
1058 : // This computes a favicon spec for when you don't want a URI object (as in
1059 : // the tree view implementation), sparing all parsing and normalization.
1060 : void
1061 0 : nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec,
1062 : nsACString& aOutput)
1063 : {
1064 0 : if (aSpec.IsEmpty()) {
1065 0 : aOutput.AssignLiteral(FAVICON_DEFAULT_URL);
1066 0 : } else if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) {
1067 0 : aOutput = aSpec;
1068 : } else {
1069 0 : aOutput.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":");
1070 0 : aOutput += aSpec;
1071 : }
1072 0 : }
1073 :
1074 :
1075 : // nsFaviconService::OptimizeFaviconImage
1076 : //
1077 : // Given a blob of data (a image file already read into a buffer), optimize
1078 : // its size by recompressing it as a 16x16 PNG.
1079 : nsresult
1080 7 : nsFaviconService::OptimizeFaviconImage(const PRUint8* aData, PRUint32 aDataLen,
1081 : const nsACString& aMimeType,
1082 : nsACString& aNewData,
1083 : nsACString& aNewMimeType)
1084 : {
1085 : nsresult rv;
1086 :
1087 14 : nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
1088 :
1089 14 : nsCOMPtr<nsIInputStream> stream;
1090 7 : rv = NS_NewByteInputStream(getter_AddRefs(stream),
1091 : reinterpret_cast<const char*>(aData), aDataLen,
1092 7 : NS_ASSIGNMENT_DEPEND);
1093 7 : NS_ENSURE_SUCCESS(rv, rv);
1094 :
1095 : // decode image
1096 14 : nsCOMPtr<imgIContainer> container;
1097 7 : rv = imgtool->DecodeImageData(stream, aMimeType, getter_AddRefs(container));
1098 7 : NS_ENSURE_SUCCESS(rv, rv);
1099 :
1100 7 : aNewMimeType.AssignLiteral(DEFAULT_MIME_TYPE);
1101 :
1102 : // scale and recompress
1103 14 : nsCOMPtr<nsIInputStream> iconStream;
1104 7 : rv = imgtool->EncodeScaledImage(container, aNewMimeType,
1105 : mOptimizedIconDimension,
1106 : mOptimizedIconDimension,
1107 7 : EmptyString(),
1108 14 : getter_AddRefs(iconStream));
1109 7 : NS_ENSURE_SUCCESS(rv, rv);
1110 :
1111 : // Read the stream into a new buffer.
1112 7 : rv = NS_ConsumeStream(iconStream, PR_UINT32_MAX, aNewData);
1113 7 : NS_ENSURE_SUCCESS(rv, rv);
1114 :
1115 7 : return NS_OK;
1116 : }
1117 :
1118 : nsresult
1119 2 : nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI,
1120 : mozIStorageStatementCallback *aCallback)
1121 : {
1122 2 : NS_ASSERTION(aCallback, "Doesn't make sense to call this without a callback");
1123 : nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
1124 : "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url"
1125 4 : );
1126 2 : NS_ENSURE_STATE(stmt);
1127 :
1128 2 : nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
1129 2 : NS_ENSURE_SUCCESS(rv, rv);
1130 :
1131 4 : nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
1132 2 : return stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
1133 : }
1134 :
1135 : ////////////////////////////////////////////////////////////////////////////////
1136 : //// ExpireFaviconsStatementCallbackNotifier
1137 :
1138 1 : ExpireFaviconsStatementCallbackNotifier::ExpireFaviconsStatementCallbackNotifier(
1139 : bool* aFaviconsExpirationRunning)
1140 1 : : mFaviconsExpirationRunning(aFaviconsExpirationRunning)
1141 : {
1142 1 : NS_ASSERTION(mFaviconsExpirationRunning, "Pointer to bool mFaviconsExpirationRunning can't be null");
1143 1 : }
1144 :
1145 :
1146 : NS_IMETHODIMP
1147 1 : ExpireFaviconsStatementCallbackNotifier::HandleCompletion(PRUint16 aReason)
1148 : {
1149 1 : *mFaviconsExpirationRunning = false;
1150 :
1151 : // We should dispatch only if expiration has been successful.
1152 1 : if (aReason != mozIStorageStatementCallback::REASON_FINISHED)
1153 0 : return NS_OK;
1154 :
1155 : nsCOMPtr<nsIObserverService> observerService =
1156 2 : mozilla::services::GetObserverService();
1157 1 : if (observerService) {
1158 1 : (void)observerService->NotifyObservers(nsnull,
1159 : NS_PLACES_FAVICONS_EXPIRED_TOPIC_ID,
1160 1 : nsnull);
1161 : }
1162 :
1163 1 : return NS_OK;
1164 : }
|