Bug 1265420 - SetAndFetchFaviconForPage should return a cancelable object to allow aborting the fetch. r=Gijs draft
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 03 May 2016 15:42:12 +0200
changeset 363268 03ac724d128d8d80ecc099af6fa5f84a85a7d02d
parent 358343 5e9136916f72c2e28363b9be5ca90220d78ebe3b
child 519999 48098eb7dce9e700a213f3e9c40d311df57302a0
push id17162
push usermak77@bonardo.net
push dateWed, 04 May 2016 12:42:10 +0000
reviewersGijs
bugs1265420
milestone49.0a1
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