--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -59,16 +59,22 @@ EXPORTS = \
nsScriptLoader.h \
nsStubDocumentObserver.h \
nsStubImageDecoderObserver.h \
nsStubMutationObserver.h \
nsTextFragment.h \
mozAutoDocUpdate.h \
$(NULL)
+EXPORTS_NAMESPACES = mozilla/dom
+
+EXPORTS_mozilla/dom = \
+ Link.h \
+ $(NULL)
+
CPPSRCS = \
mozSanitizingSerializer.cpp \
nsAtomListUtils.cpp \
nsAttrAndChildArray.cpp \
nsAttrValue.cpp \
nsCCUncollectableMarker.cpp \
nsCommentNode.cpp \
nsContentAreaDragDrop.cpp \
new file mode 100644
--- /dev/null
+++ b/docshell/base/IHistory.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=80:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_IHistory_h_
+#define mozilla_IHistory_h_
+
+#include "nsISupports.h"
+
+class nsIURI;
+
+namespace mozilla {
+
+ namespace dom {
+ class Link;
+ }
+
+#define IHISTORY_IID \
+ {0xaf27265d, 0x5672, 0x4d23, {0xa0, 0x75, 0x34, 0x8e, 0xb9, 0x73, 0x5a, 0x9a}}
+
+class IHistory : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(IHISTORY_IID)
+
+ /**
+ * Registers the Link for notifications about the visited-ness of aURI.
+ * Consumers should assume that the URI is unvisited after calling this, and
+ * they will be notified if that state (unvisited) changes by having
+ * SetLinkState called on themselves. This function is guaranteed to run to
+ * completion before aLink is notified. After the node is notified, it will
+ * be unregistered.
+ *
+ * @note SetLinkState must not call RegisterVisitedCallback or
+ * UnregisterVisitedCallback.
+ *
+ * @pre aURI must not be null.
+ * @pre aLink must not be null.
+ *
+ * @param aURI
+ * The URI to check.
+ * @param aLink
+ * The link to update whenever the history status changes. The
+ * implementation will only hold onto a raw pointer, so if this
+ * object should be destroyed, be sure to call
+ * UnregisterVistedCallback first.
+ */
+ NS_IMETHOD RegisterVisitedCallback(nsIURI *aURI, dom::Link *aLink) = 0;
+
+ /**
+ * Unregisters a previously registered Link object. This must be called
+ * before destroying the registered object.
+ *
+ * @pre aURI must not be null.
+ * @pre aLink must not be null.
+ *
+ * @param aURI
+ * The URI that aLink was registered for.
+ * @param aLink
+ * The link object to unregister for aURI.
+ */
+ NS_IMETHOD UnregisterVisitedCallback(nsIURI *aURI, dom::Link *aLink) = 0;
+
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IHistory, IHISTORY_IID)
+
+#define NS_DECL_IHISTORY \
+ NS_IMETHOD RegisterVisitedCallback(nsIURI *aURI, \
+ mozilla::dom::Link *aContent); \
+ NS_IMETHOD UnregisterVisitedCallback(nsIURI *aURI, \
+ mozilla::dom::Link *aContent);
+
+} // namespace mozilla
+
+#endif // mozilla_IHistory_h_
--- a/docshell/base/Makefile.in
+++ b/docshell/base/Makefile.in
@@ -77,16 +77,22 @@ XPIDLSRCS = \
nsIURIClassifier.idl \
nsIChannelClassifier.idl \
nsIDownloadHistory.idl \
nsILoadContext.idl \
$(NULL)
EXPORTS = nsDocShellLoadTypes.h
+EXPORTS_NAMESPACES = mozilla
+
+EXPORTS_mozilla = \
+ IHistory.h \
+ $(NULL)
+
CPPSRCS = \
nsDocShell.cpp \
nsDocShellLoadInfo.cpp \
nsDocShellEditorData.cpp \
nsDocShellTransferableHooks.cpp \
nsDocShellEnumerator.cpp \
nsDSURIContentListener.cpp \
nsDefaultURIFixup.cpp \
--- a/docshell/build/nsDocShellCID.h
+++ b/docshell/build/nsDocShellCID.h
@@ -81,17 +81,21 @@
* exact ones keep changing; if they stabilize somewhat that will get
* documented.
*/
#define NS_DOCSHELL_CID \
{ 0xf1eac762, 0x87e9, 0x11d3, \
{ 0xaf, 0x80, 0x00, 0xa0, 0x24, 0xff, 0xc0, 0x8c } }
#define NS_DOCSHELL_CONTRACTID "@mozilla.org/docshell/html;1"
-
+/**
+ * Contract ID to obtain the IHistory interface. This is a non-scriptable
+ * interface used to interact with history in an asynchronous manner.
+ */
+#define NS_IHISTORY_CONTRACTID "@mozilla.org/browser/history;1"
/**
* An observer service topic that can be listened to to catch creation
* of content browsing areas (both toplevel ones and subframes). The
* subject of the notification will be the nsIWebNavigation being
* created. At this time the additional data wstring is not defined
* to be anything in particular.
*/
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/src/History.cpp
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Places code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "History.h"
+#include "nsNavHistory.h"
+
+#include "mozilla/storage.h"
+#include "mozilla/dom/Link.h"
+#include "nsDocShellCID.h"
+#include "nsIEventStateManager.h"
+
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace places {
+
+////////////////////////////////////////////////////////////////////////////////
+//// Anonymous Helpers
+
+namespace {
+
+class VisitedQuery : public mozIStorageStatementCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ static nsresult Start(nsIURI* aURI)
+ {
+ NS_ASSERTION(aURI, "Don't pass a null URI!");
+
+ nsNavHistory* navHist = nsNavHistory::GetHistoryService();
+ NS_ENSURE_TRUE(navHist, NS_ERROR_FAILURE);
+ mozIStorageStatement* stmt = navHist->DBGetIsVisited();
+ NS_ENSURE_STATE(stmt);
+
+ // Be sure to reset our statement!
+ mozStorageStatementScoper scoper(stmt);
+ nsCString spec;
+ nsresult rv = aURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = stmt->BindUTF8StringParameter(0, spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsRefPtr<VisitedQuery> callback = new VisitedQuery(aURI);
+ NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
+
+ nsCOMPtr<mozIStoragePendingStatement> handle;
+ return stmt->ExecuteAsync(callback, getter_AddRefs(handle));
+ }
+
+ NS_IMETHOD HandleResult(mozIStorageResultSet* aResults)
+ {
+ // If this method is called, we've gotten results, which means we have a
+ // visit.
+ mIsVisited = true;
+ return NS_OK;
+ }
+
+ NS_IMETHOD HandleError(mozIStorageError* aError)
+ {
+ // mIsVisited is already set to false, and that's the assumption we will
+ // make if an error occurred.
+ return NS_OK;
+ }
+
+ NS_IMETHOD HandleCompletion(PRUint16 aReason)
+ {
+ if (mIsVisited) {
+ History::GetService()->NotifyVisited(mURI);
+ }
+ return NS_OK;
+ }
+private:
+ VisitedQuery(nsIURI* aURI)
+ : mURI(aURI)
+ , mIsVisited(false)
+ {
+ }
+
+ nsCOMPtr<nsIURI> mURI;
+ bool mIsVisited;
+};
+NS_IMPL_ISUPPORTS1(
+ VisitedQuery,
+ mozIStorageStatementCallback
+)
+
+} // anonymous namespace
+
+////////////////////////////////////////////////////////////////////////////////
+//// History
+
+History* History::gService = NULL;
+
+History::History()
+{
+ NS_ASSERTION(!gService, "Ruh-roh! This service has already been created!");
+ gService = this;
+}
+
+History::~History()
+{
+ gService = NULL;
+}
+
+void
+History::NotifyVisited(nsIURI* aURI)
+{
+ NS_ASSERTION(aURI, "Ruh-roh! A NULL URI was passed to us!");
+
+ // If the hash table has not been initialized, then we have nothing to notify
+ // about.
+ if (!mObservers.IsInitialized()) {
+ return;
+ }
+
+ // Additionally, if we have no observers for this URI, we have nothing to
+ // notify about.
+ KeyClass* key = mObservers.GetEntry(aURI);
+ if (!key) {
+ return;
+ }
+
+ // Walk through the array, and update each Link node.
+ const ObserverArray& observers = key->array;
+ ObserverArray::index_type len = observers.Length();
+ for (ObserverArray::index_type i = 0; i < len; i++) {
+ Link* link = observers[i];
+ link->SetLinkState(eLinkState_Visited);
+ NS_ASSERTION(len == observers.Length(),
+ "Calling SetLinkState added or removed an observer!");
+ }
+
+ // All the registered nodes can now be removed for this URI.
+ mObservers.RemoveEntry(aURI);
+}
+
+/* static */
+History*
+History::GetService()
+{
+ if (gService) {
+ return gService;
+ }
+
+ nsCOMPtr<IHistory> service(do_GetService(NS_IHISTORY_CONTRACTID));
+ NS_ABORT_IF_FALSE(service, "Cannot obtain IHistory service!");
+ NS_ASSERTION(gService, "Our constructor was not run?!");
+
+ return gService;
+}
+
+/* static */
+History*
+History::GetSingleton()
+{
+ if (!gService) {
+ gService = new History();
+ NS_ENSURE_TRUE(gService, nsnull);
+ }
+
+ NS_ADDREF(gService);
+ return gService;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// IHistory
+
+NS_IMETHODIMP
+History::RegisterVisitedCallback(nsIURI* aURI,
+ Link* aLink)
+{
+ NS_ASSERTION(aURI, "Must pass a non-null URI!");
+ NS_ASSERTION(aLink, "Must pass a non-null Link object!");
+
+ // First, ensure that our hash table is setup.
+ if (!mObservers.IsInitialized()) {
+ NS_ENSURE_TRUE(mObservers.Init(), NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ // Obtain our array of observers for this URI.
+ KeyClass* key = mObservers.PutEntry(aURI);
+ NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
+ ObserverArray& observers = key->array;
+
+ if (observers.IsEmpty()) {
+ // We are the first Link node to ask about this URI, or there are no pending
+ // Links wanting to know about this URI. Therefore, we should query the
+ // database now.
+ nsresult rv = VisitedQuery::Start(aURI);
+ if (NS_FAILED(rv)) {
+ // Curses - unregister and return failure.
+ (void)UnregisterVisitedCallback(aURI, aLink);
+ return rv;
+ }
+ }
+
+ // Sanity check that Links are not registered more than once for a given URI.
+ // This will not catch a case where it is registered for two different URIs.
+ NS_ASSERTION(!observers.Contains(aLink),
+ "Already tracking this Link object!");
+
+ // Start tracking our Link.
+ if (!observers.AppendElement(aLink)) {
+ // Curses - unregister and return failure.
+ (void)UnregisterVisitedCallback(aURI, aLink);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+History::UnregisterVisitedCallback(nsIURI* aURI,
+ Link* aLink)
+{
+ NS_ASSERTION(aURI, "Must pass a non-null URI!");
+ NS_ASSERTION(aLink, "Must pass a non-null Link object!");
+
+ // Get the array, and remove the item from it.
+ KeyClass* key = mObservers.GetEntry(aURI);
+ if (!key) {
+ NS_ERROR("Trying to unregister for a URI that wasn't registered!");
+ return NS_ERROR_UNEXPECTED;
+ }
+ ObserverArray& observers = key->array;
+ if (!observers.RemoveElement(aLink)) {
+ NS_ERROR("Trying to unregister a node that wasn't registered!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // If the array is now empty, we should remove it from the hashtable.
+ if (observers.IsEmpty()) {
+ mObservers.RemoveEntry(aURI);
+ }
+
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsISupports
+
+NS_IMPL_ISUPPORTS1(
+ History,
+ IHistory
+)
+
+} // namespace places
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/src/History.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Places code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_places_History_h_
+#define mozilla_places_History_h_
+
+#include "mozilla/IHistory.h"
+#include "mozilla/dom/Link.h"
+#include "nsTHashtable.h"
+#include "nsString.h"
+#include "nsURIHashKey.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace places {
+
+#define NS_HISTORYSERVICE_CID \
+ {0x9fc91e65, 0x1475, 0x4353, {0x9b, 0x9a, 0x93, 0xd7, 0x6f, 0x5b, 0xd9, 0xb7}}
+
+class History : public IHistory
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_IHISTORY
+
+ History();
+
+ /**
+ * Notifies about the visited status of a given URI.
+ *
+ * @param aURI
+ * The URI to notify about.
+ */
+ void NotifyVisited(nsIURI *aURI);
+
+ /**
+ * Obtains a pointer to this service.
+ */
+ static History *GetService();
+
+ /**
+ * Obtains a pointer that has had AddRef called on it. Used by the service
+ * manager only.
+ */
+ static History *GetSingleton();
+
+private:
+ ~History();
+
+ static History *gService;
+
+ typedef nsTArray<mozilla::dom::Link *> ObserverArray;
+
+ class KeyClass : public nsURIHashKey
+ {
+ public:
+ KeyClass(const nsIURI *aURI)
+ : nsURIHashKey(aURI)
+ {
+ }
+ KeyClass(const KeyClass &aOther)
+ : nsURIHashKey(aOther)
+ {
+ NS_NOTREACHED("Do not call me!");
+ }
+ ObserverArray array;
+ };
+
+ nsTHashtable<KeyClass> mObservers;
+};
+
+} // namespace places
+} // namespace mozilla
+
+#endif // mozilla_places_History_h_
--- a/toolkit/components/places/src/Makefile.in
+++ b/toolkit/components/places/src/Makefile.in
@@ -59,16 +59,17 @@ CPPSRCS = \
nsNavHistoryQuery.cpp \
nsNavHistoryResult.cpp \
nsNavBookmarks.cpp \
nsMaybeWeakPtr.cpp \
nsMorkHistoryImporter.cpp \
nsPlacesModule.cpp \
SQLFunctions.cpp \
Helpers.cpp \
+ History.cpp \
$(NULL)
EXTRA_DSO_LDOPTS += \
$(DEPTH)/db/morkreader/$(LIB_PREFIX)morkreader_s.$(LIB_SUFFIX) \
$(MOZ_UNICHARUTIL_LIBS) \
$(MOZ_COMPONENT_LIBS) \
$(NULL)
--- a/toolkit/components/places/src/nsNavHistory.cpp
+++ b/toolkit/components/places/src/nsNavHistory.cpp
@@ -72,16 +72,17 @@
#include "nsFaviconService.h"
#include "nsPlacesTables.h"
#include "nsPlacesIndexes.h"
#include "nsPlacesTriggers.h"
#include "nsPlacesMacros.h"
#include "SQLFunctions.h"
#include "Helpers.h"
+#include "History.h"
#ifdef MOZ_XUL
#include "nsIAutoCompleteInput.h"
#include "nsIAutoCompletePopup.h"
#endif
using namespace mozilla::places;
@@ -2795,16 +2796,19 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRT
// send it ourselves.
if (newItem && (aIsRedirect || aTransitionType == TRANSITION_DOWNLOAD)) {
nsCOMPtr<nsIObserverService> obsService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
if (obsService)
obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
}
+ // Because we implement IHistory, we always have to notify about the visit.
+ History::GetService()->NotifyVisited(aURI);
+
return NS_OK;
}
// nsNavHistory::GetNewQuery
NS_IMETHODIMP
nsNavHistory::GetNewQuery(nsINavHistoryQuery **_retval)
--- a/toolkit/components/places/src/nsNavHistory.h
+++ b/toolkit/components/places/src/nsNavHistory.h
@@ -231,16 +231,18 @@ public:
static const PRInt32 kGetInfoIndex_ItemParentId;
// select a history row by id
mozIStorageStatement *DBGetIdPageInfo() { return mDBGetIdPageInfo; }
mozIStorageStatement *DBGetTags() { return mDBGetTags; }
PRInt64 GetTagsFolder();
+ mozIStorageStatement *DBGetIsVisited() { return mDBIsPageVisited; }
+
// Constants for the columns returned by the above statement
// (in addition to the ones above).
static const PRInt32 kGetInfoIndex_VisitDate;
static const PRInt32 kGetInfoIndex_FaviconURL;
// used in execute queries to get session ID info (only for visits)
static const PRInt32 kGetInfoIndex_SessionId;
--- a/toolkit/components/places/src/nsPlacesModule.cpp
+++ b/toolkit/components/places/src/nsPlacesModule.cpp
@@ -1,18 +1,21 @@
#include "nsIGenericFactory.h"
#include "nsIClassInfoImpl.h"
#include "nsAnnoProtocolHandler.h"
#include "nsAnnotationService.h"
#include "nsNavHistory.h"
#include "nsNavBookmarks.h"
#include "nsFaviconService.h"
+#include "History.h"
#include "nsDocShellCID.h"
+using namespace mozilla::places;
+
#define NS_NAVHISTORY_CLASSINFO \
nsnull, nsnull, nsnull, \
NS_CI_INTERFACE_GETTER_NAME(nsNavHistory), \
nsnull, \
&NS_CLASSINFO_NAME(nsNavHistory), \
nsIClassInfo::SINGLETON
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNavHistory,
@@ -20,16 +23,17 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
NS_DECL_CLASSINFO(nsNavHistory)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsAnnotationService,
nsAnnotationService::GetSingleton)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNavBookmarks,
nsNavBookmarks::GetSingleton)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsFaviconService,
nsFaviconService::GetSingleton)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(History, History::GetSingleton)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAnnoProtocolHandler)
static const nsModuleComponentInfo components[] =
{
{ "Browser Navigation History",
NS_NAVHISTORYSERVICE_CID,
NS_NAVHISTORYSERVICE_CONTRACTID,
@@ -69,11 +73,16 @@ static const nsModuleComponentInfo compo
nsFaviconServiceConstructor },
{ "Browser History Charset Resolver",
NS_NAVHISTORYSERVICE_CID,
"@mozilla.org/embeddor.implemented/bookmark-charset-resolver;1",
nsNavHistoryConstructor,
NS_NAVHISTORY_CLASSINFO },
+ { "Browser History",
+ NS_HISTORYSERVICE_CID,
+ NS_IHISTORY_CONTRACTID,
+ HistoryConstructor },
+
};
NS_IMPL_NSGETMODULE(nsPlacesModule, components)
--- a/toolkit/components/places/tests/Makefile.in
+++ b/toolkit/components/places/tests/Makefile.in
@@ -63,12 +63,16 @@ MOCHI_TESTS = \
DIRS = \
chrome \
mochitest/bug_411966 \
mochitest/bug_461710 \
browser \
$(NULL)
+TOOL_DIRS = \
+ cpp \
+ $(NULL)
+
include $(topsrcdir)/config/rules.mk
libs:: $(MOCHI_TESTS)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/cpp/Makefile.in
@@ -0,0 +1,53 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Places cpp unit test code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Shawn Wilsher <me@shawnwilsher.com> (Original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+relativesrcdir = toolkit/components/places/tests/cpp
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = test_places
+
+CPP_UNIT_TESTS = \
+ test_IHistory.cpp \
+ $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/cpp/mock_Link.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is places test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This is a mock Link object which can be used in tests.
+ */
+
+#ifndef mock_Link_h__
+#define mock_Link_h__
+
+#include "mozilla/dom/Link.h"
+
+class mock_Link : public mozilla::dom::Link
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ mock_Link(void (*aHandlerFunction)(nsLinkState),
+ bool aRunNextTest = true)
+ : mozilla::dom::Link()
+ , mHandler(aHandlerFunction)
+ , mRunNextTest(aRunNextTest)
+ {
+ }
+
+ virtual void SetLinkState(nsLinkState aState)
+ {
+ // Notify our callback function.
+ mHandler(aState);
+
+ // Run the next test if we are supposed to.
+ if (mRunNextTest) {
+ run_next_test();
+ }
+
+ // Finally, we must manually release ourselves.
+ NS_RELEASE_THIS();
+ }
+
+private:
+ void (*mHandler)(nsLinkState);
+ bool mRunNextTest;
+};
+
+NS_IMPL_ISUPPORTS1(
+ mock_Link,
+ mozilla::dom::Link
+)
+
+////////////////////////////////////////////////////////////////////////////////
+//// Needed Link Methods
+
+namespace mozilla {
+namespace dom {
+
+Link::Link()
+: mLinkState(mozilla::dom::Link::defaultState)
+{
+}
+
+nsLinkState
+Link::GetLinkState() const
+{
+ NS_NOTREACHED("Unexpected call to Link::GetLinkState");
+ return eLinkState_Unknown; // suppress compiler warning
+}
+
+void
+Link::SetLinkState(nsLinkState aState)
+{
+ NS_NOTREACHED("Unexpected call to Link::SetLinkState");
+}
+
+void
+Link::ResetLinkState()
+{
+ NS_NOTREACHED("Unexpected call to Link::ResetLinkState");
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mock_Link_h__
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/cpp/places_test_harness.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is places test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "TestHarness.h"
+#include "nsMemory.h"
+#include "nsThreadUtils.h"
+#include "nsNetUtil.h"
+#include "nsDocShellCID.h"
+
+#include "nsToolkitCompsCID.h"
+#include "nsINavHistoryService.h"
+#include "nsIObserverService.h"
+#include "mozilla/IHistory.h"
+
+using namespace mozilla;
+
+static size_t gTotalTests = 0;
+static size_t gPassedTests = 0;
+
+#define do_check_true(aCondition) \
+ PR_BEGIN_MACRO \
+ gTotalTests++; \
+ if (aCondition) { \
+ gPassedTests++; \
+ } else { \
+ fail("Expected true, got false at %s:%d!", __FILE__, __LINE__); \
+ } \
+ PR_END_MACRO
+
+#define do_check_false(aCondition) \
+ PR_BEGIN_MACRO \
+ gTotalTests++; \
+ if (!aCondition) { \
+ gPassedTests++; \
+ } else { \
+ fail("Expected false, got true at %s:%d!", __FILE__, __LINE__); \
+ } \
+ PR_END_MACRO
+
+#define do_check_success(aResult) \
+ do_check_true(NS_SUCCEEDED(aResult))
+
+struct Test
+{
+ void (*func)(void);
+ const char* const name;
+};
+#define TEST(aName) \
+ {aName, #aName}
+
+/**
+ * Runs the next text.
+ */
+void run_next_test();
+
+/**
+ * To be used around asynchronous work.
+ */
+void do_test_pending();
+void do_test_finished();
+
+/**
+ * Adds a URI to the database.
+ *
+ * @param aURI
+ * The URI to add to the database.
+ */
+void
+addURI(nsIURI* aURI)
+{
+ nsCOMPtr<nsINavHistoryService> hist =
+ do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
+
+ PRInt64 id;
+ nsresult rv = hist->AddVisit(aURI, PR_Now(), nsnull,
+ nsINavHistoryService::TRANSITION_LINK, PR_FALSE,
+ 0, &id);
+ do_check_success(rv);
+}
+
+already_AddRefed<IHistory>
+do_get_IHistory()
+{
+ nsCOMPtr<IHistory> history = do_GetService(NS_IHISTORY_CONTRACTID);
+ do_check_true(history);
+ return history.forget();
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/cpp/places_test_harness_tail.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is places test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef TEST_NAME
+#error "Must #define TEST_NAME before including places_test_harness_tail.h"
+#endif
+
+#ifndef TEST_FILE
+#error "Must #define TEST_FILE before include places_test_harness_tail.h"
+#endif
+
+size_t gTestsIndex = 0;
+
+#ifdef XP_WIN
+#define SIZE_T "%Iu"
+#else
+#define SIZE_T "%zu"
+#endif
+
+#define TEST_INFO_STR "TEST-INFO | (%s) | "
+
+class RunNextTest : public nsRunnable
+{
+public:
+ NS_IMETHOD Run()
+ {
+ NS_ASSERTION(NS_IsMainThread(), "Not running on the main thread?");
+ if (gTestsIndex < NS_ARRAY_LENGTH(gTests)) {
+ do_test_pending();
+ Test &test = gTests[gTestsIndex++];
+ (void)fprintf(stderr, TEST_INFO_STR "Running %s.\n", TEST_FILE,
+ test.name);
+ test.func();
+ }
+
+ do_test_finished();
+ return NS_OK;
+ }
+};
+
+void
+run_next_test()
+{
+ nsCOMPtr<nsIRunnable> event = new RunNextTest();
+ do_check_success(NS_DispatchToCurrentThread(event));
+}
+
+size_t gPendingTests = 0;
+
+void
+do_test_pending()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Not running on the main thread?");
+ gPendingTests++;
+}
+
+void
+do_test_finished()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Not running on the main thread?");
+ NS_ASSERTION(gPendingTests > 0, "Invalid pending test count!");
+ gPendingTests--;
+}
+
+int
+main(int aArgc,
+ char** aArgv)
+{
+ ScopedXPCOM xpcom(TEST_NAME);
+
+ do_test_pending();
+ run_next_test();
+
+ // Spin the event loop until we've run out of tests to run.
+ while (gPendingTests) {
+ (void)NS_ProcessNextEvent();
+ }
+
+ // And let any other events finish before we quit.
+ (void)NS_ProcessPendingEvents(nsnull);
+
+ // Check that we have passed all of our tests, and output accordingly.
+ if (gPassedTests == gTotalTests) {
+ passed(TEST_FILE);
+ }
+
+ (void)fprintf(stderr, TEST_INFO_STR SIZE_T " of " SIZE_T " tests passed\n",
+ TEST_FILE, gPassedTests, gTotalTests);
+
+ return gPassedTests == gTotalTests ? 0 : -1;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/cpp/test_IHistory.cpp
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is places test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "places_test_harness.h"
+
+#include "mock_Link.h"
+using namespace mozilla::dom;
+
+/**
+ * This file tests the IHistory interface.
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//// Helper Methods
+
+void
+expect_visit(nsLinkState aState)
+{
+ do_check_true(aState == eLinkState_Visited);
+}
+
+void
+expect_no_visit(nsLinkState aState)
+{
+ do_check_true(aState == eLinkState_Unvisited);
+}
+
+already_AddRefed<nsIURI>
+new_test_uri()
+{
+ // Create a unique spec.
+ static PRInt32 specNumber = 0;
+ nsCAutoString spec = NS_LITERAL_CSTRING("http://mozilla.org/");
+ spec.AppendInt(specNumber++);
+
+ // Create the URI for the spec.
+ nsCOMPtr<nsIURI> testURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(testURI), spec);
+ do_check_success(rv);
+ return testURI.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Test Functions
+
+// These variables are shared between part 1 and part 2 of the test. Part 2
+// sets the nsCOMPtr's to nsnull, freeing the reference.
+namespace test_unvisited_does_not_notify {
+ nsCOMPtr<nsIURI> testURI;
+ nsCOMPtr<Link> link;
+}
+void
+test_unvisted_does_not_notify_part1()
+{
+ using namespace test_unvisited_does_not_notify;
+
+ // This test is done in two parts. The first part registers for a URI that
+ // should not be visited. We then run another test that will also do a
+ // lookup and will be notified. Since requests are answered in the order they
+ // are requested (at least as long as the same URI isn't asked for later), we
+ // will know that the Link was not notified.
+
+ // First, we need a test URI.
+ testURI = new_test_uri();
+
+ // Create our test Link.
+ link = new mock_Link(expect_no_visit);
+
+ // Now, register our Link to be notified.
+ nsCOMPtr<IHistory> history(do_get_IHistory());
+ nsresult rv = history->RegisterVisitedCallback(testURI, link);
+ do_check_success(rv);
+
+ // Run the next test.
+ run_next_test();
+}
+
+void
+test_visited_notifies()
+{
+ // First, we add our test URI to history.
+ nsCOMPtr<nsIURI> testURI(new_test_uri());
+ addURI(testURI);
+
+ // Create our test Link. The callback function will release the reference we
+ // have on the Link.
+ Link* link = new mock_Link(expect_visit);
+ NS_ADDREF(link);
+
+ // Now, register our Link to be notified.
+ nsCOMPtr<IHistory> history(do_get_IHistory());
+ nsresult rv = history->RegisterVisitedCallback(testURI, link);
+ do_check_success(rv);
+ // Note: test will continue upon notification.
+}
+
+void
+test_unvisted_does_not_notify_part2()
+{
+ using namespace test_unvisited_does_not_notify;
+
+ // We would have had a failure at this point had the content node been told it
+ // was visited. Therefore, it is safe to unregister our content node.
+ nsCOMPtr<IHistory> history(do_get_IHistory());
+ nsresult rv = history->UnregisterVisitedCallback(testURI, link);
+ do_check_success(rv);
+
+ // Clear the stored variables now.
+ testURI = nsnull;
+ link = nsnull;
+
+ // Run the next test.
+ run_next_test();
+}
+
+void
+test_same_uri_notifies_both()
+{
+ // First, we add our test URI to history.
+ nsCOMPtr<nsIURI> testURI(new_test_uri());
+ addURI(testURI);
+
+ // Create our two test Links. The callback function will release the
+ // reference we have on the Links. Only the second Link should run the next
+ // test!
+ Link* link1 = new mock_Link(expect_visit, false);
+ NS_ADDREF(link1);
+ Link* link2 = new mock_Link(expect_visit);
+ NS_ADDREF(link2);
+
+ // Now, register our Link to be notified.
+ nsCOMPtr<IHistory> history(do_get_IHistory());
+ nsresult rv = history->RegisterVisitedCallback(testURI, link1);
+ do_check_success(rv);
+ rv = history->RegisterVisitedCallback(testURI, link2);
+ do_check_success(rv);
+
+ // Note: test will continue upon notification.
+}
+
+void
+test_unregistered_visited_does_not_notify()
+{
+ // This test must have a test that has a successful notification after it.
+ // The Link would have been notified by now if we were buggy and notified
+ // unregistered Links (due to request serialization).
+
+ nsCOMPtr<nsIURI> testURI(new_test_uri());
+ nsCOMPtr<Link> link(new mock_Link(expect_no_visit));
+
+ // Now, register our Link to be notified.
+ nsCOMPtr<IHistory> history(do_get_IHistory());
+ nsresult rv = history->RegisterVisitedCallback(testURI, link);
+ do_check_success(rv);
+
+ // Unregister the Link.
+ rv = history->UnregisterVisitedCallback(testURI, link);
+ do_check_success(rv);
+
+ // And finally add a visit for the URI.
+ addURI(testURI);
+
+ // If history tries to notify us, we'll either crash because the Link will
+ // have been deleted (we are the only thing holding a reference to it), or our
+ // expect_no_visit call back will produce a failure. Either way, the test
+ // will be reported as a failure.
+
+ // Run the next test.
+ run_next_test();
+}
+
+void
+test_new_visit_notifies_waiting_Link()
+{
+ // Create our test Link. The callback function will release the reference we
+ // have on the link.
+ Link* link = new mock_Link(expect_visit);
+ NS_ADDREF(link);
+
+ // Now, register our content node to be notified.
+ nsCOMPtr<nsIURI> testURI(new_test_uri());
+ nsCOMPtr<IHistory> history(do_get_IHistory());
+ nsresult rv = history->RegisterVisitedCallback(testURI, link);
+ do_check_success(rv);
+
+ // Add ourselves to history.
+ addURI(testURI);
+
+ // Note: test will continue upon notification.
+}
+
+void
+test_RegisterVisitedCallback_returns_before_notifying()
+{
+ // Add a URI so that it's already in history.
+ nsCOMPtr<nsIURI> testURI(new_test_uri());
+ addURI(testURI);
+
+ // Create our test Link.
+ nsCOMPtr<Link> link(new mock_Link(expect_no_visit));
+
+ // Now, register our content node to be notified. It should not be notified.
+ nsCOMPtr<IHistory> history(do_get_IHistory());
+ nsresult rv = history->RegisterVisitedCallback(testURI, link);
+ do_check_success(rv);
+
+ // Remove ourselves as an observer. We would have failed if we had been
+ // notified.
+ rv = history->UnregisterVisitedCallback(testURI, link);
+ do_check_success(rv);
+
+ run_next_test();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Test Harness
+
+/**
+ * Note: for tests marked "Order Important!", please see the test for details.
+ */
+Test gTests[] = {
+ TEST(test_unvisted_does_not_notify_part1), // Order Important!
+ TEST(test_visited_notifies),
+ TEST(test_unvisted_does_not_notify_part2), // Order Important!
+ TEST(test_same_uri_notifies_both),
+ TEST(test_unregistered_visited_does_not_notify), // Order Important!
+ TEST(test_new_visit_notifies_waiting_Link),
+ TEST(test_RegisterVisitedCallback_returns_before_notifying),
+};
+
+const char* file = __FILE__;
+#define TEST_NAME "IHistory"
+#define TEST_FILE file
+#include "places_test_harness_tail.h"