зеркало из https://github.com/mozilla/pjs.git
Bug 606966 - Need an async history visit API exposed to JS
Part 20 - API implementation with tests. r=mak r=lw a=blocking
This commit is contained in:
Родитель
5e94cb1462
Коммит
92b5b0cad4
|
@ -57,6 +57,7 @@
|
|||
#include "mozilla/Services.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
// Initial size for the cache holding visited status observers.
|
||||
#define VISIT_OBSERVERS_INITIAL_CACHE_SIZE 128
|
||||
|
@ -168,6 +169,141 @@ struct VisitData {
|
|||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Obtains an nsIURI from the "uri" property of a JSObject.
|
||||
*
|
||||
* @param aCtx
|
||||
* The JSContext for aObject.
|
||||
* @param aObject
|
||||
* The JSObject to get the URI from.
|
||||
* @param aProperty
|
||||
* The name of the property to get the URI from.
|
||||
* @return the URI if it exists.
|
||||
*/
|
||||
already_AddRefed<nsIURI>
|
||||
GetURIFromJSObject(JSContext* aCtx,
|
||||
JSObject* aObject,
|
||||
const char* aProperty)
|
||||
{
|
||||
jsval uriVal;
|
||||
JSBool rc = JS_GetProperty(aCtx, aObject, aProperty, &uriVal);
|
||||
NS_ENSURE_TRUE(rc, nsnull);
|
||||
if (!JSVAL_IS_PRIMITIVE(uriVal)) {
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrappedObj;
|
||||
nsresult rv = nsContentUtils::XPConnect()->GetWrappedNativeOfJSObject(
|
||||
aCtx,
|
||||
JSVAL_TO_OBJECT(uriVal),
|
||||
getter_AddRefs(wrappedObj)
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
nsCOMPtr<nsIURI> uri = do_QueryWrappedNative(wrappedObj);
|
||||
return uri.forget();
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the specified property of a JSObject.
|
||||
*
|
||||
* @param aCtx
|
||||
* The JSContext for aObject.
|
||||
* @param aObject
|
||||
* The JSObject to get the string from.
|
||||
* @param aProperty
|
||||
* The property to get the value from.
|
||||
* @param _string
|
||||
* The string to populate with the value, or set it to void.
|
||||
*/
|
||||
void
|
||||
GetStringFromJSObject(JSContext* aCtx,
|
||||
JSObject* aObject,
|
||||
const char* aProperty,
|
||||
nsString& _string)
|
||||
{
|
||||
jsval val;
|
||||
JSBool rc = JS_GetProperty(aCtx, aObject, aProperty, &val);
|
||||
if (!rc || JSVAL_IS_VOID(val) || !JSVAL_IS_STRING(val)) {
|
||||
_string.SetIsVoid(PR_TRUE);
|
||||
return;
|
||||
}
|
||||
size_t length;
|
||||
const jschar* chars =
|
||||
JS_GetStringCharsZAndLength(aCtx, JSVAL_TO_STRING(val), &length);
|
||||
if (!chars) {
|
||||
_string.SetIsVoid(PR_TRUE);
|
||||
return;
|
||||
}
|
||||
_string.Assign(static_cast<const PRUnichar*>(chars), length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the specified property of a JSObject.
|
||||
*
|
||||
* @param aCtx
|
||||
* The JSContext for aObject.
|
||||
* @param aObject
|
||||
* The JSObject to get the int from.
|
||||
* @param aProperty
|
||||
* The property to get the value from.
|
||||
* @param _int
|
||||
* The integer to populate with the value on success.
|
||||
*/
|
||||
template <typename IntType>
|
||||
nsresult
|
||||
GetIntFromJSObject(JSContext* aCtx,
|
||||
JSObject* aObject,
|
||||
const char* aProperty,
|
||||
IntType* _int)
|
||||
{
|
||||
jsval value;
|
||||
JSBool rc = JS_GetProperty(aCtx, aObject, aProperty, &value);
|
||||
NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
|
||||
if (JSVAL_IS_VOID(value)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
NS_ENSURE_ARG(JSVAL_IS_PRIMITIVE(value));
|
||||
NS_ENSURE_ARG(JSVAL_IS_NUMBER(value));
|
||||
|
||||
jsdouble num;
|
||||
rc = JS_ValueToNumber(aCtx, value, &num);
|
||||
NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
|
||||
NS_ENSURE_ARG(IntType(num) == num);
|
||||
|
||||
*_int = IntType(num);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the specified property of a JSObject.
|
||||
*
|
||||
* @pre aArray must be an Array object.
|
||||
*
|
||||
* @param aCtx
|
||||
* The JSContext for aArray.
|
||||
* @param aArray
|
||||
* The JSObject to get the object from.
|
||||
* @param aIndex
|
||||
* The index to get the object from.
|
||||
* @param _object
|
||||
* The JSObject pointer on success.
|
||||
*/
|
||||
nsresult
|
||||
GetJSObjectFromArray(JSContext* aCtx,
|
||||
JSObject* aArray,
|
||||
jsuint aIndex,
|
||||
JSObject** _rooter)
|
||||
{
|
||||
NS_PRECONDITION(JS_IsArrayObject(aCtx, aArray),
|
||||
"Must provide an object that is an array!");
|
||||
|
||||
jsval value;
|
||||
JSBool rc = JS_GetElement(aCtx, aArray, aIndex, &value);
|
||||
NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
|
||||
NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(value));
|
||||
*_rooter = JSVAL_TO_OBJECT(value);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class VisitedQuery : public AsyncStatementCallback
|
||||
{
|
||||
public:
|
||||
|
@ -1604,8 +1740,141 @@ History::UpdatePlaces(const jsval& aPlaceInfos,
|
|||
JSContext* aCtx)
|
||||
{
|
||||
NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
|
||||
NS_ENSURE_TRUE(!JSVAL_IS_PRIMITIVE(aPlaceInfos), NS_ERROR_INVALID_ARG);
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
jsuint infosLength = 1;
|
||||
JSObject* infos;
|
||||
if (JS_IsArrayObject(aCtx, JSVAL_TO_OBJECT(aPlaceInfos))) {
|
||||
infos = JSVAL_TO_OBJECT(aPlaceInfos);
|
||||
(void)JS_GetArrayLength(aCtx, infos, &infosLength);
|
||||
NS_ENSURE_ARG(infosLength > 0);
|
||||
}
|
||||
else {
|
||||
// Build a temporary array to store this one item so the code below can
|
||||
// just loop.
|
||||
infos = JS_NewArrayObject(aCtx, 0, NULL);
|
||||
NS_ENSURE_TRUE(infos, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
JSBool rc = JS_DefineElement(aCtx, infos, 0, aPlaceInfos, NULL, NULL, 0);
|
||||
NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
nsTArray<VisitData> visitData;
|
||||
for (jsuint i = 0; i < infosLength; i++) {
|
||||
JSObject* info;
|
||||
nsresult rv = GetJSObjectFromArray(aCtx, infos, i, &info);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri = GetURIFromJSObject(aCtx, info, "uri");
|
||||
PRInt64 placeId;
|
||||
rv = GetIntFromJSObject(aCtx, info, "placeId", &placeId);
|
||||
if (rv == NS_ERROR_INVALID_ARG) {
|
||||
placeId = 0;
|
||||
}
|
||||
else {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_ARG(placeId > 0);
|
||||
}
|
||||
nsCString guid;
|
||||
{
|
||||
nsString fatGUID;
|
||||
GetStringFromJSObject(aCtx, info, "guid", fatGUID);
|
||||
if (fatGUID.IsVoid()) {
|
||||
guid.SetIsVoid(PR_TRUE);
|
||||
}
|
||||
else {
|
||||
guid = NS_ConvertUTF16toUTF8(fatGUID);
|
||||
}
|
||||
}
|
||||
|
||||
// We must have at least one of uri, valid id, or guid.
|
||||
NS_ENSURE_ARG(uri || placeId > 0 || !guid.IsVoid());
|
||||
|
||||
// If we were given a guid, make sure it is valid.
|
||||
bool isValidGUID = IsValidGUID(guid);
|
||||
NS_ENSURE_ARG(guid.IsVoid() || isValidGUID);
|
||||
|
||||
nsString title;
|
||||
GetStringFromJSObject(aCtx, info, "title", title);
|
||||
|
||||
JSObject* visits = NULL;
|
||||
{
|
||||
jsval visitsVal;
|
||||
JSBool rc = JS_GetProperty(aCtx, info, "visits", &visitsVal);
|
||||
NS_ENSURE_TRUE(rc, NS_ERROR_UNEXPECTED);
|
||||
if (!JSVAL_IS_PRIMITIVE(visitsVal)) {
|
||||
visits = JSVAL_TO_OBJECT(visitsVal);
|
||||
NS_ENSURE_ARG(JS_IsArrayObject(aCtx, visits));
|
||||
}
|
||||
}
|
||||
|
||||
jsuint visitsLength = 0;
|
||||
if (visits) {
|
||||
(void)JS_GetArrayLength(aCtx, visits, &visitsLength);
|
||||
}
|
||||
|
||||
// If we have no id or guid, we must have visits.
|
||||
if (!(placeId > 0 || isValidGUID)) {
|
||||
NS_ENSURE_ARG(visits);
|
||||
NS_ENSURE_ARG(visitsLength > 0);
|
||||
}
|
||||
|
||||
// Check each visit, and build our array of VisitData objects.
|
||||
visitData.SetCapacity(visitData.Length() + visitsLength);
|
||||
for (jsuint j = 0; j < visitsLength; j++) {
|
||||
JSObject* visit;
|
||||
rv = GetJSObjectFromArray(aCtx, visits, j, &visit);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
VisitData& data = *visitData.AppendElement(VisitData(uri));
|
||||
data.placeId = placeId;
|
||||
data.title = title;
|
||||
data.guid = guid;
|
||||
|
||||
// We must have a date and a transaction type!
|
||||
rv = GetIntFromJSObject(aCtx, visit, "visitDate", &data.visitTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
PRUint32 transitionType;
|
||||
rv = GetIntFromJSObject(aCtx, visit, "transitionType", &transitionType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_ARG_RANGE(transitionType,
|
||||
nsINavHistoryService::TRANSITION_LINK,
|
||||
nsINavHistoryService::TRANSITION_FRAMED_LINK);
|
||||
data.SetTransitionType(transitionType);
|
||||
|
||||
// If the visit is an embed visit, we do not actually add it to the
|
||||
// database.
|
||||
if (transitionType == nsINavHistoryService::TRANSITION_EMBED) {
|
||||
StoreAndNotifyEmbedVisit(data, aCallback);
|
||||
visitData.RemoveElementAt(visitData.Length() - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// The session id is optional.
|
||||
rv = GetIntFromJSObject(aCtx, visit, "sessionId", &data.sessionId);
|
||||
if (rv == NS_ERROR_INVALID_ARG) {
|
||||
data.sessionId = 0;
|
||||
}
|
||||
else {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// The referrer is optional.
|
||||
nsCOMPtr<nsIURI> referrer = GetURIFromJSObject(aCtx, visit,
|
||||
"referrerURI");
|
||||
if (referrer) {
|
||||
(void)referrer->GetSpec(data.referrerSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mozIStorageConnection* dbConn = GetDBConn();
|
||||
NS_ENSURE_STATE(dbConn);
|
||||
|
||||
nsresult rv = InsertVisitedURIs::Start(dbConn, visitData, aCallback);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -597,10 +597,16 @@ function waitForAsyncUpdates(aCallback, aScope, aArguments)
|
|||
*
|
||||
* @param aGuid
|
||||
* The guid to test.
|
||||
* @param [optional] aStack
|
||||
* The stack frame used to report the error.
|
||||
*/
|
||||
function do_check_valid_places_guid(aGuid)
|
||||
function do_check_valid_places_guid(aGuid,
|
||||
aStack)
|
||||
{
|
||||
do_check_true(/^[a-zA-Z0-9\-_]{12}$/.test(aGuid), Components.stack.caller);
|
||||
if (!aStack) {
|
||||
aStack = Components.stack.caller;
|
||||
}
|
||||
do_check_true(/^[a-zA-Z0-9\-_]{12}$/.test(aGuid), aStack);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -608,17 +614,25 @@ function do_check_valid_places_guid(aGuid)
|
|||
*
|
||||
* @param aURI
|
||||
* The uri to check.
|
||||
* @param [optional] aGUID
|
||||
* The expected guid in the database.
|
||||
*/
|
||||
function do_check_guid_for_uri(aURI)
|
||||
function do_check_guid_for_uri(aURI,
|
||||
aGUID)
|
||||
{
|
||||
let caller = Components.stack.caller;
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT guid "
|
||||
+ "FROM moz_places "
|
||||
+ "WHERE url = :url "
|
||||
);
|
||||
stmt.params.url = aURI.spec;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_valid_places_guid(stmt.row.guid);
|
||||
do_check_true(stmt.executeStep(), caller);
|
||||
do_check_valid_places_guid(stmt.row.guid, caller);
|
||||
if (aGUID) {
|
||||
do_check_valid_places_guid(aGUID, caller);
|
||||
do_check_eq(stmt.row.guid, aGUID, caller);
|
||||
}
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
|
@ -641,11 +655,6 @@ let gRunningTest = null;
|
|||
let gTestIndex = 0; // The index of the currently running test.
|
||||
function run_next_test()
|
||||
{
|
||||
if (gRunningTest !== null) {
|
||||
// Close the previous test do_test_pending call.
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
function _run_next_test()
|
||||
{
|
||||
if (gTestIndex < gTests.length) {
|
||||
|
@ -665,4 +674,9 @@ function run_next_test()
|
|||
|
||||
// For sane stacks during failures, we execute this code soon, but not now.
|
||||
do_execute_soon(_run_next_test);
|
||||
|
||||
if (gRunningTest !== null) {
|
||||
// Close the previous test do_test_pending call.
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -590,12 +590,6 @@ var gTests = [
|
|||
cleanDatabase(run_next_test);
|
||||
},
|
||||
|
||||
function lastTestJustCallsTestFinished() {
|
||||
// close out our over-arching test
|
||||
do_test_finished();
|
||||
// and let run_next_test close out its pending test too.
|
||||
run_next_test();
|
||||
},
|
||||
];
|
||||
|
||||
// The tag keys in query URIs, i.e., "place:tag=foo&!tags=1"
|
||||
|
@ -832,9 +826,5 @@ function setsAreEqual(aArr1, aArr2, aIsOrdered) {
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function run_test() {
|
||||
// Create an overarching test for our entire test run. this is balanced
|
||||
// by a dummy test as the last test in our test list that just calls
|
||||
// do_test_finished().
|
||||
do_test_pending();
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,62 @@
|
|||
* This file tests the async history API exposed by mozIAsyncHistory.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gHistory",
|
||||
"@mozilla.org/browser/history;1",
|
||||
"mozIAsyncHistory");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gGlobalHistory",
|
||||
"@mozilla.org/browser/nav-history-service;1",
|
||||
"nsIGlobalHistory2");
|
||||
|
||||
const TEST_DOMAIN = "http://mozilla.org/"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Helpers
|
||||
|
||||
/**
|
||||
* Object that represents a mozIVisitInfo object.
|
||||
*
|
||||
* @param [optional] aTransitionType
|
||||
* The transition type of the visit. Defaults to TRANSITION_LINK if not
|
||||
* provided.
|
||||
* @param [optional] aVisitTime
|
||||
* The time of the visit. Defaults to now if not provided.
|
||||
*/
|
||||
function VisitInfo(aTransitionType,
|
||||
aVisitTime)
|
||||
{
|
||||
this.transitionType =
|
||||
aTransitionType === undefined ? TRANSITION_LINK : aTransitionType;
|
||||
this.visitDate = aVisitTime || Date.now() * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a title was set properly in the database.
|
||||
*
|
||||
* @param aURI
|
||||
* The uri to check.
|
||||
* @param aTitle
|
||||
* The expected title in the database.
|
||||
*/
|
||||
function do_check_title_for_uri(aURI,
|
||||
aTitle)
|
||||
{
|
||||
let stack = Components.stack.caller;
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT title " +
|
||||
"FROM moz_places " +
|
||||
"WHERE url = :url "
|
||||
);
|
||||
stmt.params.url = aURI.spec;
|
||||
do_check_true(stmt.executeStep(), stack);
|
||||
do_check_eq(stmt.row.title, aTitle, stack);
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Functions
|
||||
|
||||
|
@ -15,11 +71,581 @@ function test_interface_exists()
|
|||
run_next_test();
|
||||
}
|
||||
|
||||
function test_invalid_uri_throws()
|
||||
{
|
||||
// First, test passing in nothing.
|
||||
let place = {
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// Now, test other bogus things.
|
||||
const TEST_VALUES = [
|
||||
null,
|
||||
undefined,
|
||||
{},
|
||||
[],
|
||||
TEST_DOMAIN + "test_invalid_id_throws",
|
||||
];
|
||||
for (let i = 0; i < TEST_VALUES.length; i++) {
|
||||
place.uri = TEST_VALUES[i];
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_invalid_places_throws()
|
||||
{
|
||||
// First, test passing in nothing.
|
||||
try {
|
||||
gHistory.updatePlaces();
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS);
|
||||
}
|
||||
|
||||
// Now, test other bogus things.
|
||||
const TEST_VALUES = [
|
||||
null,
|
||||
undefined,
|
||||
{},
|
||||
[],
|
||||
"",
|
||||
];
|
||||
for (let i = 0; i < TEST_VALUES.length; i++) {
|
||||
let value = TEST_VALUES[i];
|
||||
try {
|
||||
gHistory.updatePlaces(value);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_invalid_id_throws()
|
||||
{
|
||||
// First check invalid id "0".
|
||||
let place = {
|
||||
placeId: 0,
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_invalid_id_throws"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// Now check negative id.
|
||||
place.placeId = -5;
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_invalid_guid_throws()
|
||||
{
|
||||
// First check invalid length guid.
|
||||
let place = {
|
||||
guid: "BAD_GUID",
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_invalid_guid_throws"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// Now check invalid character guid.
|
||||
place.guid = "__BADGUID+__";
|
||||
do_check_eq(place.guid.length, 12);
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_no_id_or_guid_no_visits_throws()
|
||||
{
|
||||
// First, test with no visit attribute.
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_no_id_or_guid_no_visits_throws"),
|
||||
};
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// Now test with an empty array.
|
||||
place.visits = [];
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_add_visit_no_date_throws()
|
||||
{
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit_no_date_throws"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
delete place.visits[0].visitDate;
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_add_visit_no_transitionType_throws()
|
||||
{
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit_no_transitionType_throws"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
delete place.visits[0].transitionType;
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_add_visit_invalid_transitionType_throws()
|
||||
{
|
||||
// First, test something that has a transition type lower than the first one.
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN +
|
||||
"test_add_visit_invalid_transitionType_throws"),
|
||||
visits: [
|
||||
new VisitInfo(TRANSITION_LINK - 1),
|
||||
],
|
||||
};
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
// Now, test something that has a transition type greater than the last one.
|
||||
place.visits[0] = new VisitInfo(TRANSITION_FRAMED_LINK + 1);
|
||||
try {
|
||||
gHistory.updatePlaces(place);
|
||||
do_throw("Should have thrown!");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_add_visit()
|
||||
{
|
||||
const VISIT_TIME = Date.now() * 1000;
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit"),
|
||||
title: "test_add_visit title",
|
||||
visits: [],
|
||||
};
|
||||
for (let transitionType = TRANSITION_LINK;
|
||||
transitionType <= TRANSITION_FRAMED_LINK;
|
||||
transitionType++) {
|
||||
place.visits.push(new VisitInfo(transitionType, VISIT_TIME));
|
||||
}
|
||||
do_check_false(gGlobalHistory.isVisited(place.uri));
|
||||
|
||||
let callbackCount = 0;
|
||||
gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
do_check_true(gGlobalHistory.isVisited(place.uri));
|
||||
|
||||
// Check mozIPlaceInfo properties.
|
||||
do_check_true(place.uri.equals(aPlaceInfo.uri));
|
||||
do_check_eq(aPlaceInfo.frecency, -1); // We don't pass frecency here!
|
||||
do_check_eq(aPlaceInfo.title, place.title);
|
||||
|
||||
// Check mozIVisitInfo properties.
|
||||
let visits = aPlaceInfo.visits;
|
||||
do_check_eq(visits.length, 1);
|
||||
let visit = visits[0];
|
||||
do_check_eq(visit.visitDate, VISIT_TIME);
|
||||
do_check_true(visit.transitionType >= TRANSITION_LINK &&
|
||||
visit.transitionType <= TRANSITION_FRAMED_LINK);
|
||||
do_check_true(visit.referrerURI === null);
|
||||
|
||||
// For TRANSITION_EMBED visits, many properties will always be zero or
|
||||
// undefined.
|
||||
if (visit.transitionType == TRANSITION_EMBED) {
|
||||
// Check mozIPlaceInfo properties.
|
||||
do_check_eq(aPlaceInfo.placeId, 0);
|
||||
do_check_eq(aPlaceInfo.guid, null);
|
||||
|
||||
// Check mozIVisitInfo properties.
|
||||
do_check_eq(visit.visitId, 0);
|
||||
do_check_eq(visit.sessionId, 0);
|
||||
}
|
||||
// But they should be valid for non-embed visits.
|
||||
else {
|
||||
// Check mozIPlaceInfo properties.
|
||||
do_check_true(aPlaceInfo.placeId > 0);
|
||||
do_check_valid_places_guid(aPlaceInfo.guid);
|
||||
|
||||
// Check mozIVisitInfo properties.
|
||||
do_check_true(visit.visitId > 0);
|
||||
do_check_true(visit.sessionId > 0);
|
||||
}
|
||||
|
||||
// If we have had all of our callbacks, continue running tests.
|
||||
if (++callbackCount == place.visits.length) {
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function test_properties_saved()
|
||||
{
|
||||
// Check each transition type to make sure it is saved properly.
|
||||
let places = [];
|
||||
for (let transitionType = TRANSITION_LINK;
|
||||
transitionType <= TRANSITION_FRAMED_LINK;
|
||||
transitionType++) {
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_properties_saved/" +
|
||||
transitionType),
|
||||
title: "test_properties_saved test",
|
||||
visits: [
|
||||
new VisitInfo(transitionType),
|
||||
],
|
||||
};
|
||||
do_check_false(gGlobalHistory.isVisited(place.uri));
|
||||
places.push(place);
|
||||
}
|
||||
|
||||
let callbackCount = 0;
|
||||
gHistory.updatePlaces(places, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
let uri = aPlaceInfo.uri;
|
||||
do_check_true(gGlobalHistory.isVisited(uri));
|
||||
let visit = aPlaceInfo.visits[0];
|
||||
print("TEST-INFO | test_properties_saved | updatePlaces callback for " +
|
||||
"transition type " + visit.transitionType);
|
||||
|
||||
// Note that TRANSITION_EMBED should not be in the database.
|
||||
const EXPECTED_COUNT = visit.transitionType == TRANSITION_EMBED ? 0 : 1;
|
||||
|
||||
// mozIVisitInfo::date
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT COUNT(1) AS count " +
|
||||
"FROM moz_places h " +
|
||||
"JOIN moz_historyvisits v " +
|
||||
"ON h.id = v.place_id " +
|
||||
"WHERE h.url = :page_url " +
|
||||
"AND v.visit_date = :visit_date "
|
||||
);
|
||||
stmt.params.page_url = uri.spec;
|
||||
stmt.params.visit_date = visit.visitDate;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.row.count, EXPECTED_COUNT);
|
||||
stmt.finalize();
|
||||
|
||||
// mozIVisitInfo::transitionType
|
||||
stmt = DBConn().createStatement(
|
||||
"SELECT COUNT(1) AS count " +
|
||||
"FROM moz_places h " +
|
||||
"JOIN moz_historyvisits v " +
|
||||
"ON h.id = v.place_id " +
|
||||
"WHERE h.url = :page_url " +
|
||||
"AND v.visit_type = :transition_type "
|
||||
);
|
||||
stmt.params.page_url = uri.spec;
|
||||
stmt.params.transition_type = visit.transitionType;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.row.count, EXPECTED_COUNT);
|
||||
stmt.finalize();
|
||||
|
||||
// mozIVisitInfo::sessionId
|
||||
stmt = DBConn().createStatement(
|
||||
"SELECT COUNT(1) AS count " +
|
||||
"FROM moz_places h " +
|
||||
"JOIN moz_historyvisits v " +
|
||||
"ON h.id = v.place_id " +
|
||||
"WHERE h.url = :page_url " +
|
||||
"AND v.session = :session_id "
|
||||
);
|
||||
stmt.params.page_url = uri.spec;
|
||||
stmt.params.session_id = visit.sessionId;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.row.count, EXPECTED_COUNT);
|
||||
stmt.finalize();
|
||||
|
||||
// mozIPlaceInfo::title
|
||||
stmt = DBConn().createStatement(
|
||||
"SELECT COUNT(1) AS count " +
|
||||
"FROM moz_places h " +
|
||||
"WHERE h.url = :page_url " +
|
||||
"AND h.title = :title "
|
||||
);
|
||||
stmt.params.page_url = uri.spec;
|
||||
stmt.params.title = aPlaceInfo.title;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.row.count, EXPECTED_COUNT);
|
||||
stmt.finalize();
|
||||
|
||||
// If we have had all of our callbacks, continue running tests.
|
||||
if (++callbackCount == places.length) {
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function test_guid_saved()
|
||||
{
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_guid_saved"),
|
||||
guid: "__TESTGUID__",
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
do_check_valid_places_guid(place.guid);
|
||||
do_check_false(gGlobalHistory.isVisited(place.uri));
|
||||
|
||||
gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
let uri = aPlaceInfo.uri;
|
||||
do_check_true(gGlobalHistory.isVisited(uri));
|
||||
do_check_eq(aPlaceInfo.guid, place.guid);
|
||||
do_check_guid_for_uri(uri, place.guid);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
}
|
||||
|
||||
function test_referrer_saved()
|
||||
{
|
||||
let places = [
|
||||
{ uri: NetUtil.newURI(TEST_DOMAIN + "test_referrer_saved/referrer"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
},
|
||||
{ uri: NetUtil.newURI(TEST_DOMAIN + "test_referrer_saved/test"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
},
|
||||
];
|
||||
places[1].visits[0].referrerURI = places[0].uri;
|
||||
do_check_false(gGlobalHistory.isVisited(places[0].uri));
|
||||
do_check_false(gGlobalHistory.isVisited(places[1].uri));
|
||||
|
||||
let callbackCount = 0;
|
||||
let referrerSessionId;
|
||||
gHistory.updatePlaces(places, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
let uri = aPlaceInfo.uri;
|
||||
do_check_true(gGlobalHistory.isVisited(uri));
|
||||
let visit = aPlaceInfo.visits[0];
|
||||
|
||||
// We need to insert all of our visits before we can test conditions.
|
||||
if (++callbackCount != places.length) {
|
||||
referrerSessionId = visit.sessionId;
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_true(places[0].uri.equals(visit.referrerURI));
|
||||
do_check_eq(visit.sessionId, referrerSessionId);
|
||||
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT COUNT(1) AS count " +
|
||||
"FROM moz_historyvisits " +
|
||||
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) " +
|
||||
"AND from_visit = ( " +
|
||||
"SELECT id " +
|
||||
"FROM moz_historyvisits " +
|
||||
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :referrer) " +
|
||||
") "
|
||||
);
|
||||
stmt.params.page_url = uri.spec;
|
||||
stmt.params.referrer = visit.referrerURI.spec;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.row.count, 1);
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
}
|
||||
|
||||
function test_sessionId_saved()
|
||||
{
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_sessionId_saved"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
place.visits[0].sessionId = 3;
|
||||
do_check_false(gGlobalHistory.isVisited(place.uri));
|
||||
|
||||
gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
let uri = aPlaceInfo.uri;
|
||||
do_check_true(gGlobalHistory.isVisited(uri));
|
||||
|
||||
let visit = aPlaceInfo.visits[0];
|
||||
do_check_eq(visit.sessionId, place.visits[0].sessionId);
|
||||
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT COUNT(1) AS count " +
|
||||
"FROM moz_historyvisits " +
|
||||
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) " +
|
||||
"AND session = :session_id "
|
||||
);
|
||||
stmt.params.page_url = uri.spec;
|
||||
stmt.params.session_id = visit.sessionId;
|
||||
do_check_true(stmt.executeStep());
|
||||
do_check_eq(stmt.row.count, 1);
|
||||
stmt.finalize();
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
}
|
||||
|
||||
function test_guid_change_saved()
|
||||
{
|
||||
// First, add a visit for it.
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_guid_change_saved"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
do_check_false(gGlobalHistory.isVisited(place.uri));
|
||||
|
||||
gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
|
||||
// Then, change the guid with visits.
|
||||
place.guid = "_GUIDCHANGE_";
|
||||
place.visits = [new VisitInfo()];
|
||||
gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
do_check_guid_for_uri(place.uri, place.guid);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function test_title_change_saved()
|
||||
{
|
||||
// First, add a visit for it.
|
||||
let place = {
|
||||
uri: NetUtil.newURI(TEST_DOMAIN + "test_title_change_saved"),
|
||||
visits: [
|
||||
new VisitInfo(),
|
||||
],
|
||||
};
|
||||
do_check_false(gGlobalHistory.isVisited(place.uri));
|
||||
|
||||
gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
|
||||
// Then, change the title with visits.
|
||||
place.title = "title change";
|
||||
place.visits = [new VisitInfo()];
|
||||
gHistory.updatePlaces(place, function(aResultCode, aPlaceInfo) {
|
||||
do_check_true(Components.isSuccessCode(aResultCode));
|
||||
do_check_title_for_uri(place.uri, place.title);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Test Runner
|
||||
|
||||
let gTests = [
|
||||
test_interface_exists,
|
||||
test_invalid_uri_throws,
|
||||
test_invalid_places_throws,
|
||||
test_invalid_id_throws,
|
||||
test_invalid_guid_throws,
|
||||
test_no_id_or_guid_no_visits_throws,
|
||||
test_add_visit_no_date_throws,
|
||||
test_add_visit_no_transitionType_throws,
|
||||
test_add_visit_invalid_transitionType_throws,
|
||||
test_add_visit,
|
||||
test_properties_saved,
|
||||
test_guid_saved,
|
||||
test_referrer_saved,
|
||||
test_sessionId_saved,
|
||||
test_guid_change_saved,
|
||||
test_title_change_saved,
|
||||
];
|
||||
|
||||
function run_test()
|
||||
|
|
Загрузка…
Ссылка в новой задаче