toolkit/components/places/AsyncFaviconHelpers.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial