Bug 1265420 - SetAndFetchFaviconForPage should return a cancelable object to allow aborting the fetch. r=Gijs
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 03 May 2016 15:42:12 +0200
changeset 296142 e8597da96606eb42367533e6eb65c5d318bf440c
parent 296141 ebf3313b6a4385f0817c6f643ce1e009ef3ae2c4
child 296143 805453c94735b8e1bd22b767f979ed9fb451454d
push id30233
push userryanvm@gmail.com
push dateThu, 05 May 2016 18:57:26 +0000
treeherdermozilla-central@0177462aac74 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1265420
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1265420 - SetAndFetchFaviconForPage should return a cancelable object to allow aborting the fetch. r=Gijs MozReview-Commit-ID: Leu4iZBkP7z
docshell/base/nsDocShell.cpp
toolkit/components/places/AsyncFaviconHelpers.cpp
toolkit/components/places/AsyncFaviconHelpers.h
toolkit/components/places/moz.build
toolkit/components/places/mozIAsyncFavicons.idl
toolkit/components/places/mozIPlacesPendingOperation.idl
toolkit/components/places/nsFaviconService.cpp
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -129,16 +129,17 @@
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIWebBrowserChrome3.h"
 #include "nsITabChild.h"
 #include "nsISiteSecurityService.h"
 #include "nsStructuredCloneContainer.h"
 #include "nsIStructuredCloneContainer.h"
 #ifdef MOZ_PLACES
 #include "nsIFaviconService.h"
+#include "mozIPlacesPendingOperation.h"
 #include "mozIAsyncFavicons.h"
 #endif
 #include "nsINetworkPredictor.h"
 
 // Editor-related
 #include "nsIEditingSession.h"
 
 #include "nsPIDOMWindow.h"
@@ -9407,21 +9408,22 @@ public:
     // Continue only if there is an associated favicon.
     if (!aFaviconURI) {
       return NS_OK;
     }
 
     MOZ_ASSERT(aDataLen == 0,
                "We weren't expecting the callback to deliver data.");
 
+    nsCOMPtr<mozIPlacesPendingOperation> po;
     return mSvc->SetAndFetchFaviconForPage(
       mNewURI, aFaviconURI, false,
       mInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE :
                            nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
-      nullptr, mLoadingPrincipal);
+      nullptr, mLoadingPrincipal, getter_AddRefs(po));
   }
 
 private:
   ~nsCopyFaviconCallback() {}
 
   nsCOMPtr<mozIAsyncFavicons> mSvc;
   nsCOMPtr<nsIURI> mNewURI;
   nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
--- a/toolkit/components/places/AsyncFaviconHelpers.cpp
+++ b/toolkit/components/places/AsyncFaviconHelpers.cpp
@@ -360,40 +360,50 @@ AsyncFaviconHelperBase::~AsyncFaviconHel
   if (mCallback) {
     NS_ReleaseOnMainThread(mCallback.forget(), true);
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AsyncFetchAndSetIconForPage
 
+NS_IMPL_ISUPPORTS_INHERITED(
+  AsyncFetchAndSetIconForPage
+, Runnable
+, nsIStreamListener
+, nsIInterfaceRequestor
+, nsIChannelEventSink
+, mozIPlacesPendingOperation
+)
+
 // static
 nsresult
 AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI,
                                    nsIURI* aPageURI,
                                    enum AsyncFaviconFetchMode aFetchMode,
-                                   uint32_t aFaviconLoadType,
+                                   bool aFaviconLoadPrivate,
                                    nsIFaviconDataCallback* aCallback,
-                                   nsIPrincipal* aLoadingPrincipal)
+                                   nsIPrincipal* aLoadingPrincipal,
+                                   mozIPlacesPendingOperation** _canceler)
 {
   NS_ENSURE_ARG(aLoadingPrincipal);
   NS_PRECONDITION(NS_IsMainThread(),
                   "This should be called on the main thread");
 
   PageData page;
   nsresult rv = aPageURI->GetSpec(page.spec);
   NS_ENSURE_SUCCESS(rv, rv);
   // URIs can arguably miss a host.
   (void)GetReversedHostname(aPageURI, page.revHost);
   bool canAddToHistory;
   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
   rv = navHistory->CanAddURI(aPageURI, &canAddToHistory);
   NS_ENSURE_SUCCESS(rv, rv);
-  page.canAddToHistory = !!canAddToHistory && aFaviconLoadType != nsIFaviconService::FAVICON_LOAD_PRIVATE;
+  page.canAddToHistory = !!canAddToHistory && !aFaviconLoadPrivate;
 
   IconData icon;
 
   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
   NS_ENSURE_STATE(favicons);
 
   UnassociatedIconHashKey* iconKey =
     favicons->mUnassociatedIcons.GetEntry(aFaviconURI);
@@ -414,42 +424,42 @@ AsyncFetchAndSetIconForPage::start(nsIUR
   if (icon.spec.Equals(page.spec) ||
       icon.spec.Equals(FAVICON_ERRORPAGE_URL)) {
     return NS_OK;
   }
 
   // The event will swap owning pointers, thus we need a new pointer.
   nsCOMPtr<nsIFaviconDataCallback> callback(aCallback);
   RefPtr<AsyncFetchAndSetIconForPage> event =
-    new AsyncFetchAndSetIconForPage(icon, page, aFaviconLoadType,
+    new AsyncFetchAndSetIconForPage(icon, page, aFaviconLoadPrivate,
                                     callback, aLoadingPrincipal);
 
   // Get the target thread and start the work.
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
   DB->DispatchToAsyncThread(event);
 
+  // Return this event to the caller to allow aborting an eventual fetch.
+  event.forget(_canceler);
+
   return NS_OK;
 }
 
 AsyncFetchAndSetIconForPage::AsyncFetchAndSetIconForPage(
   IconData& aIcon
 , PageData& aPage
-, uint32_t aFaviconLoadType
+, bool aFaviconLoadPrivate
 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
 , nsIPrincipal* aLoadingPrincipal
 ) : AsyncFaviconHelperBase(aCallback)
   , mIcon(aIcon)
   , mPage(aPage)
-  , mFaviconLoadPrivate(aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE)
+  , mFaviconLoadPrivate(aFaviconLoadPrivate)
   , mLoadingPrincipal(new nsMainThreadPtrHolder<nsIPrincipal>(aLoadingPrincipal))
-{
-}
-
-AsyncFetchAndSetIconForPage::~AsyncFetchAndSetIconForPage()
+  , mCanceled(false)
 {
 }
 
 NS_IMETHODIMP
 AsyncFetchAndSetIconForPage::Run()
 {
   NS_PRECONDITION(!NS_IsMainThread(),
                   "This should not be called on the main thread");
@@ -469,66 +479,31 @@ AsyncFetchAndSetIconForPage::Run()
     // There is already a valid icon or we don't want to fetch a new one,
     // directly proceed with association.
     RefPtr<AsyncAssociateIconToPage> event =
         new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
     DB->DispatchToAsyncThread(event);
 
     return NS_OK;
   }
-  else {
-    // Fetch the icon from network.  When done this will associate the
-    // icon to the page and notify.
-    RefPtr<AsyncFetchAndSetIconFromNetwork> event =
-      new AsyncFetchAndSetIconFromNetwork(mIcon, mPage, mFaviconLoadPrivate,
-                                          mCallback, mLoadingPrincipal);
 
-    // Start the work on the main thread.
-    rv = NS_DispatchToMainThread(event);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return NS_OK;
+  // Fetch the icon from the network, the request starts from the main-thread.
+  // When done this will associate the icon to the page and notify.
+  nsCOMPtr<nsIRunnable> event =
+    NS_NewRunnableMethod(this, &AsyncFetchAndSetIconForPage::FetchFromNetwork);
+  return NS_DispatchToMainThread(event);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-//// AsyncFetchAndSetIconFromNetwork
-
-NS_IMPL_ISUPPORTS_INHERITED(
-  AsyncFetchAndSetIconFromNetwork
-, Runnable
-, nsIStreamListener
-, nsIInterfaceRequestor
-, nsIChannelEventSink
-)
+nsresult
+AsyncFetchAndSetIconForPage::FetchFromNetwork() {
+  MOZ_ASSERT(NS_IsMainThread());
 
-AsyncFetchAndSetIconFromNetwork::AsyncFetchAndSetIconFromNetwork(
-  IconData& aIcon
-, PageData& aPage
-, bool aFaviconLoadPrivate
-, nsCOMPtr<nsIFaviconDataCallback>& aCallback
-, const nsMainThreadPtrHandle<nsIPrincipal>& aLoadingPrincipal
-)
-: AsyncFaviconHelperBase(aCallback)
-, mIcon(aIcon)
-, mPage(aPage)
-, mFaviconLoadPrivate(aFaviconLoadPrivate)
-, mLoadingPrincipal(aLoadingPrincipal)
-{
-}
-
-AsyncFetchAndSetIconFromNetwork::~AsyncFetchAndSetIconFromNetwork()
-{
-}
-
-NS_IMETHODIMP
-AsyncFetchAndSetIconFromNetwork::Run()
-{
-  NS_PRECONDITION(NS_IsMainThread(),
-                  "This should be called on the main thread");
+  if (mCanceled) {
+    return NS_OK;
+  }
 
   // Ensure data is cleared, since it's going to be overwritten.
   if (mIcon.data.Length() > 0) {
     mIcon.data.Truncate(0);
     mIcon.mimeType.Truncate(0);
   }
 
   nsCOMPtr<nsIURI> iconURI;
@@ -557,28 +532,46 @@ AsyncFetchAndSetIconFromNetwork::Run()
   if (priorityChannel) {
     priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
   }
 
   return channel->AsyncOpen(this, nullptr);
 }
 
 NS_IMETHODIMP
-AsyncFetchAndSetIconFromNetwork::OnStartRequest(nsIRequest* aRequest,
-                                                nsISupports* aContext)
+AsyncFetchAndSetIconForPage::Cancel()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mCanceled) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  mCanceled = true;
+  if (mRequest) {
+    mRequest->Cancel(NS_BINDING_ABORTED);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-AsyncFetchAndSetIconFromNetwork::OnDataAvailable(nsIRequest* aRequest,
-                                                 nsISupports* aContext,
-                                                 nsIInputStream* aInputStream,
-                                                 uint64_t aOffset,
-                                                 uint32_t aCount)
+AsyncFetchAndSetIconForPage::OnStartRequest(nsIRequest* aRequest,
+                                            nsISupports* aContext)
+{
+  mRequest = aRequest;
+  if (mCanceled) {
+    mRequest->Cancel(NS_BINDING_ABORTED);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AsyncFetchAndSetIconForPage::OnDataAvailable(nsIRequest* aRequest,
+                                             nsISupports* aContext,
+                                             nsIInputStream* aInputStream,
+                                             uint64_t aOffset,
+                                             uint32_t aCount)
 {
   const size_t kMaxFaviconDownloadSize = 1 * 1024 * 1024;
   if (mIcon.data.Length() + aCount > kMaxFaviconDownloadSize) {
     mIcon.data.Truncate();
     return NS_ERROR_FILE_TOO_BIG;
   }
 
   nsAutoCString buffer;
@@ -592,42 +585,48 @@ AsyncFetchAndSetIconFromNetwork::OnDataA
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-AsyncFetchAndSetIconFromNetwork::GetInterface(const nsIID& uuid,
+AsyncFetchAndSetIconForPage::GetInterface(const nsIID& uuid,
                                               void** aResult)
 {
   return QueryInterface(uuid, aResult);
 }
 
 
 NS_IMETHODIMP
-AsyncFetchAndSetIconFromNetwork::AsyncOnChannelRedirect(
+AsyncFetchAndSetIconForPage::AsyncOnChannelRedirect(
   nsIChannel* oldChannel
 , nsIChannel* newChannel
 , uint32_t flags
 , nsIAsyncVerifyRedirectCallback *cb
 )
 {
   (void)cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest,
-                                               nsISupports* aContext,
-                                               nsresult aStatusCode)
+AsyncFetchAndSetIconForPage::OnStopRequest(nsIRequest* aRequest,
+                                           nsISupports* aContext,
+                                           nsresult aStatusCode)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  // Don't need to track this anymore.
+  mRequest = nullptr;
+  if (mCanceled) {
+    return NS_OK;
+  }
+
   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
   NS_ENSURE_STATE(favicons);
 
   nsresult rv;
 
   // If fetching the icon failed, add it to the failed cache.
   if (NS_FAILED(aStatusCode) || mIcon.data.Length() == 0) {
     nsCOMPtr<nsIURI> iconURI;
@@ -635,17 +634,16 @@ AsyncFetchAndSetIconFromNetwork::OnStopR
     NS_ENSURE_SUCCESS(rv, rv);
     rv = favicons->AddFailedFavicon(iconURI);
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   // aRequest should always QI to nsIChannel.
-  // See AsyncFetchAndSetIconFromNetwork::Run()
   MOZ_ASSERT(channel);
 
   nsAutoCString contentType;
   channel->GetContentType(contentType);
   // Bug 366324 - can't sniff SVG yet, so rely on server-specified type
   if (contentType.EqualsLiteral("image/svg+xml")) {
     mIcon.mimeType.AssignLiteral("image/svg+xml");
   } else {
--- a/toolkit/components/places/AsyncFaviconHelpers.h
+++ b/toolkit/components/places/AsyncFaviconHelpers.h
@@ -6,16 +6,17 @@
 
 #ifndef AsyncFaviconHelpers_h_
 #define AsyncFaviconHelpers_h_
 
 #include "nsIFaviconService.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIStreamListener.h"
+#include "mozIPlacesPendingOperation.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 
 class nsIPrincipal;
 
 #include "Database.h"
 #include "mozilla/storage.h"
 
@@ -108,113 +109,86 @@ protected:
   // Strong reference since we are responsible for its existence.
   nsCOMPtr<nsIFaviconDataCallback> mCallback;
 };
 
 /**
  * Async fetches icon from database or network, associates it with the required
  * page and finally notifies the change.
  */
-class AsyncFetchAndSetIconForPage : public AsyncFaviconHelperBase
-{
-public:
+class AsyncFetchAndSetIconForPage final : public AsyncFaviconHelperBase
+                                        , public nsIStreamListener
+                                        , public nsIInterfaceRequestor
+                                        , public nsIChannelEventSink
+                                        , public mozIPlacesPendingOperation
+ {
+ public:
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSICHANNELEVENTSINK
+  NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSIRUNNABLE
+  NS_DECL_MOZIPLACESPENDINGOPERATION
+  NS_DECL_ISUPPORTS_INHERITED
 
   /**
    * Creates the event and dispatches it to the async thread.
    *
    * @param aFaviconURI
    *        URI of the icon to be fetched and associated.
    * @param aPageURI
    *        URI of the page to which associate the icon.
    * @param aFetchMode
    *        Specifies whether a icon should be fetched from network if not found
    *        in the database.
+   * @param aFaviconLoadPrivate
+   *        Whether this favicon load is in private browsing.
    * @param aCallback
    *        Function to be called when the fetch-and-associate process finishes.
    * @param aLoadingPrincipal
    *        LoadingPrincipal of the icon to be fetched.
    */
   static nsresult start(nsIURI* aFaviconURI,
                         nsIURI* aPageURI,
                         enum AsyncFaviconFetchMode aFetchMode,
-                        uint32_t aFaviconLoadType,
+                        bool aFaviconLoadPrivate,
                         nsIFaviconDataCallback* aCallback,
-                        nsIPrincipal* aLoadingPrincipal);
+                        nsIPrincipal* aLoadingPrincipal,
+                        mozIPlacesPendingOperation ** _canceler);
 
   /**
    * Constructor.
    *
    * @param aIcon
    *        Icon to be fetched and associated.
    * @param aPage
    *        Page to which associate the icon.
+   * @param aFaviconLoadPrivate
+   *        Whether this favicon load is in private browsing.
    * @param aCallback
    *        Function to be called when the fetch-and-associate process finishes.
    * @param aLoadingPrincipal
    *        LoadingPrincipal of the icon to be fetched.
    */
   AsyncFetchAndSetIconForPage(IconData& aIcon,
                               PageData& aPage,
-                              uint32_t aFaviconLoadType,
+                              bool aFaviconLoadPrivate,
                               nsCOMPtr<nsIFaviconDataCallback>& aCallback,
                               nsIPrincipal* aLoadingPrincipal);
 
-  virtual ~AsyncFetchAndSetIconForPage();
-
-protected:
-  IconData mIcon;
-  PageData mPage;
-  const bool mFaviconLoadPrivate;
-  nsMainThreadPtrHandle<nsIPrincipal> mLoadingPrincipal;
-};
-
-/**
- * If needed will asynchronously fetch the icon from the network.  It will
- * finally dispatch an event to the async thread to associate the icon with
- * the required page.
- */
-class AsyncFetchAndSetIconFromNetwork : public AsyncFaviconHelperBase
-                                      , public nsIStreamListener
-                                      , public nsIInterfaceRequestor
-                                      , public nsIChannelEventSink
-{
-public:
-  NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSIINTERFACEREQUESTOR
-  NS_DECL_NSICHANNELEVENTSINK
-  NS_DECL_NSIREQUESTOBSERVER
-  NS_DECL_NSIRUNNABLE
-  NS_DECL_ISUPPORTS_INHERITED
-
-  /**
-   * Constructor.
-   *
-   * @param aIcon
-   *        Icon to be fetched and associated.
-   * @param aPage
-   *        Page to which associate the icon.
-   * @param aCallback
-   *        Function to be called when the fetch-and-associate process finishes.
-   * @param aLoadingPrincipal
-   *        LoadingPrincipal of the icon to be fetched.
-   */
-  AsyncFetchAndSetIconFromNetwork(IconData& aIcon,
-                                  PageData& aPage,
-                                  bool aFaviconLoadPrivate,
-                                  nsCOMPtr<nsIFaviconDataCallback>& aCallback,
-                                  const nsMainThreadPtrHandle<nsIPrincipal>& aLoadingPrincipal);
-
-protected:
-  virtual ~AsyncFetchAndSetIconFromNetwork();
+private:
+  nsresult FetchFromNetwork();
+  virtual ~AsyncFetchAndSetIconForPage() {}
 
   IconData mIcon;
   PageData mPage;
   const bool mFaviconLoadPrivate;
   nsMainThreadPtrHandle<nsIPrincipal> mLoadingPrincipal;
+  bool mCanceled;
+  nsCOMPtr<nsIRequest> mRequest;
 };
 
 /**
  * Associates the icon to the required page, finally dispatches an event to the
  * main thread to notify the change to observers.
  */
 class AsyncAssociateIconToPage : public AsyncFaviconHelperBase
 {
--- a/toolkit/components/places/moz.build
+++ b/toolkit/components/places/moz.build
@@ -15,16 +15,17 @@ XPIDL_MODULE = 'places'
 
 if CONFIG['MOZ_PLACES']:
     XPIDL_SOURCES += [
         'mozIAsyncFavicons.idl',
         'mozIAsyncHistory.idl',
         'mozIAsyncLivemarks.idl',
         'mozIColorAnalyzer.idl',
         'mozIPlacesAutoComplete.idl',
+        'mozIPlacesPendingOperation.idl',
         'nsIAnnotationService.idl',
         'nsIBrowserHistory.idl',
         'nsIFaviconService.idl',
         'nsINavBookmarksService.idl',
         'nsITaggingService.idl',
         'nsPIPlacesDatabase.idl',
     ]
 
--- a/toolkit/components/places/mozIAsyncFavicons.idl
+++ b/toolkit/components/places/mozIAsyncFavicons.idl
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIFaviconDataCallback;
 interface nsIPrincipal;
+interface mozIPlacesPendingOperation;
 
 [scriptable, uuid(a9c81797-9133-4823-b55f-3646e67cfd41)]
 interface mozIAsyncFavicons : nsISupports
 {
   /**
    * Declares that a given page uses a favicon with the given URI and 
    * attempts to fetch and save the icon data by loading the favicon URI
    * through an async network request.
@@ -51,23 +52,23 @@ interface mozIAsyncFavicons : nsISupport
    *        callback.
    * @param aLoadingPrincipal
    *        Principal of the page whose favicon is being set. If this argument
    *        is omitted, the loadingPrincipal defaults to the systemPrincipal
    *        and we cannot guarantee security checks anymore (see Bug 1227289)
    *
    * @see nsIFaviconDataCallback in nsIFaviconService.idl.
    */
-  void setAndFetchFaviconForPage(in nsIURI aPageURI,
-                                 in nsIURI aFaviconURI,
-                                 in boolean aForceReload,
-                                 in unsigned long aFaviconLoadType,
-                                 [optional] in nsIFaviconDataCallback aCallback,
-                                 [optional] in nsIPrincipal aLoadingPrincipal);
-
+  mozIPlacesPendingOperation setAndFetchFaviconForPage(
+    in nsIURI aPageURI,
+    in nsIURI aFaviconURI,
+    in boolean aForceReload,
+    in unsigned long aFaviconLoadType,
+    [optional] in nsIFaviconDataCallback aCallback,
+    [optional] in nsIPrincipal aLoadingPrincipal);
   /**
    * Sets the data for a given favicon URI either by replacing existing data in
    * the database or taking the place of otherwise fetched icon data when
    * calling setAndFetchFaviconForPage later.
    *
    * Favicon data for favicon URIs that are not associated with a page URI via
    * setAndFetchFaviconForPage will be stored in memory, but may be expired at
    * any time, so you should make an effort to associate favicon URIs with page
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/mozIPlacesPendingOperation.idl
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(ebd31374-3808-40e4-9e73-303bf70467c3)]
+interface mozIPlacesPendingOperation : nsISupports {
+  /**
+   * Cancels a pending operation, if possible.  This will only fail if you try
+   * to cancel more than once.
+   */
+   void cancel();
+};
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -207,20 +207,22 @@ nsFaviconService::SendFaviconNotificatio
 }
 
 NS_IMETHODIMP
 nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI,
                                             nsIURI* aFaviconURI,
                                             bool aForceReload,
                                             uint32_t aFaviconLoadType,
                                             nsIFaviconDataCallback* aCallback,
-                                            nsIPrincipal* aLoadingPrincipal)
+                                            nsIPrincipal* aLoadingPrincipal,
+                                            mozIPlacesPendingOperation **_canceler)
 {
   NS_ENSURE_ARG(aPageURI);
   NS_ENSURE_ARG(aFaviconURI);
+  NS_ENSURE_ARG_POINTER(_canceler);
 
   // If a favicon is in the failed cache, only load it during a forced reload.
   bool previouslyFailed;
   nsresult rv = IsFailedFavicon(aFaviconURI, &previouslyFailed);
   NS_ENSURE_SUCCESS(rv, rv);
   if (previouslyFailed) {
     if (aForceReload)
       RemoveFailedFavicon(aFaviconURI);
@@ -234,19 +236,20 @@ nsFaviconService::SetAndFetchFaviconForP
   MOZ_ASSERT(loadingPrincipal, "please provide aLoadingPrincipal for this favicon");
   if (!loadingPrincipal) {
     loadingPrincipal = nsContentUtils::GetSystemPrincipal();
   }
   NS_ENSURE_TRUE(loadingPrincipal, NS_ERROR_FAILURE);
 
   // Check if the icon already exists and fetch it from the network, if needed.
   // Finally associate the icon to the requested page if not yet associated.
+  bool loadPrivate = aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE;
   rv = AsyncFetchAndSetIconForPage::start(
     aFaviconURI, aPageURI, aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING,
-    aFaviconLoadType, aCallback, loadingPrincipal
+    loadPrivate, aCallback, loadingPrincipal, _canceler
   );
   NS_ENSURE_SUCCESS(rv, rv);
 
   // DB will be updated and observers notified when data has finished loading.
   return NS_OK;
 }
 
 NS_IMETHODIMP