Bug 461199 (Part 2) - Create an API for asynchronous isVisited checks that content/layout can use

Create a new API (IHistory) to check for the visitedness of a URI.
r=mak
r=bz
sr=bsmedberg
This commit is contained in:
Shawn Wilsher 2009-08-20 11:56:10 -07:00
parent 467d507067
commit 94acf62a02
16 changed files with 1237 additions and 1 deletions

View File

@ -64,6 +64,12 @@ EXPORTS = \
mozAutoDocUpdate.h \
$(NULL)
EXPORTS_NAMESPACES = mozilla/dom
EXPORTS_mozilla/dom = \
Link.h \
$(NULL)
CPPSRCS = \
mozSanitizingSerializer.cpp \
nsAtomListUtils.cpp \

111
docshell/base/IHistory.h Normal file
View File

@ -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_

View File

@ -82,6 +82,12 @@ XPIDLSRCS = \
EXPORTS = nsDocShellLoadTypes.h
EXPORTS_NAMESPACES = mozilla
EXPORTS_mozilla = \
IHistory.h \
$(NULL)
CPPSRCS = \
nsDocShell.cpp \
nsDocShellLoadInfo.cpp \

View File

@ -86,7 +86,11 @@
{ 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

View File

@ -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

View File

@ -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_

View File

@ -64,6 +64,7 @@ CPPSRCS = \
nsPlacesModule.cpp \
SQLFunctions.cpp \
Helpers.cpp \
History.cpp \
$(NULL)
EXTRA_DSO_LDOPTS += \

View File

@ -77,6 +77,7 @@
#include "nsPlacesMacros.h"
#include "SQLFunctions.h"
#include "Helpers.h"
#include "History.h"
#ifdef MOZ_XUL
#include "nsIAutoCompleteInput.h"
@ -2800,6 +2801,9 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRTime aTime, nsIURI* aReferringURI,
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;
}

View File

@ -236,6 +236,8 @@ public:
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;

View File

@ -6,8 +6,11 @@
#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), \
@ -25,6 +28,7 @@ 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)
@ -74,6 +78,11 @@ static const nsModuleComponentInfo components[] =
nsNavHistoryConstructor,
NS_NAVHISTORY_CLASSINFO },
{ "Browser History",
NS_HISTORYSERVICE_CID,
NS_IHISTORY_CONTRACTID,
HistoryConstructor },
};
NS_IMPL_NSGETMODULE(nsPlacesModule, components)

View File

@ -68,6 +68,10 @@ DIRS = \
browser \
$(NULL)
TOOL_DIRS = \
cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk
libs:: $(MOCHI_TESTS)

View File

@ -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

View File

@ -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__

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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"