Bug 1265420 - SetAndFetchFaviconForPage should return a cancelable object to allow aborting the fetch. r=Gijs
MozReview-Commit-ID: Leu4iZBkP7z
--- 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