|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /** |
|
7 * This is the favicon service, which stores favicons for web pages with your |
|
8 * history as you browse. It is also used to save the favicons for bookmarks. |
|
9 * |
|
10 * DANGER: The history query system makes assumptions about the favicon storage |
|
11 * so that icons can be quickly generated for history/bookmark result sets. If |
|
12 * you change the database layout at all, you will have to update both services. |
|
13 */ |
|
14 |
|
15 #include "nsFaviconService.h" |
|
16 |
|
17 #include "nsNavHistory.h" |
|
18 #include "nsPlacesMacros.h" |
|
19 #include "Helpers.h" |
|
20 #include "AsyncFaviconHelpers.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/Preferences.h" |
|
30 |
|
31 // For large favicons optimization. |
|
32 #include "imgITools.h" |
|
33 #include "imgIContainer.h" |
|
34 |
|
35 // Default value for mOptimizedIconDimension |
|
36 #define OPTIMIZED_FAVICON_DIMENSION 16 |
|
37 |
|
38 #define MAX_FAVICON_CACHE_SIZE 256 |
|
39 #define FAVICON_CACHE_REDUCE_COUNT 64 |
|
40 |
|
41 #define MAX_UNASSOCIATED_FAVICONS 64 |
|
42 |
|
43 // When replaceFaviconData is called, we store the icons in an in-memory cache |
|
44 // instead of in storage. Icons in the cache are expired according to this |
|
45 // interval. |
|
46 #define UNASSOCIATED_ICON_EXPIRY_INTERVAL 60000 |
|
47 |
|
48 // The MIME type of the default favicon and favicons created by |
|
49 // OptimizeFaviconImage. |
|
50 #define DEFAULT_MIME_TYPE "image/png" |
|
51 |
|
52 using namespace mozilla; |
|
53 using namespace mozilla::places; |
|
54 |
|
55 /** |
|
56 * Used to notify a topic to system observers on async execute completion. |
|
57 * Will throw on error. |
|
58 */ |
|
59 class ExpireFaviconsStatementCallbackNotifier : public AsyncStatementCallback |
|
60 { |
|
61 public: |
|
62 ExpireFaviconsStatementCallbackNotifier(); |
|
63 NS_IMETHOD HandleCompletion(uint16_t aReason); |
|
64 }; |
|
65 |
|
66 |
|
67 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsFaviconService, gFaviconService) |
|
68 |
|
69 NS_IMPL_CLASSINFO(nsFaviconService, nullptr, 0, NS_FAVICONSERVICE_CID) |
|
70 NS_IMPL_ISUPPORTS_CI( |
|
71 nsFaviconService |
|
72 , nsIFaviconService |
|
73 , mozIAsyncFavicons |
|
74 , nsITimerCallback |
|
75 ) |
|
76 |
|
77 nsFaviconService::nsFaviconService() |
|
78 : mOptimizedIconDimension(OPTIMIZED_FAVICON_DIMENSION) |
|
79 , mFailedFaviconSerial(0) |
|
80 , mFailedFavicons(MAX_FAVICON_CACHE_SIZE) |
|
81 , mUnassociatedIcons(MAX_UNASSOCIATED_FAVICONS) |
|
82 { |
|
83 NS_ASSERTION(!gFaviconService, |
|
84 "Attempting to create two instances of the service!"); |
|
85 gFaviconService = this; |
|
86 } |
|
87 |
|
88 |
|
89 nsFaviconService::~nsFaviconService() |
|
90 { |
|
91 NS_ASSERTION(gFaviconService == this, |
|
92 "Deleting a non-singleton instance of the service"); |
|
93 if (gFaviconService == this) |
|
94 gFaviconService = nullptr; |
|
95 } |
|
96 |
|
97 |
|
98 nsresult |
|
99 nsFaviconService::Init() |
|
100 { |
|
101 mDB = Database::GetDatabase(); |
|
102 NS_ENSURE_STATE(mDB); |
|
103 |
|
104 mOptimizedIconDimension = Preferences::GetInt( |
|
105 "places.favicons.optimizeToDimension", OPTIMIZED_FAVICON_DIMENSION |
|
106 ); |
|
107 |
|
108 mExpireUnassociatedIconsTimer = do_CreateInstance("@mozilla.org/timer;1"); |
|
109 NS_ENSURE_STATE(mExpireUnassociatedIconsTimer); |
|
110 |
|
111 return NS_OK; |
|
112 } |
|
113 |
|
114 NS_IMETHODIMP |
|
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 nsRefPtr<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 static PLDHashOperator |
|
149 ExpireNonrecentUnassociatedIconsEnumerator( |
|
150 UnassociatedIconHashKey* aIconKey, |
|
151 void* aNow) |
|
152 { |
|
153 PRTime now = *(reinterpret_cast<PRTime*>(aNow)); |
|
154 if (now - aIconKey->created >= UNASSOCIATED_ICON_EXPIRY_INTERVAL) { |
|
155 return PL_DHASH_REMOVE; |
|
156 } |
|
157 return PL_DHASH_NEXT; |
|
158 } |
|
159 |
|
160 NS_IMETHODIMP |
|
161 nsFaviconService::Notify(nsITimer* timer) |
|
162 { |
|
163 if (timer != mExpireUnassociatedIconsTimer.get()) { |
|
164 return NS_ERROR_INVALID_ARG; |
|
165 } |
|
166 |
|
167 PRTime now = PR_Now(); |
|
168 mUnassociatedIcons.EnumerateEntries( |
|
169 ExpireNonrecentUnassociatedIconsEnumerator, &now); |
|
170 // Re-init the expiry timer if the cache isn't empty. |
|
171 if (mUnassociatedIcons.Count() > 0) { |
|
172 mExpireUnassociatedIconsTimer->InitWithCallback( |
|
173 this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT); |
|
174 } |
|
175 |
|
176 return NS_OK; |
|
177 } |
|
178 |
|
179 //////////////////////////////////////////////////////////////////////////////// |
|
180 //// nsIFaviconService |
|
181 |
|
182 NS_IMETHODIMP |
|
183 nsFaviconService::GetDefaultFavicon(nsIURI** _retval) |
|
184 { |
|
185 NS_ENSURE_ARG_POINTER(_retval); |
|
186 |
|
187 // not found, use default |
|
188 if (!mDefaultIcon) { |
|
189 nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon), |
|
190 NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); |
|
191 NS_ENSURE_SUCCESS(rv, rv); |
|
192 } |
|
193 return mDefaultIcon->Clone(_retval); |
|
194 } |
|
195 |
|
196 void |
|
197 nsFaviconService::SendFaviconNotifications(nsIURI* aPageURI, |
|
198 nsIURI* aFaviconURI, |
|
199 const nsACString& aGUID) |
|
200 { |
|
201 nsAutoCString faviconSpec; |
|
202 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
203 if (history && NS_SUCCEEDED(aFaviconURI->GetSpec(faviconSpec))) { |
|
204 history->SendPageChangedNotification(aPageURI, |
|
205 nsINavHistoryObserver::ATTRIBUTE_FAVICON, |
|
206 NS_ConvertUTF8toUTF16(faviconSpec), |
|
207 aGUID); |
|
208 } |
|
209 } |
|
210 |
|
211 NS_IMETHODIMP |
|
212 nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI, |
|
213 nsIURI* aFaviconURI, |
|
214 bool aForceReload, |
|
215 uint32_t aFaviconLoadType, |
|
216 nsIFaviconDataCallback* aCallback) |
|
217 { |
|
218 NS_ENSURE_ARG(aPageURI); |
|
219 NS_ENSURE_ARG(aFaviconURI); |
|
220 |
|
221 // If a favicon is in the failed cache, only load it during a forced reload. |
|
222 bool previouslyFailed; |
|
223 nsresult rv = IsFailedFavicon(aFaviconURI, &previouslyFailed); |
|
224 NS_ENSURE_SUCCESS(rv, rv); |
|
225 if (previouslyFailed) { |
|
226 if (aForceReload) |
|
227 RemoveFailedFavicon(aFaviconURI); |
|
228 else |
|
229 return NS_OK; |
|
230 } |
|
231 |
|
232 // Check if the icon already exists and fetch it from the network, if needed. |
|
233 // Finally associate the icon to the requested page if not yet associated. |
|
234 rv = AsyncFetchAndSetIconForPage::start( |
|
235 aFaviconURI, aPageURI, aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING, |
|
236 aFaviconLoadType, aCallback |
|
237 ); |
|
238 NS_ENSURE_SUCCESS(rv, rv); |
|
239 |
|
240 // DB will be updated and observers notified when data has finished loading. |
|
241 return NS_OK; |
|
242 } |
|
243 |
|
244 NS_IMETHODIMP |
|
245 nsFaviconService::ReplaceFaviconData(nsIURI* aFaviconURI, |
|
246 const uint8_t* aData, |
|
247 uint32_t aDataLen, |
|
248 const nsACString& aMimeType, |
|
249 PRTime aExpiration) |
|
250 { |
|
251 NS_ENSURE_ARG(aFaviconURI); |
|
252 NS_ENSURE_ARG(aData); |
|
253 NS_ENSURE_TRUE(aDataLen > 0, NS_ERROR_INVALID_ARG); |
|
254 NS_ENSURE_TRUE(aMimeType.Length() > 0, NS_ERROR_INVALID_ARG); |
|
255 if (aExpiration == 0) { |
|
256 aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION; |
|
257 } |
|
258 |
|
259 UnassociatedIconHashKey* iconKey = mUnassociatedIcons.PutEntry(aFaviconURI); |
|
260 if (!iconKey) { |
|
261 return NS_ERROR_OUT_OF_MEMORY; |
|
262 } |
|
263 |
|
264 iconKey->created = PR_Now(); |
|
265 |
|
266 // If the cache contains unassociated icons, an expiry timer should already exist, otherwise |
|
267 // there may be a timer left hanging around, so make sure we fire a new one. |
|
268 int32_t unassociatedCount = mUnassociatedIcons.Count(); |
|
269 if (unassociatedCount == 1) { |
|
270 mExpireUnassociatedIconsTimer->Cancel(); |
|
271 mExpireUnassociatedIconsTimer->InitWithCallback( |
|
272 this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT); |
|
273 } |
|
274 |
|
275 IconData* iconData = &(iconKey->iconData); |
|
276 iconData->expiration = aExpiration; |
|
277 iconData->status = ICON_STATUS_CACHED; |
|
278 iconData->fetchMode = FETCH_NEVER; |
|
279 nsresult rv = aFaviconURI->GetSpec(iconData->spec); |
|
280 NS_ENSURE_SUCCESS(rv, rv); |
|
281 |
|
282 // If the page provided a large image for the favicon (eg, a highres image |
|
283 // or a multiresolution .ico file), we don't want to store more data than |
|
284 // needed. |
|
285 if (aDataLen > MAX_ICON_FILESIZE(mOptimizedIconDimension)) { |
|
286 rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, iconData->data, iconData->mimeType); |
|
287 NS_ENSURE_SUCCESS(rv, rv); |
|
288 |
|
289 if (iconData->data.Length() > MAX_FAVICON_SIZE) { |
|
290 // We cannot optimize this favicon size and we are over the maximum size |
|
291 // allowed, so we will not save data to the db to avoid bloating it. |
|
292 mUnassociatedIcons.RemoveEntry(aFaviconURI); |
|
293 return NS_ERROR_FAILURE; |
|
294 } |
|
295 } else { |
|
296 iconData->mimeType.Assign(aMimeType); |
|
297 iconData->data.Assign(TO_CHARBUFFER(aData), aDataLen); |
|
298 } |
|
299 |
|
300 // If the database contains an icon at the given url, we will update the |
|
301 // database immediately so that the associated pages are kept in sync. |
|
302 // Otherwise, do nothing and let the icon be picked up from the memory hash. |
|
303 rv = AsyncReplaceFaviconData::start(iconData); |
|
304 NS_ENSURE_SUCCESS(rv, rv); |
|
305 |
|
306 return NS_OK; |
|
307 } |
|
308 |
|
309 NS_IMETHODIMP |
|
310 nsFaviconService::ReplaceFaviconDataFromDataURL(nsIURI* aFaviconURI, |
|
311 const nsAString& aDataURL, |
|
312 PRTime aExpiration) |
|
313 { |
|
314 NS_ENSURE_ARG(aFaviconURI); |
|
315 NS_ENSURE_TRUE(aDataURL.Length() > 0, NS_ERROR_INVALID_ARG); |
|
316 if (aExpiration == 0) { |
|
317 aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION; |
|
318 } |
|
319 |
|
320 nsCOMPtr<nsIURI> dataURI; |
|
321 nsresult rv = NS_NewURI(getter_AddRefs(dataURI), aDataURL); |
|
322 NS_ENSURE_SUCCESS(rv, rv); |
|
323 |
|
324 // Use the data: protocol handler to convert the data. |
|
325 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); |
|
326 NS_ENSURE_SUCCESS(rv, rv); |
|
327 nsCOMPtr<nsIProtocolHandler> protocolHandler; |
|
328 rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler)); |
|
329 NS_ENSURE_SUCCESS(rv, rv); |
|
330 |
|
331 nsCOMPtr<nsIChannel> channel; |
|
332 rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel)); |
|
333 NS_ENSURE_SUCCESS(rv, rv); |
|
334 |
|
335 // Blocking stream is OK for data URIs. |
|
336 nsCOMPtr<nsIInputStream> stream; |
|
337 rv = channel->Open(getter_AddRefs(stream)); |
|
338 NS_ENSURE_SUCCESS(rv, rv); |
|
339 |
|
340 uint64_t available64; |
|
341 rv = stream->Available(&available64); |
|
342 NS_ENSURE_SUCCESS(rv, rv); |
|
343 if (available64 == 0 || available64 > UINT32_MAX / sizeof(uint8_t)) |
|
344 return NS_ERROR_FILE_TOO_BIG; |
|
345 uint32_t available = (uint32_t)available64; |
|
346 |
|
347 // Read all the decoded data. |
|
348 uint8_t* buffer = static_cast<uint8_t*> |
|
349 (nsMemory::Alloc(sizeof(uint8_t) * available)); |
|
350 if (!buffer) |
|
351 return NS_ERROR_OUT_OF_MEMORY; |
|
352 uint32_t numRead; |
|
353 rv = stream->Read(TO_CHARBUFFER(buffer), available, &numRead); |
|
354 if (NS_FAILED(rv) || numRead != available) { |
|
355 nsMemory::Free(buffer); |
|
356 return rv; |
|
357 } |
|
358 |
|
359 nsAutoCString mimeType; |
|
360 rv = channel->GetContentType(mimeType); |
|
361 if (NS_FAILED(rv)) { |
|
362 nsMemory::Free(buffer); |
|
363 return rv; |
|
364 } |
|
365 |
|
366 // ReplaceFaviconData can now do the dirty work. |
|
367 rv = ReplaceFaviconData(aFaviconURI, buffer, available, mimeType, aExpiration); |
|
368 nsMemory::Free(buffer); |
|
369 NS_ENSURE_SUCCESS(rv, rv); |
|
370 |
|
371 return NS_OK; |
|
372 } |
|
373 |
|
374 NS_IMETHODIMP |
|
375 nsFaviconService::GetFaviconURLForPage(nsIURI *aPageURI, |
|
376 nsIFaviconDataCallback* aCallback) |
|
377 { |
|
378 NS_ENSURE_ARG(aPageURI); |
|
379 NS_ENSURE_ARG(aCallback); |
|
380 |
|
381 nsresult rv = AsyncGetFaviconURLForPage::start(aPageURI, aCallback); |
|
382 NS_ENSURE_SUCCESS(rv, rv); |
|
383 return NS_OK; |
|
384 } |
|
385 |
|
386 NS_IMETHODIMP |
|
387 nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI, |
|
388 nsIFaviconDataCallback* aCallback) |
|
389 { |
|
390 NS_ENSURE_ARG(aPageURI); |
|
391 NS_ENSURE_ARG(aCallback); |
|
392 |
|
393 nsresult rv = AsyncGetFaviconDataForPage::start(aPageURI, aCallback); |
|
394 NS_ENSURE_SUCCESS(rv, rv); |
|
395 return NS_OK; |
|
396 } |
|
397 |
|
398 nsresult |
|
399 nsFaviconService::GetFaviconLinkForIcon(nsIURI* aFaviconURI, |
|
400 nsIURI** aOutputURI) |
|
401 { |
|
402 NS_ENSURE_ARG(aFaviconURI); |
|
403 NS_ENSURE_ARG_POINTER(aOutputURI); |
|
404 |
|
405 nsAutoCString spec; |
|
406 if (aFaviconURI) { |
|
407 nsresult rv = aFaviconURI->GetSpec(spec); |
|
408 NS_ENSURE_SUCCESS(rv, rv); |
|
409 } |
|
410 return GetFaviconLinkForIconString(spec, aOutputURI); |
|
411 } |
|
412 |
|
413 |
|
414 static PLDHashOperator |
|
415 ExpireFailedFaviconsCallback(nsCStringHashKey::KeyType aKey, |
|
416 uint32_t& aData, |
|
417 void* userArg) |
|
418 { |
|
419 uint32_t* threshold = reinterpret_cast<uint32_t*>(userArg); |
|
420 if (aData < *threshold) |
|
421 return PL_DHASH_REMOVE; |
|
422 return PL_DHASH_NEXT; |
|
423 } |
|
424 |
|
425 |
|
426 NS_IMETHODIMP |
|
427 nsFaviconService::AddFailedFavicon(nsIURI* aFaviconURI) |
|
428 { |
|
429 NS_ENSURE_ARG(aFaviconURI); |
|
430 |
|
431 nsAutoCString spec; |
|
432 nsresult rv = aFaviconURI->GetSpec(spec); |
|
433 NS_ENSURE_SUCCESS(rv, rv); |
|
434 |
|
435 mFailedFavicons.Put(spec, mFailedFaviconSerial); |
|
436 mFailedFaviconSerial ++; |
|
437 |
|
438 if (mFailedFavicons.Count() > MAX_FAVICON_CACHE_SIZE) { |
|
439 // need to expire some entries, delete the FAVICON_CACHE_REDUCE_COUNT number |
|
440 // of items that are the oldest |
|
441 uint32_t threshold = mFailedFaviconSerial - |
|
442 MAX_FAVICON_CACHE_SIZE + FAVICON_CACHE_REDUCE_COUNT; |
|
443 mFailedFavicons.Enumerate(ExpireFailedFaviconsCallback, &threshold); |
|
444 } |
|
445 return NS_OK; |
|
446 } |
|
447 |
|
448 |
|
449 NS_IMETHODIMP |
|
450 nsFaviconService::RemoveFailedFavicon(nsIURI* aFaviconURI) |
|
451 { |
|
452 NS_ENSURE_ARG(aFaviconURI); |
|
453 |
|
454 nsAutoCString spec; |
|
455 nsresult rv = aFaviconURI->GetSpec(spec); |
|
456 NS_ENSURE_SUCCESS(rv, rv); |
|
457 |
|
458 // we silently do nothing and succeed if the icon is not in the cache |
|
459 mFailedFavicons.Remove(spec); |
|
460 return NS_OK; |
|
461 } |
|
462 |
|
463 |
|
464 NS_IMETHODIMP |
|
465 nsFaviconService::IsFailedFavicon(nsIURI* aFaviconURI, bool* _retval) |
|
466 { |
|
467 NS_ENSURE_ARG(aFaviconURI); |
|
468 nsAutoCString spec; |
|
469 nsresult rv = aFaviconURI->GetSpec(spec); |
|
470 NS_ENSURE_SUCCESS(rv, rv); |
|
471 |
|
472 uint32_t serial; |
|
473 *_retval = mFailedFavicons.Get(spec, &serial); |
|
474 return NS_OK; |
|
475 } |
|
476 |
|
477 |
|
478 // nsFaviconService::GetFaviconLinkForIconString |
|
479 // |
|
480 // This computes a favicon URL with string input and using the cached |
|
481 // default one to minimize parsing. |
|
482 |
|
483 nsresult |
|
484 nsFaviconService::GetFaviconLinkForIconString(const nsCString& aSpec, |
|
485 nsIURI** aOutput) |
|
486 { |
|
487 if (aSpec.IsEmpty()) { |
|
488 // default icon for empty strings |
|
489 if (! mDefaultIcon) { |
|
490 nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon), |
|
491 NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); |
|
492 NS_ENSURE_SUCCESS(rv, rv); |
|
493 } |
|
494 return mDefaultIcon->Clone(aOutput); |
|
495 } |
|
496 |
|
497 if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) { |
|
498 // pass through for chrome URLs, since they can be referenced without |
|
499 // this service |
|
500 return NS_NewURI(aOutput, aSpec); |
|
501 } |
|
502 |
|
503 nsAutoCString annoUri; |
|
504 annoUri.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":"); |
|
505 annoUri += aSpec; |
|
506 return NS_NewURI(aOutput, annoUri); |
|
507 } |
|
508 |
|
509 |
|
510 // nsFaviconService::GetFaviconSpecForIconString |
|
511 // |
|
512 // This computes a favicon spec for when you don't want a URI object (as in |
|
513 // the tree view implementation), sparing all parsing and normalization. |
|
514 void |
|
515 nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec, |
|
516 nsACString& aOutput) |
|
517 { |
|
518 if (aSpec.IsEmpty()) { |
|
519 aOutput.AssignLiteral(FAVICON_DEFAULT_URL); |
|
520 } else if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) { |
|
521 aOutput = aSpec; |
|
522 } else { |
|
523 aOutput.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":"); |
|
524 aOutput += aSpec; |
|
525 } |
|
526 } |
|
527 |
|
528 |
|
529 // nsFaviconService::OptimizeFaviconImage |
|
530 // |
|
531 // Given a blob of data (a image file already read into a buffer), optimize |
|
532 // its size by recompressing it as a 16x16 PNG. |
|
533 nsresult |
|
534 nsFaviconService::OptimizeFaviconImage(const uint8_t* aData, uint32_t aDataLen, |
|
535 const nsACString& aMimeType, |
|
536 nsACString& aNewData, |
|
537 nsACString& aNewMimeType) |
|
538 { |
|
539 nsresult rv; |
|
540 |
|
541 nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1"); |
|
542 |
|
543 nsCOMPtr<nsIInputStream> stream; |
|
544 rv = NS_NewByteInputStream(getter_AddRefs(stream), |
|
545 reinterpret_cast<const char*>(aData), aDataLen, |
|
546 NS_ASSIGNMENT_DEPEND); |
|
547 NS_ENSURE_SUCCESS(rv, rv); |
|
548 |
|
549 // decode image |
|
550 nsCOMPtr<imgIContainer> container; |
|
551 rv = imgtool->DecodeImageData(stream, aMimeType, getter_AddRefs(container)); |
|
552 NS_ENSURE_SUCCESS(rv, rv); |
|
553 |
|
554 aNewMimeType.AssignLiteral(DEFAULT_MIME_TYPE); |
|
555 |
|
556 // scale and recompress |
|
557 nsCOMPtr<nsIInputStream> iconStream; |
|
558 rv = imgtool->EncodeScaledImage(container, aNewMimeType, |
|
559 mOptimizedIconDimension, |
|
560 mOptimizedIconDimension, |
|
561 EmptyString(), |
|
562 getter_AddRefs(iconStream)); |
|
563 NS_ENSURE_SUCCESS(rv, rv); |
|
564 |
|
565 // Read the stream into a new buffer. |
|
566 rv = NS_ConsumeStream(iconStream, UINT32_MAX, aNewData); |
|
567 NS_ENSURE_SUCCESS(rv, rv); |
|
568 |
|
569 return NS_OK; |
|
570 } |
|
571 |
|
572 nsresult |
|
573 nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI, |
|
574 mozIStorageStatementCallback *aCallback) |
|
575 { |
|
576 NS_ASSERTION(aCallback, "Doesn't make sense to call this without a callback"); |
|
577 nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement( |
|
578 "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url" |
|
579 ); |
|
580 NS_ENSURE_STATE(stmt); |
|
581 |
|
582 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI); |
|
583 NS_ENSURE_SUCCESS(rv, rv); |
|
584 |
|
585 nsCOMPtr<mozIStoragePendingStatement> pendingStatement; |
|
586 return stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement)); |
|
587 } |
|
588 |
|
589 //////////////////////////////////////////////////////////////////////////////// |
|
590 //// ExpireFaviconsStatementCallbackNotifier |
|
591 |
|
592 ExpireFaviconsStatementCallbackNotifier::ExpireFaviconsStatementCallbackNotifier() |
|
593 { |
|
594 } |
|
595 |
|
596 |
|
597 NS_IMETHODIMP |
|
598 ExpireFaviconsStatementCallbackNotifier::HandleCompletion(uint16_t aReason) |
|
599 { |
|
600 // We should dispatch only if expiration has been successful. |
|
601 if (aReason != mozIStorageStatementCallback::REASON_FINISHED) |
|
602 return NS_OK; |
|
603 |
|
604 nsCOMPtr<nsIObserverService> observerService = |
|
605 mozilla::services::GetObserverService(); |
|
606 if (observerService) { |
|
607 (void)observerService->NotifyObservers(nullptr, |
|
608 NS_PLACES_FAVICONS_EXPIRED_TOPIC_ID, |
|
609 nullptr); |
|
610 } |
|
611 |
|
612 return NS_OK; |
|
613 } |