Bug 606966 - Need an async history visit API exposed to JS

Part 24 - Dispatch observer topic when done
r=mak
a=blocking
This commit is contained in:
Shawn Wilsher 2011-01-25 09:41:42 -08:00
parent 41a8c2ebd1
commit a4ff998a92
5 changed files with 136 additions and 61 deletions

View File

@ -15,7 +15,7 @@
* The Original Code is Places code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
@ -42,6 +42,7 @@
#include "prio.h"
#include "nsString.h"
#include "nsNavHistory.h"
#include "mozilla/Services.h"
// The length of guids that are used by history and bookmarks.
#define GUID_LENGTH 12
@ -357,5 +358,57 @@ GetHiddenState(bool aIsRedirect,
aIsRedirect;
}
////////////////////////////////////////////////////////////////////////////////
//// PlacesEvent
PlacesEvent::PlacesEvent(const char* aTopic)
: mTopic(aTopic)
, mDoubleEnqueue(false)
{
}
PlacesEvent::PlacesEvent(const char* aTopic,
bool aDoubleEnqueue)
: mTopic(aTopic)
, mDoubleEnqueue(aDoubleEnqueue)
{
}
NS_IMETHODIMP
PlacesEvent::Run()
{
Notify();
return NS_OK;
}
NS_IMETHODIMP
PlacesEvent::Complete()
{
Notify();
return NS_OK;
}
void
PlacesEvent::Notify()
{
if (mDoubleEnqueue) {
mDoubleEnqueue = false;
(void)NS_DispatchToMainThread(this);
}
else {
NS_ASSERTION(NS_IsMainThread(), "Must only be used on the main thread!");
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
(void)obs->NotifyObservers(nsnull, mTopic, nsnull);
}
}
}
NS_IMPL_THREADSAFE_ISUPPORTS2(
PlacesEvent
, mozIStorageCompletionCallback
, nsIRunnable
)
} // namespace places
} // namespace mozilla

View File

@ -280,6 +280,26 @@ void ForceWALCheckpoint(mozIStorageConnection* aDBConn);
bool GetHiddenState(bool aIsRedirect,
PRUint32 aTransitionType);
/**
* Notifies a specified topic via the observer service.
*/
class PlacesEvent : public nsRunnable
, public mozIStorageCompletionCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
PlacesEvent(const char* aTopic);
PlacesEvent(const char* aTopic, bool aDoubleEnqueue);
protected:
void Notify();
const char* const mTopic;
bool mDoubleEnqueue;
};
} // namespace places
} // namespace mozilla

View File

@ -62,6 +62,9 @@
// Initial size for the cache holding visited status observers.
#define VISIT_OBSERVERS_INITIAL_CACHE_SIZE 128
// Topic used to notify that work in mozIAsyncHistory::updatePlaces is done.
#define TOPIC_UPDATEPLACES_COMPLETE "places-updatePlaces-complete"
using namespace mozilla::dom;
namespace mozilla {
@ -1953,17 +1956,26 @@ History::UpdatePlaces(const jsval& aPlaceInfos,
}
}
mozIStorageConnection* dbConn = GetDBConn();
NS_ENSURE_STATE(dbConn);
// It is possible that all of the visits we were passed were dissallowed by
// CanAddURI, which isn't an error. If we have no visits to add, however,
// we should not call InsertVisitedURIs::Start.
if (visitData.Length()) {
mozIStorageConnection* dbConn = GetDBConn();
NS_ENSURE_STATE(dbConn);
nsresult rv = InsertVisitedURIs::Start(dbConn, visitData, aCallback);
NS_ENSURE_SUCCESS(rv, rv);
}
// Be sure to notify that all of our operations are complete. This is
// double enqueued to make sure that all database notifications and all embed
// or canAddURI notifications have finished.
nsCOMPtr<nsIEventTarget> backgroundThread = do_GetInterface(dbConn);
NS_ENSURE_TRUE(backgroundThread, NS_ERROR_UNEXPECTED);
nsRefPtr<PlacesEvent> completeEvent =
new PlacesEvent(TOPIC_UPDATEPLACES_COMPLETE, true);
(void)backgroundThread->Dispatch(completeEvent, 0);
return NS_OK;
}

View File

@ -316,62 +316,6 @@ protected:
};
class PlacesEvent : public nsRunnable
, public mozIStorageCompletionCallback
{
public:
NS_DECL_ISUPPORTS
PlacesEvent(const char* aTopic)
: mTopic(aTopic)
, mDoubleEnqueue(false)
{
}
PlacesEvent(const char* aTopic,
bool aDoubleEnqueue)
: mTopic(aTopic)
, mDoubleEnqueue(aDoubleEnqueue)
{
}
NS_IMETHODIMP Run()
{
Notify();
return NS_OK;
}
NS_IMETHODIMP Complete()
{
Notify();
return NS_OK;
}
protected:
void Notify()
{
if (mDoubleEnqueue) {
mDoubleEnqueue = false;
(void)NS_DispatchToMainThread(this);
}
else {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs)
(void)obs->NotifyObservers(nsnull, mTopic, nsnull);
}
}
const char* const mTopic;
bool mDoubleEnqueue;
};
NS_IMPL_ISUPPORTS2(
PlacesEvent
, mozIStorageCompletionCallback
, nsIRunnable
)
// Used to notify a topic to system observers on async execute completion.
class AsyncStatementCallbackNotifier : public AsyncStatementCallback
{

View File

@ -16,7 +16,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "gGlobalHistory",
"@mozilla.org/browser/nav-history-service;1",
"nsIGlobalHistory2");
const TEST_DOMAIN = "http://mozilla.org/"
const TEST_DOMAIN = "http://mozilla.org/";
const TOPIC_UPDATEPLACES_COMPLETE = "places-updatePlaces-complete";
////////////////////////////////////////////////////////////////////////////////
//// Helpers
@ -407,6 +408,50 @@ function test_non_addable_uri_errors()
});
}
function test_observer_topic_dispatched_when_complete()
{
// We test a normal visit, and embeded visit, and a uri that would fail
// the canAddURI test to make sure that the notification happens after *all*
// of them have had a callback.
let places = [
{ uri: NetUtil.newURI(TEST_DOMAIN +
"test_observer_topic_dispatched_when_complete"),
visits: [
new VisitInfo(),
new VisitInfo(TRANSITION_EMBED),
],
},
{ uri: NetUtil.newURI("data:,Hello%2C%20World!"),
visits: [
new VisitInfo(),
],
},
];
do_check_false(gGlobalHistory.isVisited(places[0].uri));
do_check_false(gGlobalHistory.isVisited(places[1].uri));
const EXPECTED_COUNT = 3;
let callbackCount = 0;
gHistory.updatePlaces(places, function(aResultCode, aPlaceInfo) {
let checker = PlacesUtils.history.canAddURI(aPlaceInfo.uri) ?
do_check_true : do_check_false;
checker(Components.isSuccessCode(aResultCode));
callbackCount++;
});
let observer = {
observe: function(aSubject, aTopic, aData)
{
do_check_eq(aTopic, TOPIC_UPDATEPLACES_COMPLETE);
do_check_eq(callbackCount, EXPECTED_COUNT);
Services.obs.removeObserver(observer, TOPIC_UPDATEPLACES_COMPLETE);
run_next_test();
},
};
Services.obs.addObserver(observer, TOPIC_UPDATEPLACES_COMPLETE, false);
}
function test_add_visit()
{
const VISIT_TIME = Date.now() * 1000;
@ -835,6 +880,7 @@ let gTests = [
test_add_visit_no_transitionType_throws,
test_add_visit_invalid_transitionType_throws,
test_non_addable_uri_errors,
test_observer_topic_dispatched_when_complete,
test_add_visit,
test_properties_saved,
test_guid_saved,