import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,229 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
* 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/. */
/**
* This is a mock Link object which can be used in tests.
*/
#ifndef mock_Link_h__
#define mock_Link_h__
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/Link.h"
#include "mozilla/dom/URLSearchParams.h"
class mock_Link : public mozilla::dom::Link
{
public:
NS_DECL_ISUPPORTS
explicit mock_Link(void (*aHandlerFunction)(nsLinkState),
bool aRunNextTest = true)
: mozilla::dom::Link(nullptr)
, mHandler(aHandlerFunction)
, mRunNextTest(aRunNextTest)
{
// Create a cyclic ownership, so that the link will be released only
// after its status has been updated. This will ensure that, when it should
// run the next test, it will happen at the end of the test function, if
// the link status has already been set before. Indeed the link status is
// updated on a separate connection, thus may happen at any time.
mDeathGrip = this;
}
virtual void SetLinkState(nsLinkState aState) override
{
// Notify our callback function.
mHandler(aState);
// Break the cycle so the object can be destroyed.
mDeathGrip = nullptr;
}
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override
{
return 0; // the value shouldn't matter
}
protected:
~mock_Link() {
// Run the next test if we are supposed to.
if (mRunNextTest) {
run_next_test();
}
}
private:
void (*mHandler)(nsLinkState);
bool mRunNextTest;
RefPtr<Link> mDeathGrip;
};
NS_IMPL_ISUPPORTS(
mock_Link,
mozilla::dom::Link
)
////////////////////////////////////////////////////////////////////////////////
//// Needed Link Methods
namespace mozilla {
namespace dom {
Link::Link(Element* aElement)
: mElement(aElement)
, mLinkState(eLinkState_NotLink)
, mRegistered(false)
{
}
Link::~Link()
{
}
bool
Link::ElementHasHref() const
{
NS_NOTREACHED("Unexpected call to Link::ElementHasHref");
return false; // suppress compiler warning
}
void
Link::SetLinkState(nsLinkState aState)
{
NS_NOTREACHED("Unexpected call to Link::SetLinkState");
}
void
Link::ResetLinkState(bool aNotify, bool aHasHref)
{
NS_NOTREACHED("Unexpected call to Link::ResetLinkState");
}
nsIURI*
Link::GetURI() const
{
NS_NOTREACHED("Unexpected call to Link::GetURI");
return nullptr; // suppress compiler warning
}
size_t
Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
NS_NOTREACHED("Unexpected call to Link::SizeOfExcludingThis");
return 0;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(URLSearchParams)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URLSearchParams)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(URLSearchParams)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(URLSearchParams)
NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
URLSearchParams::URLSearchParams(nsISupports* aParent,
URLSearchParamsObserver* aObserver)
{
}
URLSearchParams::~URLSearchParams()
{
}
JSObject*
URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return nullptr;
}
void
URLSearchParams::ParseInput(const nsACString& aInput)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::ParseInput");
}
void
URLSearchParams::Serialize(nsAString& aValue) const
{
NS_NOTREACHED("Unexpected call to URLSearchParams::Serialize");
}
void
URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::Get");
}
void
URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString >& aRetval)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::GetAll");
}
void
URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::Set");
}
void
URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::Append");
}
void
URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::AppendInternal");
}
bool
URLSearchParams::Has(const nsAString& aName)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::Has");
return false;
}
void
URLSearchParams::Delete(const nsAString& aName)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::Delete");
}
void
URLSearchParams::DeleteAll()
{
NS_NOTREACHED("Unexpected call to URLSearchParams::DeleteAll");
}
void
URLSearchParams::NotifyObserver()
{
NS_NOTREACHED("Unexpected call to URLSearchParams::NotifyObserver");
}
NS_IMETHODIMP
URLSearchParams::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
nsACString& aContentType, nsACString& aCharset)
{
NS_NOTREACHED("Unexpected call to URLSearchParams::GetSendInfo");
return NS_OK;
}
} // namespace dom
} // namespace mozilla
#endif // mock_Link_h__

View file

@ -0,0 +1,14 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
GeckoCppUnitTests([
'test_IHistory',
])
if CONFIG['JS_SHARED_LIBRARY']:
USE_LIBS += [
'js',
]

View file

@ -0,0 +1,413 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
* 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 "TestHarness.h"
#include "nsMemory.h"
#include "nsThreadUtils.h"
#include "nsDocShellCID.h"
#include "nsToolkitCompsCID.h"
#include "nsINavHistoryService.h"
#include "nsIObserverService.h"
#include "nsIURI.h"
#include "mozilla/IHistory.h"
#include "mozIStorageConnection.h"
#include "mozIStorageStatement.h"
#include "mozIStorageAsyncStatement.h"
#include "mozIStorageStatementCallback.h"
#include "mozIStoragePendingStatement.h"
#include "nsPIPlacesDatabase.h"
#include "nsIObserver.h"
#include "prinrval.h"
#include "prtime.h"
#include "mozilla/Attributes.h"
#define WAITFORTOPIC_TIMEOUT_SECONDS 5
static size_t gTotalTests = 0;
static size_t gPassedTests = 0;
#define do_check_true(aCondition) \
PR_BEGIN_MACRO \
gTotalTests++; \
if (aCondition) { \
gPassedTests++; \
} else { \
fail("%s | Expected true, got false at line %d", __FILE__, __LINE__); \
} \
PR_END_MACRO
#define do_check_false(aCondition) \
PR_BEGIN_MACRO \
gTotalTests++; \
if (!aCondition) { \
gPassedTests++; \
} else { \
fail("%s | Expected false, got true at line %d", __FILE__, __LINE__); \
} \
PR_END_MACRO
#define do_check_success(aResult) \
do_check_true(NS_SUCCEEDED(aResult))
#ifdef LINUX
// XXX Linux opt builds on tinderbox are orange due to linking with stdlib.
// This is sad and annoying, but it's a workaround that works.
#define do_check_eq(aExpected, aActual) \
do_check_true(aExpected == aActual)
#else
#include <sstream>
#define do_check_eq(aActual, aExpected) \
PR_BEGIN_MACRO \
gTotalTests++; \
if (aExpected == aActual) { \
gPassedTests++; \
} else { \
std::ostringstream temp; \
temp << __FILE__ << " | Expected '" << aExpected << "', got '"; \
temp << aActual <<"' at line " << __LINE__; \
fail(temp.str().c_str()); \
} \
PR_END_MACRO
#endif
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();
/**
* Spins current thread until a topic is received.
*/
class WaitForTopicSpinner final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
explicit WaitForTopicSpinner(const char* const aTopic)
: mTopicReceived(false)
, mStartTime(PR_IntervalNow())
{
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
do_check_true(observerService);
(void)observerService->AddObserver(this, aTopic, false);
}
void Spin() {
while (!mTopicReceived) {
if ((PR_IntervalNow() - mStartTime) > (WAITFORTOPIC_TIMEOUT_SECONDS * PR_USEC_PER_SEC)) {
// Timed out waiting for the topic.
do_check_true(false);
break;
}
(void)NS_ProcessNextEvent();
}
}
NS_IMETHOD Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData) override
{
mTopicReceived = true;
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
do_check_true(observerService);
(void)observerService->RemoveObserver(this, aTopic);
return NS_OK;
}
private:
~WaitForTopicSpinner() {}
bool mTopicReceived;
PRIntervalTime mStartTime;
};
NS_IMPL_ISUPPORTS(
WaitForTopicSpinner,
nsIObserver
)
/**
* Spins current thread until an async statement is executed.
*/
class AsyncStatementSpinner final : public mozIStorageStatementCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_MOZISTORAGESTATEMENTCALLBACK
AsyncStatementSpinner();
void SpinUntilCompleted();
uint16_t completionReason;
protected:
~AsyncStatementSpinner() {}
volatile bool mCompleted;
};
NS_IMPL_ISUPPORTS(AsyncStatementSpinner,
mozIStorageStatementCallback)
AsyncStatementSpinner::AsyncStatementSpinner()
: completionReason(0)
, mCompleted(false)
{
}
NS_IMETHODIMP
AsyncStatementSpinner::HandleResult(mozIStorageResultSet *aResultSet)
{
return NS_OK;
}
NS_IMETHODIMP
AsyncStatementSpinner::HandleError(mozIStorageError *aError)
{
return NS_OK;
}
NS_IMETHODIMP
AsyncStatementSpinner::HandleCompletion(uint16_t aReason)
{
completionReason = aReason;
mCompleted = true;
return NS_OK;
}
void AsyncStatementSpinner::SpinUntilCompleted()
{
nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
nsresult rv = NS_OK;
bool processed = true;
while (!mCompleted && NS_SUCCEEDED(rv)) {
rv = thread->ProcessNextEvent(true, &processed);
}
}
struct PlaceRecord
{
int64_t id;
int32_t hidden;
int32_t typed;
int32_t visitCount;
nsCString guid;
};
struct VisitRecord
{
int64_t id;
int64_t lastVisitId;
int32_t transitionType;
};
already_AddRefed<mozilla::IHistory>
do_get_IHistory()
{
nsCOMPtr<mozilla::IHistory> history = do_GetService(NS_IHISTORY_CONTRACTID);
do_check_true(history);
return history.forget();
}
already_AddRefed<nsINavHistoryService>
do_get_NavHistory()
{
nsCOMPtr<nsINavHistoryService> serv =
do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
do_check_true(serv);
return serv.forget();
}
already_AddRefed<mozIStorageConnection>
do_get_db()
{
nsCOMPtr<nsINavHistoryService> history = do_get_NavHistory();
nsCOMPtr<nsPIPlacesDatabase> database = do_QueryInterface(history);
do_check_true(database);
nsCOMPtr<mozIStorageConnection> dbConn;
nsresult rv = database->GetDBConnection(getter_AddRefs(dbConn));
do_check_success(rv);
return dbConn.forget();
}
/**
* Get the place record from the database.
*
* @param aURI The unique URI of the place we are looking up
* @param result Out parameter where the result is stored
*/
void
do_get_place(nsIURI* aURI, PlaceRecord& result)
{
nsCOMPtr<mozIStorageConnection> dbConn = do_get_db();
nsCOMPtr<mozIStorageStatement> stmt;
nsCString spec;
nsresult rv = aURI->GetSpec(spec);
do_check_success(rv);
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id, hidden, typed, visit_count, guid FROM moz_places "
"WHERE url_hash = hash(?1) AND url = ?1"
), getter_AddRefs(stmt));
do_check_success(rv);
rv = stmt->BindUTF8StringByIndex(0, spec);
do_check_success(rv);
bool hasResults;
rv = stmt->ExecuteStep(&hasResults);
do_check_success(rv);
if (!hasResults) {
result.id = 0;
return;
}
rv = stmt->GetInt64(0, &result.id);
do_check_success(rv);
rv = stmt->GetInt32(1, &result.hidden);
do_check_success(rv);
rv = stmt->GetInt32(2, &result.typed);
do_check_success(rv);
rv = stmt->GetInt32(3, &result.visitCount);
do_check_success(rv);
rv = stmt->GetUTF8String(4, result.guid);
do_check_success(rv);
}
/**
* Gets the most recent visit to a place.
*
* @param placeID ID from the moz_places table
* @param result Out parameter where visit is stored
*/
void
do_get_lastVisit(int64_t placeId, VisitRecord& result)
{
nsCOMPtr<mozIStorageConnection> dbConn = do_get_db();
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id, from_visit, visit_type FROM moz_historyvisits "
"WHERE place_id=?1 "
"LIMIT 1"
), getter_AddRefs(stmt));
do_check_success(rv);
rv = stmt->BindInt64ByIndex(0, placeId);
do_check_success(rv);
bool hasResults;
rv = stmt->ExecuteStep(&hasResults);
do_check_success(rv);
if (!hasResults) {
result.id = 0;
return;
}
rv = stmt->GetInt64(0, &result.id);
do_check_success(rv);
rv = stmt->GetInt64(1, &result.lastVisitId);
do_check_success(rv);
rv = stmt->GetInt32(2, &result.transitionType);
do_check_success(rv);
}
void
do_wait_async_updates() {
nsCOMPtr<mozIStorageConnection> db = do_get_db();
nsCOMPtr<mozIStorageAsyncStatement> stmt;
db->CreateAsyncStatement(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"),
getter_AddRefs(stmt));
nsCOMPtr<mozIStoragePendingStatement> pending;
(void)stmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
db->CreateAsyncStatement(NS_LITERAL_CSTRING("COMMIT"),
getter_AddRefs(stmt));
RefPtr<AsyncStatementSpinner> spinner = new AsyncStatementSpinner();
(void)stmt->ExecuteAsync(spinner, getter_AddRefs(pending));
spinner->SpinUntilCompleted();
}
/**
* Adds a URI to the database.
*
* @param aURI
* The URI to add to the database.
*/
void
addURI(nsIURI* aURI)
{
nsCOMPtr<mozilla::IHistory> history = do_GetService(NS_IHISTORY_CONTRACTID);
do_check_true(history);
nsresult rv = history->VisitURI(aURI, nullptr, mozilla::IHistory::TOP_LEVEL);
do_check_success(rv);
do_wait_async_updates();
}
static const char TOPIC_PROFILE_CHANGE[] = "profile-before-change";
static const char TOPIC_PLACES_CONNECTION_CLOSED[] = "places-connection-closed";
class WaitForConnectionClosed final : public nsIObserver
{
RefPtr<WaitForTopicSpinner> mSpinner;
~WaitForConnectionClosed() {}
public:
NS_DECL_ISUPPORTS
WaitForConnectionClosed()
{
nsCOMPtr<nsIObserverService> os =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
MOZ_ASSERT(os);
if (os) {
MOZ_ALWAYS_SUCCEEDS(os->AddObserver(this, TOPIC_PROFILE_CHANGE, false));
}
mSpinner = new WaitForTopicSpinner(TOPIC_PLACES_CONNECTION_CLOSED);
}
NS_IMETHOD Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData) override
{
nsCOMPtr<nsIObserverService> os =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
MOZ_ASSERT(os);
if (os) {
MOZ_ALWAYS_SUCCEEDS(os->RemoveObserver(this, aTopic));
}
mSpinner->Spin();
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(WaitForConnectionClosed, nsIObserver)

View file

@ -0,0 +1,149 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
* 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 "nsWidgetsCID.h"
#include "nsIComponentRegistrar.h"
#ifdef MOZ_CRASHREPORTER
#include "nsICrashReporter.h"
#endif
#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
int gTestsIndex = 0;
#define TEST_INFO_STR "TEST-INFO | (%s) | "
class RunNextTest : public mozilla::Runnable
{
public:
NS_IMETHOD Run() override
{
NS_ASSERTION(NS_IsMainThread(), "Not running on the main thread?");
if (gTestsIndex < int(mozilla::ArrayLength(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));
}
int 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--;
}
void
disable_idle_service()
{
(void)fprintf(stderr, TEST_INFO_STR "Disabling Idle Service.\n", TEST_FILE);
static NS_DEFINE_IID(kIdleCID, NS_IDLE_SERVICE_CID);
nsresult rv;
nsCOMPtr<nsIFactory> idleFactory = do_GetClassObject(kIdleCID, &rv);
do_check_success(rv);
nsCOMPtr<nsIComponentRegistrar> registrar;
rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
do_check_success(rv);
rv = registrar->UnregisterFactory(kIdleCID, idleFactory);
do_check_success(rv);
}
int
main(int aArgc,
char** aArgv)
{
ScopedXPCOM xpcom(TEST_NAME);
if (xpcom.failed())
return -1;
// Initialize a profile folder to ensure a clean shutdown.
nsCOMPtr<nsIFile> profile = xpcom.GetProfileDirectory();
if (!profile) {
fail("Couldn't get the profile directory.");
return -1;
}
#ifdef MOZ_CRASHREPORTER
char* enabled = PR_GetEnv("MOZ_CRASHREPORTER");
if (enabled && !strcmp(enabled, "1")) {
// bug 787458: move this to an even-more-common location to use in all
// C++ unittests
nsCOMPtr<nsICrashReporter> crashreporter =
do_GetService("@mozilla.org/toolkit/crash-reporter;1");
if (crashreporter) {
fprintf(stderr, "Setting up crash reporting\n");
nsCOMPtr<nsIProperties> dirsvc =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
if (!dirsvc)
NS_RUNTIMEABORT("Couldn't get directory service");
nsCOMPtr<nsIFile> cwd;
nsresult rv = dirsvc->Get(NS_OS_CURRENT_WORKING_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(cwd));
if (NS_FAILED(rv))
NS_RUNTIMEABORT("Couldn't get CWD");
crashreporter->SetEnabled(true);
crashreporter->SetMinidumpPath(cwd);
}
}
#endif
RefPtr<WaitForConnectionClosed> spinClose = new WaitForConnectionClosed();
// Tinderboxes are constantly on idle. Since idle tasks can interact with
// tests, causing random failures, disable the idle service.
disable_idle_service();
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(nullptr);
// Check that we have passed all of our tests, and output accordingly.
if (gPassedTests == gTotalTests) {
passed(TEST_FILE);
}
(void)fprintf(stderr, TEST_INFO_STR "%u of %u tests passed\n",
TEST_FILE, unsigned(gPassedTests), unsigned(gTotalTests));
return gPassedTests == gTotalTests ? 0 : -1;
}

View file

@ -0,0 +1,639 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
* 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 "places_test_harness.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "mozilla/Attributes.h"
#include "nsNetUtil.h"
#include "mock_Link.h"
using namespace mozilla;
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 int32_t specNumber = 0;
nsAutoCString 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();
}
class VisitURIObserver final : public nsIObserver
{
~VisitURIObserver() {}
public:
NS_DECL_ISUPPORTS
explicit VisitURIObserver(int aExpectedVisits = 1) :
mVisits(0),
mExpectedVisits(aExpectedVisits)
{
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
do_check_true(observerService);
(void)observerService->AddObserver(this,
"uri-visit-saved",
false);
}
void WaitForNotification()
{
while (mVisits < mExpectedVisits) {
(void)NS_ProcessNextEvent();
}
}
NS_IMETHOD Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData) override
{
mVisits++;
if (mVisits == mExpectedVisits) {
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
(void)observerService->RemoveObserver(this, "uri-visit-saved");
}
return NS_OK;
}
private:
int mVisits;
int mExpectedVisits;
};
NS_IMPL_ISUPPORTS(
VisitURIObserver,
nsIObserver
)
////////////////////////////////////////////////////////////////////////////////
//// Test Functions
void
test_set_places_enabled()
{
// Ensure places is enabled for everyone.
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch =
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
do_check_success(rv);
rv = prefBranch->SetBoolPref("places.history.enabled", true);
do_check_success(rv);
// Run the next test.
run_next_test();
}
void
test_wait_checkpoint()
{
// This "fake" test is here to wait for the initial WAL checkpoint we force
// after creating the database schema, since that may happen at any time,
// and cause concurrent readers to access an older checkpoint.
nsCOMPtr<mozIStorageConnection> db = do_get_db();
nsCOMPtr<mozIStorageAsyncStatement> stmt;
db->CreateAsyncStatement(NS_LITERAL_CSTRING("SELECT 1"),
getter_AddRefs(stmt));
RefPtr<AsyncStatementSpinner> spinner = new AsyncStatementSpinner();
nsCOMPtr<mozIStoragePendingStatement> pending;
(void)stmt->ExecuteAsync(spinner, getter_AddRefs(pending));
spinner->SpinUntilCompleted();
// Run the next test.
run_next_test();
}
// These variables are shared between part 1 and part 2 of the test. Part 2
// sets the nsCOMPtr's to nullptr, freeing the reference.
namespace test_unvisited_does_not_notify {
nsCOMPtr<nsIURI> testURI;
RefPtr<Link> testLink;
} // namespace test_unvisited_does_not_notify
void
test_unvisited_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.
testLink = new mock_Link(expect_no_visit);
// Now, register our Link to be notified.
nsCOMPtr<IHistory> history = do_get_IHistory();
nsresult rv = history->RegisterVisitedCallback(testURI, testLink);
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.
RefPtr<Link> link = new mock_Link(expect_visit);
// 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_unvisited_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, testLink);
do_check_success(rv);
// Clear the stored variables now.
testURI = nullptr;
testLink = nullptr;
// 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!
RefPtr<Link> link1 = new mock_Link(expect_visit, false);
RefPtr<Link> link2 = new mock_Link(expect_visit);
// 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();
RefPtr<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.
RefPtr<Link> link = new mock_Link(expect_visit);
// 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.
RefPtr<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();
}
namespace test_observer_topic_dispatched_helpers {
#define URI_VISITED "visited"
#define URI_NOT_VISITED "not visited"
#define URI_VISITED_RESOLUTION_TOPIC "visited-status-resolution"
class statusObserver final : public nsIObserver
{
~statusObserver() {}
public:
NS_DECL_ISUPPORTS
statusObserver(nsIURI* aURI,
const bool aExpectVisit,
bool& _notified)
: mURI(aURI)
, mExpectVisit(aExpectVisit)
, mNotified(_notified)
{
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
do_check_true(observerService);
(void)observerService->AddObserver(this,
URI_VISITED_RESOLUTION_TOPIC,
false);
}
NS_IMETHOD Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData) override
{
// Make sure we got notified of the right topic.
do_check_false(strcmp(aTopic, URI_VISITED_RESOLUTION_TOPIC));
// If this isn't for our URI, do not do anything.
nsCOMPtr<nsIURI> notifiedURI = do_QueryInterface(aSubject);
do_check_true(notifiedURI);
bool isOurURI;
nsresult rv = notifiedURI->Equals(mURI, &isOurURI);
do_check_success(rv);
if (!isOurURI) {
return NS_OK;
}
// Check that we have either the visited or not visited string.
bool visited = !!NS_LITERAL_STRING(URI_VISITED).Equals(aData);
bool notVisited = !!NS_LITERAL_STRING(URI_NOT_VISITED).Equals(aData);
do_check_true(visited || notVisited);
// Check to make sure we got the state we expected.
do_check_eq(visited, mExpectVisit);
// Indicate that we've been notified.
mNotified = true;
// Remove ourselves as an observer.
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
(void)observerService->RemoveObserver(this,
URI_VISITED_RESOLUTION_TOPIC);
return NS_OK;
}
private:
nsCOMPtr<nsIURI> mURI;
const bool mExpectVisit;
bool& mNotified;
};
NS_IMPL_ISUPPORTS(
statusObserver,
nsIObserver
)
} // namespace test_observer_topic_dispatched_helpers
void
test_observer_topic_dispatched()
{
using namespace test_observer_topic_dispatched_helpers;
// Create two URIs, making sure only one is in history.
nsCOMPtr<nsIURI> visitedURI = new_test_uri();
nsCOMPtr<nsIURI> notVisitedURI = new_test_uri();
bool urisEqual;
nsresult rv = visitedURI->Equals(notVisitedURI, &urisEqual);
do_check_success(rv);
do_check_false(urisEqual);
addURI(visitedURI);
// Need two Link objects as well - one for each URI.
RefPtr<Link> visitedLink = new mock_Link(expect_visit, false);
RefPtr<Link> visitedLinkCopy = visitedLink;
RefPtr<Link> notVisitedLink = new mock_Link(expect_no_visit);
// Add the right observers for the URIs to check results.
bool visitedNotified = false;
nsCOMPtr<nsIObserver> visitedObs =
new statusObserver(visitedURI, true, visitedNotified);
bool notVisitedNotified = false;
nsCOMPtr<nsIObserver> unvisitedObs =
new statusObserver(notVisitedURI, false, notVisitedNotified);
// Register our Links to be notified.
nsCOMPtr<IHistory> history = do_get_IHistory();
rv = history->RegisterVisitedCallback(visitedURI, visitedLink);
do_check_success(rv);
rv = history->RegisterVisitedCallback(notVisitedURI, notVisitedLink);
do_check_success(rv);
// Spin the event loop as long as we have not been properly notified.
while (!visitedNotified || !notVisitedNotified) {
(void)NS_ProcessNextEvent();
}
// Unregister our observer that would not have been released.
rv = history->UnregisterVisitedCallback(notVisitedURI, notVisitedLink);
do_check_success(rv);
run_next_test();
}
void
test_visituri_inserts()
{
nsCOMPtr<IHistory> history = do_get_IHistory();
nsCOMPtr<nsIURI> lastURI = new_test_uri();
nsCOMPtr<nsIURI> visitedURI = new_test_uri();
history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
RefPtr<VisitURIObserver> finisher = new VisitURIObserver();
finisher->WaitForNotification();
PlaceRecord place;
do_get_place(visitedURI, place);
do_check_true(place.id > 0);
do_check_false(place.hidden);
do_check_false(place.typed);
do_check_eq(place.visitCount, 1);
run_next_test();
}
void
test_visituri_updates()
{
nsCOMPtr<IHistory> history = do_get_IHistory();
nsCOMPtr<nsIURI> lastURI = new_test_uri();
nsCOMPtr<nsIURI> visitedURI = new_test_uri();
RefPtr<VisitURIObserver> finisher;
history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
finisher = new VisitURIObserver();
finisher->WaitForNotification();
history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
finisher = new VisitURIObserver();
finisher->WaitForNotification();
PlaceRecord place;
do_get_place(visitedURI, place);
do_check_eq(place.visitCount, 2);
run_next_test();
}
void
test_visituri_preserves_shown_and_typed()
{
nsCOMPtr<IHistory> history = do_get_IHistory();
nsCOMPtr<nsIURI> lastURI = new_test_uri();
nsCOMPtr<nsIURI> visitedURI = new_test_uri();
history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
// this simulates the uri visit happening in a frame. Normally frame
// transitions would be hidden unless it was previously loaded top-level
history->VisitURI(visitedURI, lastURI, 0);
RefPtr<VisitURIObserver> finisher = new VisitURIObserver(2);
finisher->WaitForNotification();
PlaceRecord place;
do_get_place(visitedURI, place);
do_check_false(place.hidden);
run_next_test();
}
void
test_visituri_creates_visit()
{
nsCOMPtr<IHistory> history = do_get_IHistory();
nsCOMPtr<nsIURI> lastURI = new_test_uri();
nsCOMPtr<nsIURI> visitedURI = new_test_uri();
history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
RefPtr<VisitURIObserver> finisher = new VisitURIObserver();
finisher->WaitForNotification();
PlaceRecord place;
VisitRecord visit;
do_get_place(visitedURI, place);
do_get_lastVisit(place.id, visit);
do_check_true(visit.id > 0);
do_check_eq(visit.lastVisitId, 0);
do_check_eq(visit.transitionType, nsINavHistoryService::TRANSITION_LINK);
run_next_test();
}
void
test_visituri_transition_typed()
{
nsCOMPtr<nsINavHistoryService> navHistory = do_get_NavHistory();
nsCOMPtr<IHistory> history = do_get_IHistory();
nsCOMPtr<nsIURI> lastURI = new_test_uri();
nsCOMPtr<nsIURI> visitedURI = new_test_uri();
navHistory->MarkPageAsTyped(visitedURI);
history->VisitURI(visitedURI, lastURI, mozilla::IHistory::TOP_LEVEL);
RefPtr<VisitURIObserver> finisher = new VisitURIObserver();
finisher->WaitForNotification();
PlaceRecord place;
VisitRecord visit;
do_get_place(visitedURI, place);
do_get_lastVisit(place.id, visit);
do_check_true(visit.transitionType == nsINavHistoryService::TRANSITION_TYPED);
run_next_test();
}
void
test_visituri_transition_embed()
{
nsCOMPtr<IHistory> history = do_get_IHistory();
nsCOMPtr<nsIURI> lastURI = new_test_uri();
nsCOMPtr<nsIURI> visitedURI = new_test_uri();
history->VisitURI(visitedURI, lastURI, 0);
RefPtr<VisitURIObserver> finisher = new VisitURIObserver();
finisher->WaitForNotification();
PlaceRecord place;
VisitRecord visit;
do_get_place(visitedURI, place);
do_get_lastVisit(place.id, visit);
do_check_eq(place.id, 0);
do_check_eq(visit.id, 0);
run_next_test();
}
void
test_new_visit_adds_place_guid()
{
// First, add a visit and wait. This will also add a place.
nsCOMPtr<nsIURI> visitedURI = new_test_uri();
nsCOMPtr<IHistory> history = do_get_IHistory();
nsresult rv = history->VisitURI(visitedURI, nullptr,
mozilla::IHistory::TOP_LEVEL);
do_check_success(rv);
RefPtr<VisitURIObserver> finisher = new VisitURIObserver();
finisher->WaitForNotification();
// Check that we have a guid for our visit.
PlaceRecord place;
do_get_place(visitedURI, place);
do_check_eq(place.visitCount, 1);
do_check_eq(place.guid.Length(), 12);
run_next_test();
}
////////////////////////////////////////////////////////////////////////////////
//// IPC-only Tests
void
test_two_null_links_same_uri()
{
// Tests that we do not crash when we have had two nullptr Links passed to
// RegisterVisitedCallback and then the visit occurs (bug 607469). This only
// happens in IPC builds.
nsCOMPtr<nsIURI> testURI = new_test_uri();
nsCOMPtr<IHistory> history = do_get_IHistory();
nsresult rv = history->RegisterVisitedCallback(testURI, nullptr);
do_check_success(rv);
rv = history->RegisterVisitedCallback(testURI, nullptr);
do_check_success(rv);
rv = history->VisitURI(testURI, nullptr, mozilla::IHistory::TOP_LEVEL);
do_check_success(rv);
RefPtr<VisitURIObserver> finisher = new VisitURIObserver();
finisher->WaitForNotification();
run_next_test();
}
////////////////////////////////////////////////////////////////////////////////
//// Test Harness
/**
* Note: for tests marked "Order Important!", please see the test for details.
*/
Test gTests[] = {
TEST(test_set_places_enabled), // Must come first!
TEST(test_wait_checkpoint), // Must come second!
TEST(test_unvisited_does_not_notify_part1), // Order Important!
TEST(test_visited_notifies),
TEST(test_unvisited_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),
TEST(test_observer_topic_dispatched),
TEST(test_visituri_inserts),
TEST(test_visituri_updates),
TEST(test_visituri_preserves_shown_and_typed),
TEST(test_visituri_creates_visit),
TEST(test_visituri_transition_typed),
TEST(test_visituri_transition_embed),
TEST(test_new_visit_adds_place_guid),
// The rest of these tests are tests that are only run in IPC builds.
TEST(test_two_null_links_same_uri),
};
const char* file = __FILE__;
#define TEST_NAME "IHistory"
#define TEST_FILE file
#include "places_test_harness_tail.h"