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