1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /**
8 * This is the favicon service, which stores favicons for web pages with your
9 * history as you browse. It is also used to save the favicons for bookmarks.
10 *
11 * DANGER: The history query system makes assumptions about the favicon storage
12 * so that icons can be quickly generated for history/bookmark result sets. If
13 * you change the database layout at all, you will have to update both services.
14 */
15
16 #include "nsFaviconService.h"
17
18 #include "nsNavHistory.h"
19 #include "nsPlacesMacros.h"
20 #include "Helpers.h"
21
22 #include "nsNetUtil.h"
23 #include "nsReadableUtils.h"
24 #include "nsStreamUtils.h"
25 #include "nsStringStream.h"
26 #include "plbase64.h"
27 #include "nsIClassInfoImpl.h"
28 #include "mozilla/ArrayUtils.h"
29 #include "mozilla/LoadInfo.h"
30 #include "mozilla/Preferences.h"
31 #include "nsILoadInfo.h"
32 #include "nsIContentPolicy.h"
33 #include "nsContentUtils.h"
34 #include "nsNullPrincipal.h"
35
36 // For large favicons optimization.
37 #include "imgITools.h"
38 #include "imgIContainer.h"
39
40 // The target dimension, in pixels, for favicons we optimize.
41 #define OPTIMIZED_FAVICON_DIMENSION 32
42
43 #define MAX_FAILED_FAVICONS 256
44 #define FAVICON_CACHE_REDUCE_COUNT 64
45
46 #define UNASSOCIATED_FAVICONS_LENGTH 32
47
48 // When replaceFaviconData is called, we store the icons in an in-memory cache
49 // instead of in storage. Icons in the cache are expired according to this
50 // interval.
51 #define UNASSOCIATED_ICON_EXPIRY_INTERVAL 60000
52
53 // The MIME type of the default favicon and favicons created by
54 // OptimizeFaviconImage.
55 #define DEFAULT_MIME_TYPE "image/png"
56
57 using namespace mozilla;
58 using namespace mozilla::places;
59
60 /**
61 * Used to notify a topic to system observers on async execute completion.
62 * Will throw on error.
63 */
64 class ExpireFaviconsStatementCallbackNotifier : public AsyncStatementCallback
65 {
66 public:
67 ExpireFaviconsStatementCallbackNotifier();
68 NS_IMETHOD HandleCompletion(uint16_t aReason);
69 };
70
71
PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsFaviconService,gFaviconService)72 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsFaviconService, gFaviconService)
73
74 NS_IMPL_CLASSINFO(nsFaviconService, nullptr, 0, NS_FAVICONSERVICE_CID)
75 NS_IMPL_ISUPPORTS_CI(
76 nsFaviconService
77 , nsIFaviconService
78 , mozIAsyncFavicons
79 , nsITimerCallback
80 )
81
82 nsFaviconService::nsFaviconService()
83 : mFailedFaviconSerial(0)
84 , mFailedFavicons(MAX_FAILED_FAVICONS / 2)
85 , mUnassociatedIcons(UNASSOCIATED_FAVICONS_LENGTH)
86 {
87 NS_ASSERTION(!gFaviconService,
88 "Attempting to create two instances of the service!");
89 gFaviconService = this;
90 }
91
92
~nsFaviconService()93 nsFaviconService::~nsFaviconService()
94 {
95 NS_ASSERTION(gFaviconService == this,
96 "Deleting a non-singleton instance of the service");
97 if (gFaviconService == this)
98 gFaviconService = nullptr;
99 }
100
101
102 nsresult
Init()103 nsFaviconService::Init()
104 {
105 mDB = Database::GetDatabase();
106 NS_ENSURE_STATE(mDB);
107
108 mExpireUnassociatedIconsTimer = do_CreateInstance("@mozilla.org/timer;1");
109 NS_ENSURE_STATE(mExpireUnassociatedIconsTimer);
110
111 return NS_OK;
112 }
113
114 NS_IMETHODIMP
ExpireAllFavicons()115 nsFaviconService::ExpireAllFavicons()
116 {
117 nsCOMPtr<mozIStorageAsyncStatement> unlinkIconsStmt = mDB->GetAsyncStatement(
118 "UPDATE moz_places "
119 "SET favicon_id = NULL "
120 "WHERE favicon_id NOT NULL"
121 );
122 NS_ENSURE_STATE(unlinkIconsStmt);
123 nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = mDB->GetAsyncStatement(
124 "DELETE FROM moz_favicons WHERE id NOT IN ("
125 "SELECT favicon_id FROM moz_places WHERE favicon_id NOT NULL "
126 ")"
127 );
128 NS_ENSURE_STATE(removeIconsStmt);
129
130 mozIStorageBaseStatement* stmts[] = {
131 unlinkIconsStmt.get()
132 , removeIconsStmt.get()
133 };
134 nsCOMPtr<mozIStoragePendingStatement> ps;
135 RefPtr<ExpireFaviconsStatementCallbackNotifier> callback =
136 new ExpireFaviconsStatementCallbackNotifier();
137 nsresult rv = mDB->MainConn()->ExecuteAsync(
138 stmts, ArrayLength(stmts), callback, getter_AddRefs(ps)
139 );
140 NS_ENSURE_SUCCESS(rv, rv);
141
142 return NS_OK;
143 }
144
145 ////////////////////////////////////////////////////////////////////////////////
146 //// nsITimerCallback
147
148 NS_IMETHODIMP
Notify(nsITimer * timer)149 nsFaviconService::Notify(nsITimer* timer)
150 {
151 if (timer != mExpireUnassociatedIconsTimer.get()) {
152 return NS_ERROR_INVALID_ARG;
153 }
154
155 PRTime now = PR_Now();
156 for (auto iter = mUnassociatedIcons.Iter(); !iter.Done(); iter.Next()) {
157 UnassociatedIconHashKey* iconKey = iter.Get();
158 if (now - iconKey->created >= UNASSOCIATED_ICON_EXPIRY_INTERVAL) {
159 iter.Remove();
160 }
161 }
162
163 // Re-init the expiry timer if the cache isn't empty.
164 if (mUnassociatedIcons.Count() > 0) {
165 mExpireUnassociatedIconsTimer->InitWithCallback(
166 this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT);
167 }
168
169 return NS_OK;
170 }
171
172 ////////////////////////////////////////////////////////////////////////////////
173 //// nsIFaviconService
174
175 NS_IMETHODIMP
GetDefaultFavicon(nsIURI ** _retval)176 nsFaviconService::GetDefaultFavicon(nsIURI** _retval)
177 {
178 NS_ENSURE_ARG_POINTER(_retval);
179
180 // not found, use default
181 if (!mDefaultIcon) {
182 nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon),
183 NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL));
184 NS_ENSURE_SUCCESS(rv, rv);
185 }
186 return mDefaultIcon->Clone(_retval);
187 }
188
189 void
SendFaviconNotifications(nsIURI * aPageURI,nsIURI * aFaviconURI,const nsACString & aGUID)190 nsFaviconService::SendFaviconNotifications(nsIURI* aPageURI,
191 nsIURI* aFaviconURI,
192 const nsACString& aGUID)
193 {
194 nsAutoCString faviconSpec;
195 nsNavHistory* history = nsNavHistory::GetHistoryService();
196 if (history && NS_SUCCEEDED(aFaviconURI->GetSpec(faviconSpec))) {
197 history->SendPageChangedNotification(aPageURI,
198 nsINavHistoryObserver::ATTRIBUTE_FAVICON,
199 NS_ConvertUTF8toUTF16(faviconSpec),
200 aGUID);
201 }
202 }
203
204 NS_IMETHODIMP
SetAndFetchFaviconForPage(nsIURI * aPageURI,nsIURI * aFaviconURI,bool aForceReload,uint32_t aFaviconLoadType,nsIFaviconDataCallback * aCallback,nsIPrincipal * aLoadingPrincipal,mozIPlacesPendingOperation ** _canceler)205 nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI,
206 nsIURI* aFaviconURI,
207 bool aForceReload,
208 uint32_t aFaviconLoadType,
209 nsIFaviconDataCallback* aCallback,
210 nsIPrincipal* aLoadingPrincipal,
211 mozIPlacesPendingOperation **_canceler)
212 {
213 MOZ_ASSERT(NS_IsMainThread());
214 NS_ENSURE_ARG(aPageURI);
215 NS_ENSURE_ARG(aFaviconURI);
216 NS_ENSURE_ARG_POINTER(_canceler);
217
218 // If a favicon is in the failed cache, only load it during a forced reload.
219 bool previouslyFailed;
220 nsresult rv = IsFailedFavicon(aFaviconURI, &previouslyFailed);
221 NS_ENSURE_SUCCESS(rv, rv);
222 if (previouslyFailed) {
223 if (aForceReload)
224 RemoveFailedFavicon(aFaviconURI);
225 else
226 return NS_OK;
227 }
228
229 nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadingPrincipal;
230 MOZ_ASSERT(loadingPrincipal, "please provide aLoadingPrincipal for this favicon");
231 if (!loadingPrincipal) {
232 // Let's default to the nullPrincipal if no loadingPrincipal is provided.
233 const char16_t* params[] = {
234 u"nsFaviconService::setAndFetchFaviconForPage()",
235 u"nsFaviconService::setAndFetchFaviconForPage(..., [optional aLoadingPrincipal])"
236 };
237 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
238 NS_LITERAL_CSTRING("Security by Default"),
239 nullptr, // aDocument
240 nsContentUtils::eNECKO_PROPERTIES,
241 "APIDeprecationWarning",
242 params, ArrayLength(params));
243 loadingPrincipal = nsNullPrincipal::Create();
244 }
245 NS_ENSURE_TRUE(loadingPrincipal, NS_ERROR_FAILURE);
246
247 // Check if the icon already exists and fetch it from the network, if needed.
248 // Finally associate the icon to the requested page if not yet associated.
249 bool loadPrivate = aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE;
250
251 PageData page;
252 rv = aPageURI->GetSpec(page.spec);
253 NS_ENSURE_SUCCESS(rv, rv);
254 // URIs can arguably miss a host.
255 (void)GetReversedHostname(aPageURI, page.revHost);
256 bool canAddToHistory;
257 nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
258 NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
259 rv = navHistory->CanAddURI(aPageURI, &canAddToHistory);
260 NS_ENSURE_SUCCESS(rv, rv);
261 page.canAddToHistory = !!canAddToHistory && !loadPrivate;
262
263 IconData icon;
264 UnassociatedIconHashKey* iconKey = mUnassociatedIcons.GetEntry(aFaviconURI);
265 if (iconKey) {
266 icon = iconKey->iconData;
267 mUnassociatedIcons.RemoveEntry(iconKey);
268 } else {
269 icon.fetchMode = aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING;
270 rv = aFaviconURI->GetSpec(icon.spec);
271 NS_ENSURE_SUCCESS(rv, rv);
272 }
273
274 // If the page url points to an image, the icon's url will be the same.
275 // In future evaluate to store a resample of the image. For now avoid that
276 // for database size concerns.
277 // Don't store favicons for error pages too.
278 if (icon.spec.Equals(page.spec) ||
279 icon.spec.Equals(FAVICON_ERRORPAGE_URL)) {
280 return NS_OK;
281 }
282
283 RefPtr<AsyncFetchAndSetIconForPage> event =
284 new AsyncFetchAndSetIconForPage(icon, page, loadPrivate,
285 aCallback, aLoadingPrincipal);
286
287 // Get the target thread and start the work.
288 // DB will be updated and observers notified when data has finished loading.
289 RefPtr<Database> DB = Database::GetDatabase();
290 NS_ENSURE_STATE(DB);
291 DB->DispatchToAsyncThread(event);
292
293 // Return this event to the caller to allow aborting an eventual fetch.
294 event.forget(_canceler);
295
296 return NS_OK;
297 }
298
299 NS_IMETHODIMP
ReplaceFaviconData(nsIURI * aFaviconURI,const uint8_t * aData,uint32_t aDataLen,const nsACString & aMimeType,PRTime aExpiration)300 nsFaviconService::ReplaceFaviconData(nsIURI* aFaviconURI,
301 const uint8_t* aData,
302 uint32_t aDataLen,
303 const nsACString& aMimeType,
304 PRTime aExpiration)
305 {
306 MOZ_ASSERT(NS_IsMainThread());
307 NS_ENSURE_ARG(aFaviconURI);
308 NS_ENSURE_ARG(aData);
309 NS_ENSURE_TRUE(aDataLen > 0, NS_ERROR_INVALID_ARG);
310 NS_ENSURE_TRUE(aMimeType.Length() > 0, NS_ERROR_INVALID_ARG);
311 if (aExpiration == 0) {
312 aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION;
313 }
314
315 UnassociatedIconHashKey* iconKey = mUnassociatedIcons.PutEntry(aFaviconURI);
316 if (!iconKey) {
317 return NS_ERROR_OUT_OF_MEMORY;
318 }
319
320 iconKey->created = PR_Now();
321
322 // If the cache contains unassociated icons, an expiry timer should already exist, otherwise
323 // there may be a timer left hanging around, so make sure we fire a new one.
324 int32_t unassociatedCount = mUnassociatedIcons.Count();
325 if (unassociatedCount == 1) {
326 mExpireUnassociatedIconsTimer->Cancel();
327 mExpireUnassociatedIconsTimer->InitWithCallback(
328 this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT);
329 }
330
331 IconData* iconData = &(iconKey->iconData);
332 iconData->expiration = aExpiration;
333 iconData->status = ICON_STATUS_CACHED;
334 iconData->fetchMode = FETCH_NEVER;
335 nsresult rv = aFaviconURI->GetSpec(iconData->spec);
336 NS_ENSURE_SUCCESS(rv, rv);
337
338 // If the page provided a large image for the favicon (eg, a highres image
339 // or a multiresolution .ico file), we don't want to store more data than
340 // needed.
341 if (aDataLen > MAX_FAVICON_FILESIZE) {
342 rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, iconData->data, iconData->mimeType);
343 NS_ENSURE_SUCCESS(rv, rv);
344
345 if (iconData->data.Length() > nsIFaviconService::MAX_FAVICON_BUFFER_SIZE) {
346 // We cannot optimize this favicon size and we are over the maximum size
347 // allowed, so we will not save data to the db to avoid bloating it.
348 mUnassociatedIcons.RemoveEntry(aFaviconURI);
349 return NS_ERROR_FAILURE;
350 }
351 } else {
352 iconData->mimeType.Assign(aMimeType);
353 iconData->data.Assign(TO_CHARBUFFER(aData), aDataLen);
354 }
355
356 // If the database contains an icon at the given url, we will update the
357 // database immediately so that the associated pages are kept in sync.
358 // Otherwise, do nothing and let the icon be picked up from the memory hash.
359 RefPtr<AsyncReplaceFaviconData> event = new AsyncReplaceFaviconData(*iconData);
360 RefPtr<Database> DB = Database::GetDatabase();
361 NS_ENSURE_STATE(DB);
362 DB->DispatchToAsyncThread(event);
363
364 return NS_OK;
365 }
366
367 NS_IMETHODIMP
ReplaceFaviconDataFromDataURL(nsIURI * aFaviconURI,const nsAString & aDataURL,PRTime aExpiration,nsIPrincipal * aLoadingPrincipal)368 nsFaviconService::ReplaceFaviconDataFromDataURL(nsIURI* aFaviconURI,
369 const nsAString& aDataURL,
370 PRTime aExpiration,
371 nsIPrincipal* aLoadingPrincipal)
372 {
373 NS_ENSURE_ARG(aFaviconURI);
374 NS_ENSURE_TRUE(aDataURL.Length() > 0, NS_ERROR_INVALID_ARG);
375 if (aExpiration == 0) {
376 aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION;
377 }
378
379 nsCOMPtr<nsIURI> dataURI;
380 nsresult rv = NS_NewURI(getter_AddRefs(dataURI), aDataURL);
381 NS_ENSURE_SUCCESS(rv, rv);
382
383 // Use the data: protocol handler to convert the data.
384 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
385 NS_ENSURE_SUCCESS(rv, rv);
386 nsCOMPtr<nsIProtocolHandler> protocolHandler;
387 rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler));
388 NS_ENSURE_SUCCESS(rv, rv);
389
390 nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadingPrincipal;
391 MOZ_ASSERT(loadingPrincipal, "please provide aLoadingPrincipal for this favicon");
392 if (!loadingPrincipal) {
393 // Let's default to the nullPrincipal if no loadingPrincipal is provided.
394 const char16_t* params[] = {
395 u"nsFaviconService::ReplaceFaviconDataFromDataURL()",
396 u"nsFaviconService::ReplaceFaviconDataFromDataURL(..., [optional aLoadingPrincipal])"
397 };
398 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
399 NS_LITERAL_CSTRING("Security by Default"),
400 nullptr, // aDocument
401 nsContentUtils::eNECKO_PROPERTIES,
402 "APIDeprecationWarning",
403 params, ArrayLength(params));
404
405 loadingPrincipal = nsNullPrincipal::Create();
406 }
407 NS_ENSURE_TRUE(loadingPrincipal, NS_ERROR_FAILURE);
408
409 nsCOMPtr<nsILoadInfo> loadInfo =
410 new mozilla::LoadInfo(loadingPrincipal,
411 nullptr, // aTriggeringPrincipal
412 nullptr, // aLoadingNode
413 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
414 nsILoadInfo::SEC_ALLOW_CHROME |
415 nsILoadInfo::SEC_DISALLOW_SCRIPT,
416 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON);
417
418 nsCOMPtr<nsIChannel> channel;
419 rv = protocolHandler->NewChannel2(dataURI, loadInfo, getter_AddRefs(channel));
420 NS_ENSURE_SUCCESS(rv, rv);
421
422 // Blocking stream is OK for data URIs.
423 nsCOMPtr<nsIInputStream> stream;
424 rv = channel->Open2(getter_AddRefs(stream));
425 NS_ENSURE_SUCCESS(rv, rv);
426
427 uint64_t available64;
428 rv = stream->Available(&available64);
429 NS_ENSURE_SUCCESS(rv, rv);
430 if (available64 == 0 || available64 > UINT32_MAX / sizeof(uint8_t))
431 return NS_ERROR_FILE_TOO_BIG;
432 uint32_t available = (uint32_t)available64;
433
434 // Read all the decoded data.
435 uint8_t* buffer = static_cast<uint8_t*>
436 (moz_xmalloc(sizeof(uint8_t) * available));
437 if (!buffer)
438 return NS_ERROR_OUT_OF_MEMORY;
439 uint32_t numRead;
440 rv = stream->Read(TO_CHARBUFFER(buffer), available, &numRead);
441 if (NS_FAILED(rv) || numRead != available) {
442 free(buffer);
443 return rv;
444 }
445
446 nsAutoCString mimeType;
447 rv = channel->GetContentType(mimeType);
448 if (NS_FAILED(rv)) {
449 free(buffer);
450 return rv;
451 }
452
453 // ReplaceFaviconData can now do the dirty work.
454 rv = ReplaceFaviconData(aFaviconURI, buffer, available, mimeType, aExpiration);
455 free(buffer);
456 NS_ENSURE_SUCCESS(rv, rv);
457
458 return NS_OK;
459 }
460
461 NS_IMETHODIMP
GetFaviconURLForPage(nsIURI * aPageURI,nsIFaviconDataCallback * aCallback)462 nsFaviconService::GetFaviconURLForPage(nsIURI *aPageURI,
463 nsIFaviconDataCallback* aCallback)
464 {
465 MOZ_ASSERT(NS_IsMainThread());
466 NS_ENSURE_ARG(aPageURI);
467 NS_ENSURE_ARG(aCallback);
468
469 nsAutoCString pageSpec;
470 nsresult rv = aPageURI->GetSpec(pageSpec);
471 NS_ENSURE_SUCCESS(rv, rv);
472
473 RefPtr<AsyncGetFaviconURLForPage> event =
474 new AsyncGetFaviconURLForPage(pageSpec, aCallback);
475
476 RefPtr<Database> DB = Database::GetDatabase();
477 NS_ENSURE_STATE(DB);
478 DB->DispatchToAsyncThread(event);
479
480 return NS_OK;
481 }
482
483 NS_IMETHODIMP
GetFaviconDataForPage(nsIURI * aPageURI,nsIFaviconDataCallback * aCallback)484 nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI,
485 nsIFaviconDataCallback* aCallback)
486 {
487 MOZ_ASSERT(NS_IsMainThread());
488 NS_ENSURE_ARG(aPageURI);
489 NS_ENSURE_ARG(aCallback);
490
491 nsAutoCString pageSpec;
492 nsresult rv = aPageURI->GetSpec(pageSpec);
493 NS_ENSURE_SUCCESS(rv, rv);
494
495 RefPtr<AsyncGetFaviconDataForPage> event =
496 new AsyncGetFaviconDataForPage(pageSpec, aCallback);
497 RefPtr<Database> DB = Database::GetDatabase();
498 NS_ENSURE_STATE(DB);
499 DB->DispatchToAsyncThread(event);
500
501 return NS_OK;
502 }
503
504 nsresult
GetFaviconLinkForIcon(nsIURI * aFaviconURI,nsIURI ** aOutputURI)505 nsFaviconService::GetFaviconLinkForIcon(nsIURI* aFaviconURI,
506 nsIURI** aOutputURI)
507 {
508 NS_ENSURE_ARG(aFaviconURI);
509 NS_ENSURE_ARG_POINTER(aOutputURI);
510
511 nsAutoCString spec;
512 if (aFaviconURI) {
513 nsresult rv = aFaviconURI->GetSpec(spec);
514 NS_ENSURE_SUCCESS(rv, rv);
515 }
516 return GetFaviconLinkForIconString(spec, aOutputURI);
517 }
518
519
520 NS_IMETHODIMP
AddFailedFavicon(nsIURI * aFaviconURI)521 nsFaviconService::AddFailedFavicon(nsIURI* aFaviconURI)
522 {
523 NS_ENSURE_ARG(aFaviconURI);
524
525 nsAutoCString spec;
526 nsresult rv = aFaviconURI->GetSpec(spec);
527 NS_ENSURE_SUCCESS(rv, rv);
528
529 mFailedFavicons.Put(spec, mFailedFaviconSerial);
530 mFailedFaviconSerial ++;
531
532 if (mFailedFavicons.Count() > MAX_FAILED_FAVICONS) {
533 // need to expire some entries, delete the FAVICON_CACHE_REDUCE_COUNT number
534 // of items that are the oldest
535 uint32_t threshold = mFailedFaviconSerial -
536 MAX_FAILED_FAVICONS + FAVICON_CACHE_REDUCE_COUNT;
537 for (auto iter = mFailedFavicons.Iter(); !iter.Done(); iter.Next()) {
538 if (iter.Data() < threshold) {
539 iter.Remove();
540 }
541 }
542 }
543 return NS_OK;
544 }
545
546
547 NS_IMETHODIMP
RemoveFailedFavicon(nsIURI * aFaviconURI)548 nsFaviconService::RemoveFailedFavicon(nsIURI* aFaviconURI)
549 {
550 NS_ENSURE_ARG(aFaviconURI);
551
552 nsAutoCString spec;
553 nsresult rv = aFaviconURI->GetSpec(spec);
554 NS_ENSURE_SUCCESS(rv, rv);
555
556 // we silently do nothing and succeed if the icon is not in the cache
557 mFailedFavicons.Remove(spec);
558 return NS_OK;
559 }
560
561
562 NS_IMETHODIMP
IsFailedFavicon(nsIURI * aFaviconURI,bool * _retval)563 nsFaviconService::IsFailedFavicon(nsIURI* aFaviconURI, bool* _retval)
564 {
565 NS_ENSURE_ARG(aFaviconURI);
566 nsAutoCString spec;
567 nsresult rv = aFaviconURI->GetSpec(spec);
568 NS_ENSURE_SUCCESS(rv, rv);
569
570 uint32_t serial;
571 *_retval = mFailedFavicons.Get(spec, &serial);
572 return NS_OK;
573 }
574
575
576 // nsFaviconService::GetFaviconLinkForIconString
577 //
578 // This computes a favicon URL with string input and using the cached
579 // default one to minimize parsing.
580
581 nsresult
GetFaviconLinkForIconString(const nsCString & aSpec,nsIURI ** aOutput)582 nsFaviconService::GetFaviconLinkForIconString(const nsCString& aSpec,
583 nsIURI** aOutput)
584 {
585 if (aSpec.IsEmpty()) {
586 // default icon for empty strings
587 if (! mDefaultIcon) {
588 nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon),
589 NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL));
590 NS_ENSURE_SUCCESS(rv, rv);
591 }
592 return mDefaultIcon->Clone(aOutput);
593 }
594
595 if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) {
596 // pass through for chrome URLs, since they can be referenced without
597 // this service
598 return NS_NewURI(aOutput, aSpec);
599 }
600
601 nsAutoCString annoUri;
602 annoUri.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":");
603 annoUri += aSpec;
604 return NS_NewURI(aOutput, annoUri);
605 }
606
607
608 // nsFaviconService::GetFaviconSpecForIconString
609 //
610 // This computes a favicon spec for when you don't want a URI object (as in
611 // the tree view implementation), sparing all parsing and normalization.
612 void
GetFaviconSpecForIconString(const nsCString & aSpec,nsACString & aOutput)613 nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec,
614 nsACString& aOutput)
615 {
616 if (aSpec.IsEmpty()) {
617 aOutput.AssignLiteral(FAVICON_DEFAULT_URL);
618 } else if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) {
619 aOutput = aSpec;
620 } else {
621 aOutput.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":");
622 aOutput += aSpec;
623 }
624 }
625
626
627 // nsFaviconService::OptimizeFaviconImage
628 //
629 // Given a blob of data (a image file already read into a buffer), optimize
630 // its size by recompressing it as a 16x16 PNG.
631 nsresult
OptimizeFaviconImage(const uint8_t * aData,uint32_t aDataLen,const nsACString & aMimeType,nsACString & aNewData,nsACString & aNewMimeType)632 nsFaviconService::OptimizeFaviconImage(const uint8_t* aData, uint32_t aDataLen,
633 const nsACString& aMimeType,
634 nsACString& aNewData,
635 nsACString& aNewMimeType)
636 {
637 nsresult rv;
638
639 nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
640
641 nsCOMPtr<nsIInputStream> stream;
642 rv = NS_NewByteInputStream(getter_AddRefs(stream),
643 reinterpret_cast<const char*>(aData), aDataLen,
644 NS_ASSIGNMENT_DEPEND);
645 NS_ENSURE_SUCCESS(rv, rv);
646
647 // decode image
648 nsCOMPtr<imgIContainer> container;
649 rv = imgtool->DecodeImageData(stream, aMimeType, getter_AddRefs(container));
650 NS_ENSURE_SUCCESS(rv, rv);
651
652 aNewMimeType.AssignLiteral(DEFAULT_MIME_TYPE);
653
654 // scale and recompress
655 nsCOMPtr<nsIInputStream> iconStream;
656 rv = imgtool->EncodeScaledImage(container, aNewMimeType,
657 OPTIMIZED_FAVICON_DIMENSION,
658 OPTIMIZED_FAVICON_DIMENSION,
659 EmptyString(),
660 getter_AddRefs(iconStream));
661 NS_ENSURE_SUCCESS(rv, rv);
662
663 // Read the stream into a new buffer.
664 rv = NS_ConsumeStream(iconStream, UINT32_MAX, aNewData);
665 NS_ENSURE_SUCCESS(rv, rv);
666
667 return NS_OK;
668 }
669
670 nsresult
GetFaviconDataAsync(nsIURI * aFaviconURI,mozIStorageStatementCallback * aCallback)671 nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI,
672 mozIStorageStatementCallback *aCallback)
673 {
674 NS_ASSERTION(aCallback, "Doesn't make sense to call this without a callback");
675 nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
676 "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url"
677 );
678 NS_ENSURE_STATE(stmt);
679
680 // Ignore the ref part of the URI before querying the database because
681 // we may have added a media fragment for rendering purposes.
682
683 nsAutoCString faviconURI;
684 aFaviconURI->GetSpecIgnoringRef(faviconURI);
685 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), faviconURI);
686 NS_ENSURE_SUCCESS(rv, rv);
687
688 nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
689 return stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
690 }
691
692 ////////////////////////////////////////////////////////////////////////////////
693 //// ExpireFaviconsStatementCallbackNotifier
694
ExpireFaviconsStatementCallbackNotifier()695 ExpireFaviconsStatementCallbackNotifier::ExpireFaviconsStatementCallbackNotifier()
696 {
697 }
698
699
700 NS_IMETHODIMP
HandleCompletion(uint16_t aReason)701 ExpireFaviconsStatementCallbackNotifier::HandleCompletion(uint16_t aReason)
702 {
703 // We should dispatch only if expiration has been successful.
704 if (aReason != mozIStorageStatementCallback::REASON_FINISHED)
705 return NS_OK;
706
707 nsCOMPtr<nsIObserverService> observerService =
708 mozilla::services::GetObserverService();
709 if (observerService) {
710 (void)observerService->NotifyObservers(nullptr,
711 NS_PLACES_FAVICONS_EXPIRED_TOPIC_ID,
712 nullptr);
713 }
714
715 return NS_OK;
716 }
717