|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : |
|
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 #include "AsyncFaviconHelpers.h" |
|
8 |
|
9 #include "nsICacheService.h" |
|
10 #include "nsICacheEntry.h" |
|
11 #include "nsICachingChannel.h" |
|
12 #include "nsIAsyncVerifyRedirectCallback.h" |
|
13 |
|
14 #include "nsNavHistory.h" |
|
15 #include "nsFaviconService.h" |
|
16 #include "mozilla/storage.h" |
|
17 #include "nsNetUtil.h" |
|
18 #include "nsPrintfCString.h" |
|
19 #include "nsStreamUtils.h" |
|
20 #include "nsIPrivateBrowsingChannel.h" |
|
21 #include "nsISupportsPriority.h" |
|
22 #include <algorithm> |
|
23 |
|
24 using namespace mozilla::places; |
|
25 using namespace mozilla::storage; |
|
26 |
|
27 namespace mozilla { |
|
28 namespace places { |
|
29 |
|
30 namespace { |
|
31 |
|
32 /** |
|
33 * Fetches information on a page from the Places database. |
|
34 * |
|
35 * @param aDBConn |
|
36 * Database connection to history tables. |
|
37 * @param _page |
|
38 * Page that should be fetched. |
|
39 */ |
|
40 nsresult |
|
41 FetchPageInfo(nsRefPtr<Database>& aDB, |
|
42 PageData& _page) |
|
43 { |
|
44 NS_PRECONDITION(_page.spec.Length(), "Must have a non-empty spec!"); |
|
45 NS_PRECONDITION(!NS_IsMainThread(), |
|
46 "This should not be called on the main thread"); |
|
47 |
|
48 // This query finds the bookmarked uri we want to set the icon for, |
|
49 // walking up to two redirect levels. |
|
50 nsCString query = nsPrintfCString( |
|
51 "SELECT h.id, h.favicon_id, h.guid, ( " |
|
52 "SELECT h.url FROM moz_bookmarks b WHERE b.fk = h.id " |
|
53 "UNION ALL " // Union not directly bookmarked pages. |
|
54 "SELECT url FROM moz_places WHERE id = ( " |
|
55 "SELECT COALESCE(grandparent.place_id, parent.place_id) as r_place_id " |
|
56 "FROM moz_historyvisits dest " |
|
57 "LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit " |
|
58 "AND dest.visit_type IN (%d, %d) " |
|
59 "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id " |
|
60 "AND parent.visit_type IN (%d, %d) " |
|
61 "WHERE dest.place_id = h.id " |
|
62 "AND EXISTS(SELECT 1 FROM moz_bookmarks b WHERE b.fk = r_place_id) " |
|
63 "LIMIT 1 " |
|
64 ") " |
|
65 ") FROM moz_places h WHERE h.url = :page_url", |
|
66 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT, |
|
67 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY, |
|
68 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT, |
|
69 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY |
|
70 ); |
|
71 |
|
72 nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(query); |
|
73 NS_ENSURE_STATE(stmt); |
|
74 mozStorageStatementScoper scoper(stmt); |
|
75 |
|
76 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), |
|
77 _page.spec); |
|
78 NS_ENSURE_SUCCESS(rv, rv); |
|
79 |
|
80 bool hasResult; |
|
81 rv = stmt->ExecuteStep(&hasResult); |
|
82 NS_ENSURE_SUCCESS(rv, rv); |
|
83 if (!hasResult) { |
|
84 // The page does not exist. |
|
85 return NS_ERROR_NOT_AVAILABLE; |
|
86 } |
|
87 |
|
88 rv = stmt->GetInt64(0, &_page.id); |
|
89 NS_ENSURE_SUCCESS(rv, rv); |
|
90 bool isNull; |
|
91 rv = stmt->GetIsNull(1, &isNull); |
|
92 NS_ENSURE_SUCCESS(rv, rv); |
|
93 // favicon_id can be nullptr. |
|
94 if (!isNull) { |
|
95 rv = stmt->GetInt64(1, &_page.iconId); |
|
96 NS_ENSURE_SUCCESS(rv, rv); |
|
97 } |
|
98 rv = stmt->GetUTF8String(2, _page.guid); |
|
99 NS_ENSURE_SUCCESS(rv, rv); |
|
100 rv = stmt->GetIsNull(3, &isNull); |
|
101 NS_ENSURE_SUCCESS(rv, rv); |
|
102 // The page could not be bookmarked. |
|
103 if (!isNull) { |
|
104 rv = stmt->GetUTF8String(3, _page.bookmarkedSpec); |
|
105 NS_ENSURE_SUCCESS(rv, rv); |
|
106 } |
|
107 |
|
108 if (!_page.canAddToHistory) { |
|
109 // Either history is disabled or the scheme is not supported. In such a |
|
110 // case we want to update the icon only if the page is bookmarked. |
|
111 |
|
112 if (_page.bookmarkedSpec.IsEmpty()) { |
|
113 // The page is not bookmarked. Since updating the icon with a disabled |
|
114 // history would be a privacy leak, bail out as if the page did not exist. |
|
115 return NS_ERROR_NOT_AVAILABLE; |
|
116 } |
|
117 else { |
|
118 // The page, or a redirect to it, is bookmarked. If the bookmarked spec |
|
119 // is different from the requested one, use it. |
|
120 if (!_page.bookmarkedSpec.Equals(_page.spec)) { |
|
121 _page.spec = _page.bookmarkedSpec; |
|
122 rv = FetchPageInfo(aDB, _page); |
|
123 NS_ENSURE_SUCCESS(rv, rv); |
|
124 } |
|
125 } |
|
126 } |
|
127 |
|
128 return NS_OK; |
|
129 } |
|
130 |
|
131 /** |
|
132 * Stores information on a icon in the database. |
|
133 * |
|
134 * @param aDBConn |
|
135 * Database connection to history tables. |
|
136 * @param aIcon |
|
137 * Icon that should be stored. |
|
138 */ |
|
139 nsresult |
|
140 SetIconInfo(nsRefPtr<Database>& aDB, |
|
141 IconData& aIcon) |
|
142 { |
|
143 NS_PRECONDITION(!NS_IsMainThread(), |
|
144 "This should not be called on the main thread"); |
|
145 |
|
146 // The 'multi-coalesce' here ensures that replacing a favicon without |
|
147 // specifying a :guid parameter doesn't cause it to be allocated a new |
|
148 // GUID. |
|
149 nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement( |
|
150 "INSERT OR REPLACE INTO moz_favicons " |
|
151 "(id, url, data, mime_type, expiration, guid) " |
|
152 "VALUES ((SELECT id FROM moz_favicons WHERE url = :icon_url), " |
|
153 ":icon_url, :data, :mime_type, :expiration, " |
|
154 "COALESCE(:guid, " |
|
155 "(SELECT guid FROM moz_favicons " |
|
156 "WHERE url = :icon_url), " |
|
157 "GENERATE_GUID()))" |
|
158 ); |
|
159 NS_ENSURE_STATE(stmt); |
|
160 mozStorageStatementScoper scoper(stmt); |
|
161 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aIcon.spec); |
|
162 NS_ENSURE_SUCCESS(rv, rv); |
|
163 rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"), |
|
164 TO_INTBUFFER(aIcon.data), aIcon.data.Length()); |
|
165 NS_ENSURE_SUCCESS(rv, rv); |
|
166 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), aIcon.mimeType); |
|
167 NS_ENSURE_SUCCESS(rv, rv); |
|
168 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aIcon.expiration); |
|
169 NS_ENSURE_SUCCESS(rv, rv); |
|
170 |
|
171 // Binding a GUID allows us to override the current (or generated) GUID. |
|
172 if (aIcon.guid.IsEmpty()) { |
|
173 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("guid")); |
|
174 } |
|
175 else { |
|
176 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aIcon.guid); |
|
177 } |
|
178 NS_ENSURE_SUCCESS(rv, rv); |
|
179 |
|
180 rv = stmt->Execute(); |
|
181 NS_ENSURE_SUCCESS(rv, rv); |
|
182 |
|
183 return NS_OK; |
|
184 } |
|
185 |
|
186 /** |
|
187 * Fetches information on a icon from the Places database. |
|
188 * |
|
189 * @param aDBConn |
|
190 * Database connection to history tables. |
|
191 * @param _icon |
|
192 * Icon that should be fetched. |
|
193 */ |
|
194 nsresult |
|
195 FetchIconInfo(nsRefPtr<Database>& aDB, |
|
196 IconData& _icon) |
|
197 { |
|
198 NS_PRECONDITION(_icon.spec.Length(), "Must have a non-empty spec!"); |
|
199 NS_PRECONDITION(!NS_IsMainThread(), |
|
200 "This should not be called on the main thread"); |
|
201 |
|
202 if (_icon.status & ICON_STATUS_CACHED) { |
|
203 return NS_OK; |
|
204 } |
|
205 |
|
206 nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement( |
|
207 "SELECT id, expiration, data, mime_type " |
|
208 "FROM moz_favicons WHERE url = :icon_url" |
|
209 ); |
|
210 NS_ENSURE_STATE(stmt); |
|
211 mozStorageStatementScoper scoper(stmt); |
|
212 |
|
213 DebugOnly<nsresult> rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), |
|
214 _icon.spec); |
|
215 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
216 |
|
217 bool hasResult; |
|
218 rv = stmt->ExecuteStep(&hasResult); |
|
219 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
220 if (!hasResult) { |
|
221 // The icon does not exist yet, bail out. |
|
222 return NS_OK; |
|
223 } |
|
224 |
|
225 rv = stmt->GetInt64(0, &_icon.id); |
|
226 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
227 |
|
228 // Expiration can be nullptr. |
|
229 bool isNull; |
|
230 rv = stmt->GetIsNull(1, &isNull); |
|
231 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
232 if (!isNull) { |
|
233 rv = stmt->GetInt64(1, reinterpret_cast<int64_t*>(&_icon.expiration)); |
|
234 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
235 } |
|
236 |
|
237 // Data can be nullptr. |
|
238 rv = stmt->GetIsNull(2, &isNull); |
|
239 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
240 if (!isNull) { |
|
241 uint8_t* data; |
|
242 uint32_t dataLen = 0; |
|
243 rv = stmt->GetBlob(2, &dataLen, &data); |
|
244 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
245 _icon.data.Adopt(TO_CHARBUFFER(data), dataLen); |
|
246 // Read mime only if we have data. |
|
247 rv = stmt->GetUTF8String(3, _icon.mimeType); |
|
248 MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
249 } |
|
250 |
|
251 return NS_OK; |
|
252 } |
|
253 |
|
254 nsresult |
|
255 FetchIconURL(nsRefPtr<Database>& aDB, |
|
256 const nsACString& aPageSpec, |
|
257 nsACString& aIconSpec) |
|
258 { |
|
259 NS_PRECONDITION(!aPageSpec.IsEmpty(), "Page spec must not be empty."); |
|
260 NS_PRECONDITION(!NS_IsMainThread(), |
|
261 "This should not be called on the main thread."); |
|
262 |
|
263 aIconSpec.Truncate(); |
|
264 |
|
265 nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement( |
|
266 "SELECT f.url " |
|
267 "FROM moz_places h " |
|
268 "JOIN moz_favicons f ON h.favicon_id = f.id " |
|
269 "WHERE h.url = :page_url" |
|
270 ); |
|
271 NS_ENSURE_STATE(stmt); |
|
272 mozStorageStatementScoper scoper(stmt); |
|
273 |
|
274 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), |
|
275 aPageSpec); |
|
276 NS_ENSURE_SUCCESS(rv, rv); |
|
277 |
|
278 bool hasResult; |
|
279 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { |
|
280 rv = stmt->GetUTF8String(0, aIconSpec); |
|
281 NS_ENSURE_SUCCESS(rv, rv); |
|
282 } |
|
283 |
|
284 return NS_OK; |
|
285 } |
|
286 |
|
287 /** |
|
288 * Tries to compute the expiration time for a icon from the channel. |
|
289 * |
|
290 * @param aChannel |
|
291 * The network channel used to fetch the icon. |
|
292 * @return a valid expiration value for the fetched icon. |
|
293 */ |
|
294 PRTime |
|
295 GetExpirationTimeFromChannel(nsIChannel* aChannel) |
|
296 { |
|
297 NS_PRECONDITION(NS_IsMainThread(), |
|
298 "This should be called on the main thread"); |
|
299 |
|
300 // Attempt to get an expiration time from the cache. If this fails, we'll |
|
301 // make one up. |
|
302 PRTime expiration = -1; |
|
303 nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aChannel); |
|
304 if (cachingChannel) { |
|
305 nsCOMPtr<nsISupports> cacheToken; |
|
306 nsresult rv = cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); |
|
307 if (NS_SUCCEEDED(rv)) { |
|
308 nsCOMPtr<nsICacheEntry> cacheEntry = do_QueryInterface(cacheToken); |
|
309 uint32_t seconds; |
|
310 rv = cacheEntry->GetExpirationTime(&seconds); |
|
311 if (NS_SUCCEEDED(rv)) { |
|
312 // Set the expiration, but make sure we honor our cap. |
|
313 expiration = PR_Now() + std::min((PRTime)seconds * PR_USEC_PER_SEC, |
|
314 MAX_FAVICON_EXPIRATION); |
|
315 } |
|
316 } |
|
317 } |
|
318 // If we did not obtain a time from the cache, use the cap value. |
|
319 return expiration < 0 ? PR_Now() + MAX_FAVICON_EXPIRATION |
|
320 : expiration; |
|
321 } |
|
322 |
|
323 /** |
|
324 * Checks the icon and evaluates if it needs to be optimized. In such a case it |
|
325 * will try to reduce its size through OptimizeFaviconImage method of the |
|
326 * favicons service. |
|
327 * |
|
328 * @param aIcon |
|
329 * The icon to be evaluated. |
|
330 * @param aFaviconSvc |
|
331 * Pointer to the favicons service. |
|
332 */ |
|
333 nsresult |
|
334 OptimizeIconSize(IconData& aIcon, |
|
335 nsFaviconService* aFaviconSvc) |
|
336 { |
|
337 NS_PRECONDITION(NS_IsMainThread(), |
|
338 "This should be called on the main thread"); |
|
339 |
|
340 // Even if the page provides a large image for the favicon (eg, a highres |
|
341 // image or a multiresolution .ico file), don't try to store more data than |
|
342 // needed. |
|
343 nsAutoCString newData, newMimeType; |
|
344 if (aIcon.data.Length() > MAX_ICON_FILESIZE(aFaviconSvc->GetOptimizedIconDimension())) { |
|
345 nsresult rv = aFaviconSvc->OptimizeFaviconImage(TO_INTBUFFER(aIcon.data), |
|
346 aIcon.data.Length(), |
|
347 aIcon.mimeType, |
|
348 newData, |
|
349 newMimeType); |
|
350 if (NS_SUCCEEDED(rv) && newData.Length() < aIcon.data.Length()) { |
|
351 aIcon.data = newData; |
|
352 aIcon.mimeType = newMimeType; |
|
353 } |
|
354 } |
|
355 return NS_OK; |
|
356 } |
|
357 |
|
358 } // Anonymous namespace. |
|
359 |
|
360 |
|
361 //////////////////////////////////////////////////////////////////////////////// |
|
362 //// AsyncFaviconHelperBase |
|
363 |
|
364 AsyncFaviconHelperBase::AsyncFaviconHelperBase( |
|
365 nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
366 ) : mDB(Database::GetDatabase()) |
|
367 { |
|
368 // Don't AddRef or Release in runnables for thread-safety. |
|
369 mCallback.swap(aCallback); |
|
370 } |
|
371 |
|
372 AsyncFaviconHelperBase::~AsyncFaviconHelperBase() |
|
373 { |
|
374 nsCOMPtr<nsIThread> thread; |
|
375 (void)NS_GetMainThread(getter_AddRefs(thread)); |
|
376 if (mCallback) { |
|
377 (void)NS_ProxyRelease(thread, mCallback, true); |
|
378 } |
|
379 } |
|
380 |
|
381 //////////////////////////////////////////////////////////////////////////////// |
|
382 //// AsyncFetchAndSetIconForPage |
|
383 |
|
384 // static |
|
385 nsresult |
|
386 AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI, |
|
387 nsIURI* aPageURI, |
|
388 enum AsyncFaviconFetchMode aFetchMode, |
|
389 uint32_t aFaviconLoadType, |
|
390 nsIFaviconDataCallback* aCallback) |
|
391 { |
|
392 NS_PRECONDITION(NS_IsMainThread(), |
|
393 "This should be called on the main thread"); |
|
394 |
|
395 PageData page; |
|
396 nsresult rv = aPageURI->GetSpec(page.spec); |
|
397 NS_ENSURE_SUCCESS(rv, rv); |
|
398 // URIs can arguably miss a host. |
|
399 (void)GetReversedHostname(aPageURI, page.revHost); |
|
400 bool canAddToHistory; |
|
401 nsNavHistory* navHistory = nsNavHistory::GetHistoryService(); |
|
402 NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY); |
|
403 rv = navHistory->CanAddURI(aPageURI, &canAddToHistory); |
|
404 NS_ENSURE_SUCCESS(rv, rv); |
|
405 page.canAddToHistory = !!canAddToHistory && aFaviconLoadType != nsIFaviconService::FAVICON_LOAD_PRIVATE; |
|
406 |
|
407 IconData icon; |
|
408 |
|
409 nsFaviconService* favicons = nsFaviconService::GetFaviconService(); |
|
410 NS_ENSURE_STATE(favicons); |
|
411 |
|
412 UnassociatedIconHashKey* iconKey = |
|
413 favicons->mUnassociatedIcons.GetEntry(aFaviconURI); |
|
414 |
|
415 if (iconKey) { |
|
416 icon = iconKey->iconData; |
|
417 favicons->mUnassociatedIcons.RemoveEntry(aFaviconURI); |
|
418 } else { |
|
419 icon.fetchMode = aFetchMode; |
|
420 rv = aFaviconURI->GetSpec(icon.spec); |
|
421 NS_ENSURE_SUCCESS(rv, rv); |
|
422 } |
|
423 |
|
424 // If the page url points to an image, the icon's url will be the same. |
|
425 // In future evaluate to store a resample of the image. For now avoid that |
|
426 // for database size concerns. |
|
427 // Don't store favicons for error pages too. |
|
428 if (icon.spec.Equals(page.spec) || |
|
429 icon.spec.Equals(FAVICON_ERRORPAGE_URL)) { |
|
430 return NS_OK; |
|
431 } |
|
432 |
|
433 // The event will swap owning pointers, thus we need a new pointer. |
|
434 nsCOMPtr<nsIFaviconDataCallback> callback(aCallback); |
|
435 nsRefPtr<AsyncFetchAndSetIconForPage> event = |
|
436 new AsyncFetchAndSetIconForPage(icon, page, aFaviconLoadType, callback); |
|
437 |
|
438 // Get the target thread and start the work. |
|
439 nsRefPtr<Database> DB = Database::GetDatabase(); |
|
440 NS_ENSURE_STATE(DB); |
|
441 DB->DispatchToAsyncThread(event); |
|
442 |
|
443 return NS_OK; |
|
444 } |
|
445 |
|
446 AsyncFetchAndSetIconForPage::AsyncFetchAndSetIconForPage( |
|
447 IconData& aIcon |
|
448 , PageData& aPage |
|
449 , uint32_t aFaviconLoadType |
|
450 , nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
451 ) : AsyncFaviconHelperBase(aCallback) |
|
452 , mIcon(aIcon) |
|
453 , mPage(aPage) |
|
454 , mFaviconLoadPrivate(aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE) |
|
455 { |
|
456 } |
|
457 |
|
458 AsyncFetchAndSetIconForPage::~AsyncFetchAndSetIconForPage() |
|
459 { |
|
460 } |
|
461 |
|
462 NS_IMETHODIMP |
|
463 AsyncFetchAndSetIconForPage::Run() |
|
464 { |
|
465 NS_PRECONDITION(!NS_IsMainThread(), |
|
466 "This should not be called on the main thread"); |
|
467 |
|
468 // Try to fetch the icon from the database. |
|
469 nsresult rv = FetchIconInfo(mDB, mIcon); |
|
470 NS_ENSURE_SUCCESS(rv, rv); |
|
471 |
|
472 bool isInvalidIcon = mIcon.data.IsEmpty() || |
|
473 (mIcon.expiration && PR_Now() > mIcon.expiration); |
|
474 bool fetchIconFromNetwork = mIcon.fetchMode == FETCH_ALWAYS || |
|
475 (mIcon.fetchMode == FETCH_IF_MISSING && isInvalidIcon); |
|
476 |
|
477 if (!fetchIconFromNetwork) { |
|
478 // There is already a valid icon or we don't want to fetch a new one, |
|
479 // directly proceed with association. |
|
480 nsRefPtr<AsyncAssociateIconToPage> event = |
|
481 new AsyncAssociateIconToPage(mIcon, mPage, mCallback); |
|
482 mDB->DispatchToAsyncThread(event); |
|
483 |
|
484 return NS_OK; |
|
485 } |
|
486 else { |
|
487 // Fetch the icon from network. When done this will associate the |
|
488 // icon to the page and notify. |
|
489 nsRefPtr<AsyncFetchAndSetIconFromNetwork> event = |
|
490 new AsyncFetchAndSetIconFromNetwork(mIcon, mPage, mFaviconLoadPrivate, mCallback); |
|
491 |
|
492 // Start the work on the main thread. |
|
493 rv = NS_DispatchToMainThread(event); |
|
494 NS_ENSURE_SUCCESS(rv, rv); |
|
495 } |
|
496 |
|
497 return NS_OK; |
|
498 } |
|
499 |
|
500 //////////////////////////////////////////////////////////////////////////////// |
|
501 //// AsyncFetchAndSetIconFromNetwork |
|
502 |
|
503 NS_IMPL_ISUPPORTS_INHERITED( |
|
504 AsyncFetchAndSetIconFromNetwork |
|
505 , nsRunnable |
|
506 , nsIStreamListener |
|
507 , nsIInterfaceRequestor |
|
508 , nsIChannelEventSink |
|
509 ) |
|
510 |
|
511 AsyncFetchAndSetIconFromNetwork::AsyncFetchAndSetIconFromNetwork( |
|
512 IconData& aIcon |
|
513 , PageData& aPage |
|
514 , bool aFaviconLoadPrivate |
|
515 , nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
516 ) |
|
517 : AsyncFaviconHelperBase(aCallback) |
|
518 , mIcon(aIcon) |
|
519 , mPage(aPage) |
|
520 , mFaviconLoadPrivate(aFaviconLoadPrivate) |
|
521 { |
|
522 } |
|
523 |
|
524 AsyncFetchAndSetIconFromNetwork::~AsyncFetchAndSetIconFromNetwork() |
|
525 { |
|
526 } |
|
527 |
|
528 NS_IMETHODIMP |
|
529 AsyncFetchAndSetIconFromNetwork::Run() |
|
530 { |
|
531 NS_PRECONDITION(NS_IsMainThread(), |
|
532 "This should be called on the main thread"); |
|
533 |
|
534 // Ensure data is cleared, since it's going to be overwritten. |
|
535 if (mIcon.data.Length() > 0) { |
|
536 mIcon.data.Truncate(0); |
|
537 mIcon.mimeType.Truncate(0); |
|
538 } |
|
539 |
|
540 nsCOMPtr<nsIURI> iconURI; |
|
541 nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec); |
|
542 NS_ENSURE_SUCCESS(rv, rv); |
|
543 nsCOMPtr<nsIChannel> channel; |
|
544 rv = NS_NewChannel(getter_AddRefs(channel), iconURI); |
|
545 NS_ENSURE_SUCCESS(rv, rv); |
|
546 nsCOMPtr<nsIInterfaceRequestor> listenerRequestor = |
|
547 do_QueryInterface(reinterpret_cast<nsISupports*>(this)); |
|
548 NS_ENSURE_STATE(listenerRequestor); |
|
549 rv = channel->SetNotificationCallbacks(listenerRequestor); |
|
550 NS_ENSURE_SUCCESS(rv, rv); |
|
551 nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel); |
|
552 if (pbChannel) { |
|
553 rv = pbChannel->SetPrivate(mFaviconLoadPrivate); |
|
554 NS_ENSURE_SUCCESS(rv, rv); |
|
555 } |
|
556 |
|
557 nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(channel); |
|
558 if (priorityChannel) { |
|
559 priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST); |
|
560 } |
|
561 |
|
562 return channel->AsyncOpen(this, nullptr); |
|
563 } |
|
564 |
|
565 NS_IMETHODIMP |
|
566 AsyncFetchAndSetIconFromNetwork::OnStartRequest(nsIRequest* aRequest, |
|
567 nsISupports* aContext) |
|
568 { |
|
569 return NS_OK; |
|
570 } |
|
571 |
|
572 NS_IMETHODIMP |
|
573 AsyncFetchAndSetIconFromNetwork::OnDataAvailable(nsIRequest* aRequest, |
|
574 nsISupports* aContext, |
|
575 nsIInputStream* aInputStream, |
|
576 uint64_t aOffset, |
|
577 uint32_t aCount) |
|
578 { |
|
579 nsAutoCString buffer; |
|
580 nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer); |
|
581 if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) { |
|
582 return rv; |
|
583 } |
|
584 |
|
585 mIcon.data.Append(buffer); |
|
586 return NS_OK; |
|
587 } |
|
588 |
|
589 |
|
590 NS_IMETHODIMP |
|
591 AsyncFetchAndSetIconFromNetwork::GetInterface(const nsIID& uuid, |
|
592 void** aResult) |
|
593 { |
|
594 return QueryInterface(uuid, aResult); |
|
595 } |
|
596 |
|
597 |
|
598 NS_IMETHODIMP |
|
599 AsyncFetchAndSetIconFromNetwork::AsyncOnChannelRedirect( |
|
600 nsIChannel* oldChannel |
|
601 , nsIChannel* newChannel |
|
602 , uint32_t flags |
|
603 , nsIAsyncVerifyRedirectCallback *cb |
|
604 ) |
|
605 { |
|
606 (void)cb->OnRedirectVerifyCallback(NS_OK); |
|
607 return NS_OK; |
|
608 } |
|
609 |
|
610 NS_IMETHODIMP |
|
611 AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest, |
|
612 nsISupports* aContext, |
|
613 nsresult aStatusCode) |
|
614 { |
|
615 MOZ_ASSERT(NS_IsMainThread()); |
|
616 |
|
617 nsFaviconService* favicons = nsFaviconService::GetFaviconService(); |
|
618 NS_ENSURE_STATE(favicons); |
|
619 |
|
620 nsresult rv; |
|
621 |
|
622 // If fetching the icon failed, add it to the failed cache. |
|
623 if (NS_FAILED(aStatusCode) || mIcon.data.Length() == 0) { |
|
624 nsCOMPtr<nsIURI> iconURI; |
|
625 rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec); |
|
626 NS_ENSURE_SUCCESS(rv, rv); |
|
627 rv = favicons->AddFailedFavicon(iconURI); |
|
628 NS_ENSURE_SUCCESS(rv, rv); |
|
629 return NS_OK; |
|
630 } |
|
631 |
|
632 NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, aRequest, |
|
633 TO_INTBUFFER(mIcon.data), mIcon.data.Length(), |
|
634 mIcon.mimeType); |
|
635 |
|
636 // If the icon does not have a valid MIME type, add it to the failed cache. |
|
637 if (mIcon.mimeType.IsEmpty()) { |
|
638 nsCOMPtr<nsIURI> iconURI; |
|
639 rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec); |
|
640 NS_ENSURE_SUCCESS(rv, rv); |
|
641 rv = favicons->AddFailedFavicon(iconURI); |
|
642 NS_ENSURE_SUCCESS(rv, rv); |
|
643 return NS_OK; |
|
644 } |
|
645 |
|
646 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); |
|
647 // aRequest should always QI to nsIChannel. |
|
648 // See AsyncFetchAndSetIconFromNetwork::Run() |
|
649 MOZ_ASSERT(channel); |
|
650 mIcon.expiration = GetExpirationTimeFromChannel(channel); |
|
651 |
|
652 rv = OptimizeIconSize(mIcon, favicons); |
|
653 NS_ENSURE_SUCCESS(rv, rv); |
|
654 |
|
655 // If over the maximum size allowed, don't save data to the database to |
|
656 // avoid bloating it. |
|
657 if (mIcon.data.Length() > MAX_FAVICON_SIZE) { |
|
658 return NS_OK; |
|
659 } |
|
660 |
|
661 mIcon.status = ICON_STATUS_CHANGED; |
|
662 |
|
663 nsRefPtr<AsyncAssociateIconToPage> event = |
|
664 new AsyncAssociateIconToPage(mIcon, mPage, mCallback); |
|
665 mDB->DispatchToAsyncThread(event); |
|
666 |
|
667 return NS_OK; |
|
668 } |
|
669 |
|
670 //////////////////////////////////////////////////////////////////////////////// |
|
671 //// AsyncAssociateIconToPage |
|
672 |
|
673 AsyncAssociateIconToPage::AsyncAssociateIconToPage( |
|
674 IconData& aIcon |
|
675 , PageData& aPage |
|
676 , nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
677 ) : AsyncFaviconHelperBase(aCallback) |
|
678 , mIcon(aIcon) |
|
679 , mPage(aPage) |
|
680 { |
|
681 } |
|
682 |
|
683 AsyncAssociateIconToPage::~AsyncAssociateIconToPage() |
|
684 { |
|
685 } |
|
686 |
|
687 NS_IMETHODIMP |
|
688 AsyncAssociateIconToPage::Run() |
|
689 { |
|
690 NS_PRECONDITION(!NS_IsMainThread(), |
|
691 "This should not be called on the main thread"); |
|
692 |
|
693 nsresult rv = FetchPageInfo(mDB, mPage); |
|
694 if (rv == NS_ERROR_NOT_AVAILABLE){ |
|
695 // We have never seen this page. If we can add the page to history, |
|
696 // we will try to do it later, otherwise just bail out. |
|
697 if (!mPage.canAddToHistory) { |
|
698 return NS_OK; |
|
699 } |
|
700 } |
|
701 else { |
|
702 NS_ENSURE_SUCCESS(rv, rv); |
|
703 } |
|
704 |
|
705 mozStorageTransaction transaction(mDB->MainConn(), false, |
|
706 mozIStorageConnection::TRANSACTION_IMMEDIATE); |
|
707 |
|
708 // If there is no entry for this icon, or the entry is obsolete, replace it. |
|
709 if (mIcon.id == 0 || (mIcon.status & ICON_STATUS_CHANGED)) { |
|
710 rv = SetIconInfo(mDB, mIcon); |
|
711 NS_ENSURE_SUCCESS(rv, rv); |
|
712 |
|
713 // Get the new icon id. Do this regardless mIcon.id, since other code |
|
714 // could have added a entry before us. Indeed we interrupted the thread |
|
715 // after the previous call to FetchIconInfo. |
|
716 mIcon.status = (mIcon.status & ~(ICON_STATUS_CACHED)) | ICON_STATUS_SAVED; |
|
717 rv = FetchIconInfo(mDB, mIcon); |
|
718 NS_ENSURE_SUCCESS(rv, rv); |
|
719 } |
|
720 |
|
721 // If the page does not have an id, don't try to insert a new one, cause we |
|
722 // don't know where the page comes from. Not doing so we may end adding |
|
723 // a page that otherwise we'd explicitly ignore, like a POST or an error page. |
|
724 if (mPage.id == 0) { |
|
725 return NS_OK; |
|
726 } |
|
727 |
|
728 // Otherwise just associate the icon to the page, if needed. |
|
729 if (mPage.iconId != mIcon.id) { |
|
730 nsCOMPtr<mozIStorageStatement> stmt; |
|
731 if (mPage.id) { |
|
732 stmt = mDB->GetStatement( |
|
733 "UPDATE moz_places SET favicon_id = :icon_id WHERE id = :page_id" |
|
734 ); |
|
735 NS_ENSURE_STATE(stmt); |
|
736 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id); |
|
737 NS_ENSURE_SUCCESS(rv, rv); |
|
738 } |
|
739 else { |
|
740 stmt = mDB->GetStatement( |
|
741 "UPDATE moz_places SET favicon_id = :icon_id WHERE url = :page_url" |
|
742 ); |
|
743 NS_ENSURE_STATE(stmt); |
|
744 rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec); |
|
745 NS_ENSURE_SUCCESS(rv, rv); |
|
746 } |
|
747 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), mIcon.id); |
|
748 NS_ENSURE_SUCCESS(rv, rv); |
|
749 |
|
750 mozStorageStatementScoper scoper(stmt); |
|
751 rv = stmt->Execute(); |
|
752 NS_ENSURE_SUCCESS(rv, rv); |
|
753 |
|
754 mIcon.status |= ICON_STATUS_ASSOCIATED; |
|
755 } |
|
756 |
|
757 rv = transaction.Commit(); |
|
758 NS_ENSURE_SUCCESS(rv, rv); |
|
759 |
|
760 // Finally, dispatch an event to the main thread to notify observers. |
|
761 nsCOMPtr<nsIRunnable> event = new NotifyIconObservers(mIcon, mPage, mCallback); |
|
762 rv = NS_DispatchToMainThread(event); |
|
763 NS_ENSURE_SUCCESS(rv, rv); |
|
764 |
|
765 return NS_OK; |
|
766 } |
|
767 |
|
768 //////////////////////////////////////////////////////////////////////////////// |
|
769 //// AsyncGetFaviconURLForPage |
|
770 |
|
771 // static |
|
772 nsresult |
|
773 AsyncGetFaviconURLForPage::start(nsIURI* aPageURI, |
|
774 nsIFaviconDataCallback* aCallback) |
|
775 { |
|
776 NS_ENSURE_ARG(aCallback); |
|
777 NS_ENSURE_ARG(aPageURI); |
|
778 NS_PRECONDITION(NS_IsMainThread(), |
|
779 "This should be called on the main thread."); |
|
780 |
|
781 nsAutoCString pageSpec; |
|
782 nsresult rv = aPageURI->GetSpec(pageSpec); |
|
783 NS_ENSURE_SUCCESS(rv, rv); |
|
784 |
|
785 nsCOMPtr<nsIFaviconDataCallback> callback = aCallback; |
|
786 nsRefPtr<AsyncGetFaviconURLForPage> event = |
|
787 new AsyncGetFaviconURLForPage(pageSpec, callback); |
|
788 |
|
789 nsRefPtr<Database> DB = Database::GetDatabase(); |
|
790 NS_ENSURE_STATE(DB); |
|
791 DB->DispatchToAsyncThread(event); |
|
792 |
|
793 return NS_OK; |
|
794 } |
|
795 |
|
796 AsyncGetFaviconURLForPage::AsyncGetFaviconURLForPage( |
|
797 const nsACString& aPageSpec |
|
798 , nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
799 ) : AsyncFaviconHelperBase(aCallback) |
|
800 { |
|
801 mPageSpec.Assign(aPageSpec); |
|
802 } |
|
803 |
|
804 AsyncGetFaviconURLForPage::~AsyncGetFaviconURLForPage() |
|
805 { |
|
806 } |
|
807 |
|
808 NS_IMETHODIMP |
|
809 AsyncGetFaviconURLForPage::Run() |
|
810 { |
|
811 NS_PRECONDITION(!NS_IsMainThread(), |
|
812 "This should not be called on the main thread."); |
|
813 |
|
814 nsAutoCString iconSpec; |
|
815 nsresult rv = FetchIconURL(mDB, mPageSpec, iconSpec); |
|
816 NS_ENSURE_SUCCESS(rv, rv); |
|
817 |
|
818 // Now notify our callback of the icon spec we retrieved, even if empty. |
|
819 IconData iconData; |
|
820 iconData.spec.Assign(iconSpec); |
|
821 |
|
822 PageData pageData; |
|
823 pageData.spec.Assign(mPageSpec); |
|
824 |
|
825 nsCOMPtr<nsIRunnable> event = |
|
826 new NotifyIconObservers(iconData, pageData, mCallback); |
|
827 rv = NS_DispatchToMainThread(event); |
|
828 NS_ENSURE_SUCCESS(rv, rv); |
|
829 |
|
830 return NS_OK; |
|
831 } |
|
832 |
|
833 //////////////////////////////////////////////////////////////////////////////// |
|
834 //// AsyncGetFaviconDataForPage |
|
835 |
|
836 // static |
|
837 nsresult |
|
838 AsyncGetFaviconDataForPage::start(nsIURI* aPageURI, |
|
839 nsIFaviconDataCallback* aCallback) |
|
840 { |
|
841 NS_ENSURE_ARG(aCallback); |
|
842 NS_ENSURE_ARG(aPageURI); |
|
843 NS_PRECONDITION(NS_IsMainThread(), |
|
844 "This should be called on the main thread."); |
|
845 |
|
846 nsAutoCString pageSpec; |
|
847 nsresult rv = aPageURI->GetSpec(pageSpec); |
|
848 NS_ENSURE_SUCCESS(rv, rv); |
|
849 |
|
850 nsCOMPtr<nsIFaviconDataCallback> callback = aCallback; |
|
851 nsRefPtr<AsyncGetFaviconDataForPage> event = |
|
852 new AsyncGetFaviconDataForPage(pageSpec, callback); |
|
853 |
|
854 nsRefPtr<Database> DB = Database::GetDatabase(); |
|
855 NS_ENSURE_STATE(DB); |
|
856 DB->DispatchToAsyncThread(event); |
|
857 |
|
858 return NS_OK; |
|
859 } |
|
860 |
|
861 AsyncGetFaviconDataForPage::AsyncGetFaviconDataForPage( |
|
862 const nsACString& aPageSpec |
|
863 , nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
864 ) : AsyncFaviconHelperBase(aCallback) |
|
865 { |
|
866 mPageSpec.Assign(aPageSpec); |
|
867 } |
|
868 |
|
869 AsyncGetFaviconDataForPage::~AsyncGetFaviconDataForPage() |
|
870 { |
|
871 } |
|
872 |
|
873 NS_IMETHODIMP |
|
874 AsyncGetFaviconDataForPage::Run() |
|
875 { |
|
876 NS_PRECONDITION(!NS_IsMainThread(), |
|
877 "This should not be called on the main thread."); |
|
878 |
|
879 nsAutoCString iconSpec; |
|
880 nsresult rv = FetchIconURL(mDB, mPageSpec, iconSpec); |
|
881 NS_ENSURE_SUCCESS(rv, rv); |
|
882 |
|
883 IconData iconData; |
|
884 iconData.spec.Assign(iconSpec); |
|
885 |
|
886 PageData pageData; |
|
887 pageData.spec.Assign(mPageSpec); |
|
888 |
|
889 if (!iconSpec.IsEmpty()) { |
|
890 rv = FetchIconInfo(mDB, iconData); |
|
891 if (NS_FAILED(rv)) { |
|
892 iconData.spec.Truncate(); |
|
893 } |
|
894 } |
|
895 |
|
896 nsCOMPtr<nsIRunnable> event = |
|
897 new NotifyIconObservers(iconData, pageData, mCallback); |
|
898 rv = NS_DispatchToMainThread(event); |
|
899 NS_ENSURE_SUCCESS(rv, rv); |
|
900 return NS_OK; |
|
901 } |
|
902 |
|
903 //////////////////////////////////////////////////////////////////////////////// |
|
904 //// AsyncReplaceFaviconData |
|
905 |
|
906 // static |
|
907 nsresult |
|
908 AsyncReplaceFaviconData::start(IconData *aIcon) |
|
909 { |
|
910 NS_ENSURE_ARG(aIcon); |
|
911 NS_PRECONDITION(NS_IsMainThread(), |
|
912 "This should be called on the main thread."); |
|
913 |
|
914 nsCOMPtr<nsIFaviconDataCallback> callback; |
|
915 nsRefPtr<AsyncReplaceFaviconData> event = |
|
916 new AsyncReplaceFaviconData(*aIcon, callback); |
|
917 |
|
918 nsRefPtr<Database> DB = Database::GetDatabase(); |
|
919 NS_ENSURE_STATE(DB); |
|
920 DB->DispatchToAsyncThread(event); |
|
921 |
|
922 return NS_OK; |
|
923 } |
|
924 |
|
925 AsyncReplaceFaviconData::AsyncReplaceFaviconData( |
|
926 IconData &aIcon |
|
927 , nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
928 ) : AsyncFaviconHelperBase(aCallback) |
|
929 , mIcon(aIcon) |
|
930 { |
|
931 } |
|
932 |
|
933 AsyncReplaceFaviconData::~AsyncReplaceFaviconData() |
|
934 { |
|
935 } |
|
936 |
|
937 NS_IMETHODIMP |
|
938 AsyncReplaceFaviconData::Run() |
|
939 { |
|
940 NS_PRECONDITION(!NS_IsMainThread(), |
|
941 "This should not be called on the main thread"); |
|
942 |
|
943 IconData dbIcon; |
|
944 dbIcon.spec.Assign(mIcon.spec); |
|
945 nsresult rv = FetchIconInfo(mDB, dbIcon); |
|
946 NS_ENSURE_SUCCESS(rv, rv); |
|
947 |
|
948 if (!dbIcon.id) { |
|
949 return NS_OK; |
|
950 } |
|
951 |
|
952 rv = SetIconInfo(mDB, mIcon); |
|
953 NS_ENSURE_SUCCESS(rv, rv); |
|
954 |
|
955 // We can invalidate the cache version since we now persist the icon. |
|
956 nsCOMPtr<nsIRunnable> event = new RemoveIconDataCacheEntry(mIcon, mCallback); |
|
957 rv = NS_DispatchToMainThread(event); |
|
958 NS_ENSURE_SUCCESS(rv, rv); |
|
959 |
|
960 return NS_OK; |
|
961 } |
|
962 |
|
963 |
|
964 //////////////////////////////////////////////////////////////////////////////// |
|
965 //// RemoveIconDataCacheEntry |
|
966 |
|
967 RemoveIconDataCacheEntry::RemoveIconDataCacheEntry( |
|
968 IconData& aIcon |
|
969 , nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
970 ) |
|
971 : AsyncFaviconHelperBase(aCallback) |
|
972 , mIcon(aIcon) |
|
973 { |
|
974 } |
|
975 |
|
976 RemoveIconDataCacheEntry::~RemoveIconDataCacheEntry() |
|
977 { |
|
978 } |
|
979 |
|
980 NS_IMETHODIMP |
|
981 RemoveIconDataCacheEntry::Run() |
|
982 { |
|
983 NS_PRECONDITION(NS_IsMainThread(), |
|
984 "This should be called on the main thread"); |
|
985 |
|
986 nsCOMPtr<nsIURI> iconURI; |
|
987 nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec); |
|
988 NS_ENSURE_SUCCESS(rv, rv); |
|
989 |
|
990 nsFaviconService* favicons = nsFaviconService::GetFaviconService(); |
|
991 NS_ENSURE_STATE(favicons); |
|
992 favicons->mUnassociatedIcons.RemoveEntry(iconURI); |
|
993 |
|
994 return NS_OK; |
|
995 } |
|
996 |
|
997 |
|
998 //////////////////////////////////////////////////////////////////////////////// |
|
999 //// NotifyIconObservers |
|
1000 |
|
1001 NotifyIconObservers::NotifyIconObservers( |
|
1002 IconData& aIcon |
|
1003 , PageData& aPage |
|
1004 , nsCOMPtr<nsIFaviconDataCallback>& aCallback |
|
1005 ) |
|
1006 : AsyncFaviconHelperBase(aCallback) |
|
1007 , mIcon(aIcon) |
|
1008 , mPage(aPage) |
|
1009 { |
|
1010 } |
|
1011 |
|
1012 NotifyIconObservers::~NotifyIconObservers() |
|
1013 { |
|
1014 } |
|
1015 |
|
1016 NS_IMETHODIMP |
|
1017 NotifyIconObservers::Run() |
|
1018 { |
|
1019 NS_PRECONDITION(NS_IsMainThread(), |
|
1020 "This should be called on the main thread"); |
|
1021 |
|
1022 nsCOMPtr<nsIURI> iconURI; |
|
1023 if (!mIcon.spec.IsEmpty()) { |
|
1024 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_NewURI(getter_AddRefs(iconURI), mIcon.spec))); |
|
1025 if (iconURI) |
|
1026 { |
|
1027 // Notify observers only if something changed. |
|
1028 if (mIcon.status & ICON_STATUS_SAVED || |
|
1029 mIcon.status & ICON_STATUS_ASSOCIATED) { |
|
1030 SendGlobalNotifications(iconURI); |
|
1031 } |
|
1032 } |
|
1033 } |
|
1034 |
|
1035 if (mCallback) { |
|
1036 (void)mCallback->OnComplete(iconURI, mIcon.data.Length(), |
|
1037 TO_INTBUFFER(mIcon.data), mIcon.mimeType); |
|
1038 } |
|
1039 |
|
1040 return NS_OK; |
|
1041 } |
|
1042 |
|
1043 void |
|
1044 NotifyIconObservers::SendGlobalNotifications(nsIURI* aIconURI) |
|
1045 { |
|
1046 nsCOMPtr<nsIURI> pageURI; |
|
1047 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pageURI), mPage.spec))); |
|
1048 if (pageURI) { |
|
1049 nsFaviconService* favicons = nsFaviconService::GetFaviconService(); |
|
1050 MOZ_ASSERT(favicons); |
|
1051 if (favicons) { |
|
1052 (void)favicons->SendFaviconNotifications(pageURI, aIconURI, mPage.guid); |
|
1053 } |
|
1054 } |
|
1055 |
|
1056 // If the page is bookmarked and the bookmarked url is different from the |
|
1057 // updated one, start a new task to update its icon as well. |
|
1058 if (!mPage.bookmarkedSpec.IsEmpty() && |
|
1059 !mPage.bookmarkedSpec.Equals(mPage.spec)) { |
|
1060 // Create a new page struct to avoid polluting it with old data. |
|
1061 PageData bookmarkedPage; |
|
1062 bookmarkedPage.spec = mPage.bookmarkedSpec; |
|
1063 |
|
1064 // This will be silent, so be sure to not pass in the current callback. |
|
1065 nsCOMPtr<nsIFaviconDataCallback> nullCallback; |
|
1066 nsRefPtr<AsyncAssociateIconToPage> event = |
|
1067 new AsyncAssociateIconToPage(mIcon, bookmarkedPage, nullCallback); |
|
1068 mDB->DispatchToAsyncThread(event); |
|
1069 } |
|
1070 } |
|
1071 |
|
1072 } // namespace places |
|
1073 } // namespace mozilla |