Bug 450290 - Sync the temp tables to the permanent tables.

This changeset adds code that is run at app-startup that will flush out changes
to the temporary tables into the permanent ones.  For moz_places, this is done
whenever we sync moz_historyvisits and when we add or modify a bookmark.  For
moz_historyvisits, this is done on a timer controlled by the preference
places.syncDBTableIntervalInSecs.
Changeset includes full test coverage for the above behaviors.
r=dietrich
r=Mak77

--HG--
rename : toolkit/components/places/tests/unit/head_bookmarks.js => toolkit/components/places/tests/background/head_background.js
This commit is contained in:
Shawn Wilsher 2008-09-18 11:10:47 -04:00
parent ec97557606
commit 16fef55947
17 changed files with 1231 additions and 4 deletions

View File

@ -234,6 +234,7 @@ bin/components/libbrowsercomps.so
bin/components/txEXSLTRegExFunctions.js
bin/components/nsLivemarkService.js
bin/components/nsTaggingService.js
bin/components/nsPlacesDBFlush.js
bin/components/nsDefaultCLH.js
bin/components/nsContentPrefService.js
bin/components/nsContentDispatchChooser.js

View File

@ -241,6 +241,7 @@ bin\components\brwsrcmp.dll
bin\components\txEXSLTRegExFunctions.js
bin\components\nsLivemarkService.js
bin\components\nsTaggingService.js
bin\components\nsPlacesDBFlush.js
bin\components\nsDefaultCLH.js
bin\components\nsContentPrefService.js
bin\components\nsContentDispatchChooser.js

View File

@ -105,6 +105,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../../build
EXTRA_PP_COMPONENTS = nsLivemarkService.js \
nsTaggingService.js \
nsPlacesDBFlush.js \
$(NULL)
EXTRA_JS_MODULES = \

View File

@ -49,6 +49,7 @@ const Ci = Components.interfaces;
const Cr = Components.results;
const kQuitApplication = "quit-application";
const kPlacesBackgroundShutdown = "places-background-shutdown";
////////////////////////////////////////////////////////////////////////////////
//// nsPlacesBackgound class
@ -84,6 +85,12 @@ nsPlacesBackground.prototype = {
observe: function PlacesBackground_observe(aSubject, aTopic, aData)
{
if (aTopic == kQuitApplication) {
// Notify consumers that we are shutting down.
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.notifyObservers(null, kPlacesBackgroundShutdown, null);
// Now shut the thread down.
this._thread.shutdown();
this._thread = null;
}

View File

@ -991,6 +991,9 @@ nsNavHistory::InitTempTables()
"CREATE INDEX moz_places_temp_frecencyindex ON moz_places_temp (frecency)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES_SYNC_TRIGGER);
NS_ENSURE_SUCCESS(rv, rv);
// moz_historyvisits_temp
rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS_TEMP);
@ -1011,6 +1014,9 @@ nsNavHistory::InitTempTables()
"ON moz_historyvisits_temp (visit_date)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS_SYNC_TRIGGER);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}

View File

@ -0,0 +1,277 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 ***** */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
////////////////////////////////////////////////////////////////////////////////
//// Constants
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const kPlacesBackgroundShutdown = "places-background-shutdown";
const kSyncPrefName = "syncDBTableIntervalInSecs";
const kDefaultSyncInterval = 120;
////////////////////////////////////////////////////////////////////////////////
//// nsPlacesDBFlush class
function nsPlacesDBFlush()
{
//////////////////////////////////////////////////////////////////////////////
//// Smart Getters
this.__defineGetter__("_db", function() {
delete this._db;
return this._db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
});
this.__defineGetter__("_bh", function() {
delete this._bh;
return this._bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
});
// Get our sync interval
this._prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("places.");
try {
// We want to silently fail if the preference does not exist, and use a
// default to fallback to.
this._syncInterval = this._prefs.getIntPref(kSyncPrefName);
if (this._syncInterval <= 0)
this._syncInterval = kDefaultSyncInterval;
}
catch (e) {
// The preference did not exist, so default to two minutes.
this._syncInterval = kDefaultSyncInterval;
}
// Register observers
this._bh.addObserver(this, false);
this._prefs.QueryInterface(Ci.nsIPrefBranch2).addObserver("", this, false);
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(this, kPlacesBackgroundShutdown, false);
// Create our timer to update everything
this._timer = this._newTimer();
}
nsPlacesDBFlush.prototype = {
//////////////////////////////////////////////////////////////////////////////
//// nsIObserver
observe: function DBFlush_observe(aSubject, aTopic, aData)
{
if (aTopic == kPlacesBackgroundShutdown) {
this._bh.removeObserver(this);
this._timer.cancel();
this._timer = null;
this._syncAll();
}
else if (aTopic == "nsPref:changed" && aData == kSyncPrefName) {
// Get the new pref value, and then update our timer
this._syncInterval = aSubject.getIntPref(kSyncPrefName);
if (this._syncInterval <= 0)
this._syncInterval = kDefaultSyncInterval;
// We may have canceled the timer already for batch updates, so we want to
// exit early.
if (!this._timer)
return;
this._timer.cancel();
this._timer = this._newTimer();
}
},
//////////////////////////////////////////////////////////////////////////////
//// nsINavBookmarkObserver
onBeginUpdateBatch: function DBFlush_onBeginUpdateBatch()
{
this._inBatchMode = true;
// We do not want to sync while we are doing batch work.
this._timer.cancel();
this._timer = null;
},
onEndUpdateBatch: function DBFlush_onEndUpdateBatch()
{
this._inBatchMode = false;
// We need to sync and restore our timer now.
this._syncAll();
this._timer = this._newTimer();
},
onItemAdded: function() this._syncMozPlaces(),
onItemChanged: function DBFlush_onItemChanged(aItemId, aProperty,
aIsAnnotationProperty,
aValue)
{
if (aProperty == "uri")
this._syncMozPlaces();
},
onItemRemoved: function() { },
onItemVisited: function() { },
onItemMoved: function() { },
//////////////////////////////////////////////////////////////////////////////
//// nsITimerCallback
notify: function() this._syncAll(),
//////////////////////////////////////////////////////////////////////////////
//// nsPlacesDBFlush
/**
* Dispatches an event to the background thread to run _doSyncMozX("places").
*/
_syncMozPlaces: function DBFlush_syncMozPlaces()
{
// No need to do extra work if we are in batch mode
if (this._inBatchMode)
return;
let self = this;
PlacesBackground.dispatch({
run: function() self._doSyncMozX("places")
}, Ci.nsIEventTarget.DISPATCH_NORMAL);
},
/**
* Dispatches an event to the background thread to sync all temporary tables.
*/
_syncAll: function DBFlush_syncAll()
{
let self = this;
PlacesBackground.dispatch({
run: function() {
// We try to get a transaction, but if we can't don't worry
let ourTransaction = false;
try {
this._db.beginTransaction();
ourTransaction = true;
}
catch (e) { }
try {
// This needs to also sync moz_places in order to maintain data
// integrity
self._doSyncMozX("places");
self._doSyncMozX("historyvisits");
}
catch (e) {
if (ourTransaction)
this._db.rollbackTransaction();
throw e;
}
if (ourTransaction)
this._db.commitTransaction();
}
}, Ci.nsIEventTarget.DISPATCH_NORMAL);
},
/**
* Synchronizes the moz_{aName} and moz_{aName}_temp by copying all the data
* from the temporary table into the permanent one. It then deletes the data
* in the temporary table. All of this is done in a transaction that is
* rolled back upon failure at any point.
*/
_doSyncMozX: function DBFlush_doSyncMozX(aName)
{
// Delete all the data in the temp table.
// We have triggers setup that ensure that the data is transfered over
// upon deletion.
this._db.executeSimpleSQL("DELETE FROM moz_" + aName + "_temp");
},
/**
* Creates a new timer bases on this._timerInterval.
*
* @returns a REPEATING_SLACK nsITimer that runs every this._timerInterval.
*/
_newTimer: function DBFlush_newTimer()
{
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(this, this._syncInterval * 1000,
Ci.nsITimer.TYPE_REPEATING_SLACK);
return timer;
},
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
classDescription: "Used to synchronize the temporary and permanent tables of Places",
classID: Components.ID("c1751cfc-e8f1-4ade-b0bb-f74edfb8ef6a"),
contractID: "@mozilla.org/places/sync;1",
_xpcom_categories: [{
category: "profile-after-change",
}],
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsINavBookmarkObserver,
Ci.nsITimerCallback,
])
};
////////////////////////////////////////////////////////////////////////////////
//// Module Registration
let components = [nsPlacesDBFlush];
function NSGetModule(compMgr, fileSpec)
{
return XPCOMUtils.generateModule(components);
}

View File

@ -202,4 +202,26 @@
"END" \
)
/**
* This trigger moves the data out of a temporary table into the permanent one
* before deleting from the temporary table.
*
* Note - it's OK to use an INSERT OR REPLACE here because the only conflict
* that will happen is the primary key. As a result, the row will be deleted,
* and the replacement will be inserted with the same id.
*/
#define CREATE_TEMP_SYNC_TRIGGER_BASE(__table) NS_LITERAL_CSTRING( \
"CREATE TEMPORARY TRIGGER " __table "_beforedelete_trigger " \
"BEFORE DELETE ON " __table "_temp FOR EACH ROW " \
"BEGIN " \
"INSERT OR REPLACE INTO " __table " " \
"SELECT * FROM " __table "_temp " \
"WHERE id = OLD.id;" \
"END" \
)
#define CREATE_MOZ_PLACES_SYNC_TRIGGER \
CREATE_TEMP_SYNC_TRIGGER_BASE("moz_places")
#define CREATE_MOZ_HISTORYVISITS_SYNC_TRIGGER \
CREATE_TEMP_SYNC_TRIGGER_BASE("moz_historyvisits")
#endif // __nsPlacesTriggers_h__

View File

@ -0,0 +1,275 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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.
*
* The Initial Developer of the Original Code is
* Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com>
* Dietrich Ayala <dietrich@mozilla.com>
*
* 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 ***** */
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
const NS_APP_HISTORY_50_FILE = "UHist";
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
function LOG(aMsg) {
aMsg = ("*** PLACES TESTS: " + aMsg);
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
logStringMessage(aMsg);
print(aMsg);
}
// If there's no location registered for the profile direcotry, register one now.
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
var profileDir = null;
try {
profileDir = dirSvc.get(NS_APP_USER_PROFILE_50_DIR, Ci.nsIFile);
} catch (e) {}
if (!profileDir) {
// Register our own provider for the profile directory.
// It will simply return the current directory.
var provider = {
getFile: function(prop, persistent) {
persistent.value = true;
if (prop == NS_APP_USER_PROFILE_50_DIR) {
return dirSvc.get("CurProcD", Ci.nsIFile);
}
if (prop == NS_APP_HISTORY_50_FILE) {
var histFile = dirSvc.get("CurProcD", Ci.nsIFile);
histFile.append("history.dat");
return histFile;
}
throw Cr.NS_ERROR_FAILURE;
},
QueryInterface: function(iid) {
if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
iid.equals(Ci.nsISupports)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
}
};
dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
}
var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
function uri(spec) {
return iosvc.newURI(spec, null, null);
}
// Delete a previously created sqlite file
function clearDB() {
try {
var file = dirSvc.get('ProfD', Ci.nsIFile);
file.append("places.sqlite");
if (file.exists())
file.remove(false);
} catch(ex) { dump("Exception: " + ex); }
}
clearDB();
/**
* Dumps the rows of a table out to the console.
*
* @param aName
* The name of the table or view to output.
*/
function dump_table(aName)
{
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
let stmt = db.createStatement("SELECT * FROM " + aName);
dump("\n*** Printing data from " + aName + ":\n");
let count = 0;
while (stmt.executeStep()) {
let columns = stmt.numEntries;
if (count == 0) {
// print the column names
for (let i = 0; i < columns; i++)
dump(stmt.getColumnName(i) + "\t");
dump("\n");
}
// print the row
for (let i = 0; i < columns; i++) {
switch (stmt.getTypeOfIndex(i)) {
case Ci.mozIStorageValueArray.VALUE_TYPE_NULL:
dump("NULL\t");
break;
case Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER:
dump(stmt.getInt64(i) + "\t");
break;
case Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT:
dump(stmt.getDouble(i) + "\t");
break;
case Ci.mozIStorageValueArray.VALUE_TYPE_TEXT:
dump(stmt.getString(i) + "\t");
break;
}
}
dump("\n");
count++;
}
dump("*** There were a total of " + count + " rows of data.\n\n");
stmt.reset();
stmt.finalize();
stmt = null;
}
/**
* This dispatches the observer topic "quit-application" to clean up the
* background thread.
*/
function finish_test()
{
// This next bit needs to run on the main thread
let tm = Cc["@mozilla.org/thread-manager;1"].
getService(Ci.nsIThreadManager);
tm.mainThread.dispatch({
run: function()
{
// xpcshell doesn't dispatch shutdown-application
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.notifyObservers(null, "quit-application", null);
do_test_finished();
}
}, Ci.nsIEventTarget.DISPATCH_NORMAL);
}
/**
* Function tests to see if the place associated with the bookmark with id
* aBookmarkId has the uri aExpectedURI. The event will call finish_test() if
* aFinish is true.
*
* @param aBookmarkId
* The bookmark to check against.
* @param aExpectedURI
* The URI we expect to be in moz_places.
* @param aExpected
* Indicates if we expect to get a result or not.
* @param [optional] aFinish
* Indicates if the test should be completed or not.
*/
function new_test_bookmark_uri_event(aBookmarkId, aExpectedURI, aExpected, aFinish)
{
return {
run: function()
{
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
let stmt = db.createStatement(
"SELECT moz_places.url " +
"FROM moz_bookmarks INNER JOIN moz_places " +
"ON moz_bookmarks.fk = moz_places.id " +
"WHERE moz_bookmarks.id = ?1"
);
stmt.bindInt64Parameter(0, aBookmarkId);
if (aExpected) {
do_check_true(stmt.executeStep());
do_check_eq(stmt.getUTF8String(0), aExpectedURI);
}
else {
do_check_false(stmt.executeStep());
}
stmt.reset();
stmt.finalize();
stmt = null;
if (aFinish)
finish_test();
}
};
}
/**
* Function tests to see if the place associated with the visit with id aVisitId
* has the uri aExpectedURI. The event will call finish_test() if aFinish is
* true.
*
* @param aVisitId
* The visit to check against.
* @param aExpectedURI
* The URI we expect to be in moz_places.
* @param aExpected
* Indicates if we expect to get a result or not.
* @param [optional] aFinish
* Indicates if the test should be completed or not.
*/
function new_test_visit_uri_event(aVisitId, aExpectedURI, aExpected, aFinish)
{
return {
run: function()
{
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
let stmt = db.createStatement(
"SELECT moz_places.url " +
"FROM moz_historyvisits INNER JOIN moz_places " +
"ON moz_historyvisits.place_id = moz_places.id " +
"WHERE moz_historyvisits.id = ?1"
);
stmt.bindInt64Parameter(0, aVisitId);
if (aExpected) {
do_check_true(stmt.executeStep());
do_check_eq(stmt.getUTF8String(0), aExpectedURI);
}
else {
do_check_false(stmt.executeStep());
}
stmt.reset();
stmt.finalize();
stmt = null;
if (aFinish)
finish_test();
}
};
}
// profile-after-change doesn't create components in xpcshell, so we have to do
// it ourselves
Cc["@mozilla.org/places/sync;1"].getService(Ci.nsISupports);

View File

@ -39,10 +39,6 @@
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
function test_service_exists()
{
do_check_neq(PlacesBackground, null);
@ -87,10 +83,32 @@ function test_two_events_same_thread()
do_check_eq(event.thread1, event.thread2);
}
function test_places_background_shutdown_topic()
{
// Ensures that the places shutdown topic is dispatched before the thread is
// shutdown.
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver({
observe: function(aSubject, aTopic, aData)
{
// We should still be able to dispatch an event without throwing now!
PlacesBackground.dispatch({
run: function()
{
do_test_finished();
}
}, Ci.nsIEventTarget.DISPATCH_NORMAL);
}
}, "places-background-shutdown", false);
do_test_pending();
}
let tests = [
test_service_exists,
test_isOnCurrentThread,
test_two_events_same_thread,
test_places_background_shutdown_topic,
];
function run_test()

View File

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 ***** */
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const TEST_URI = "http://test.com/";
function run_test()
{
// First insert it
let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
let id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI),
bh.DEFAULT_INDEX, "test");
PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true, true),
Ci.nsIEventTarget.DISPATCH_NORMAL);
do_test_pending();
}

View File

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 ***** */
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const TEST_URI = "http://test.com/";
function run_test()
{
// First insert it
let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
let id = null;
bh.runInBatchMode({
runBatched: function(aUserData)
{
id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI),
bh.DEFAULT_INDEX, "test");
PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, false),
Ci.nsIEventTarget.DISPATCH_SYNC);
}
}, null);
do_check_neq(id, null);
PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true, true),
Ci.nsIEventTarget.DISPATCH_NORMAL);
do_test_pending();
}

View File

@ -0,0 +1,71 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 ***** */
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const TEST_URI = "http://test.com/";
const kSyncPrefName = "syncDBTableIntervalInSecs";
const SYNC_INTERVAL = 1;
function run_test()
{
// First set the preference for the timer to a small value
let prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("places.");
prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
// Now add the visit
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
hs.TRANSITION_TYPED, false, 0);
// Check the visit, but after enough time has passed for the DB flush service
// to have fired it's timer.
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback({
notify: function(aTimer)
{
PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true, true),
Ci.nsIEventTarget.DISPATCH_NORMAL);
}
}, (SYNC_INTERVAL * 1000) * 2, Ci.nsITimer.TYPE_ONE_SHOT);
do_test_pending();
}

View File

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 ***** */
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const TEST_URI = "http://test.com/";
function run_test()
{
// Now add the visit in batch mode
let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
let id = null;
bh.runInBatchMode({
runBatched: function(aUserData)
{
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
hs.TRANSITION_TYPED, false, 0);
PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, false),
Ci.nsIEventTarget.DISPATCH_SYNC);
}
}, null);
do_check_neq(id, null);
PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true, true),
Ci.nsIEventTarget.DISPATCH_NORMAL);
do_test_pending();
}

View File

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 ***** */
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const TEST_URI = "http://test.com/";
const MODIFIED_URI = "http://test.com/index.html";
function run_test()
{
// First insert it
let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
let id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI),
bh.DEFAULT_INDEX, "test");
// Dispatch the check synchronously so we don't modify the data before this
// test runs
PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true),
Ci.nsIEventTarget.DISPATCH_SYNC);
// Now modify the bookmark
bh.changeBookmarkURI(id, uri(MODIFIED_URI));
PlacesBackground.dispatch(new_test_bookmark_uri_event(id, MODIFIED_URI, true, true),
Ci.nsIEventTarget.DISPATCH_NORMAL);
do_test_pending();
}

View File

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 ***** */
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const TEST_URI = "http://test.com/";
const kSyncPrefName = "syncDBTableIntervalInSecs";
const SYNC_INTERVAL = 600; // ten minutes
function run_test()
{
// First set the preference for the timer to a really large value so it won't
// run before the test finishes.
let prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("places.");
prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
// Now add the visit
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
hs.TRANSITION_TYPED, false, 0);
// Notify that we are quitting the app - we should sync!
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.notifyObservers(null, "quit-application", null);
// Check the visit. The background thread should have joined with the main
// thread by now if everything is working correctly.
new_test_visit_uri_event(id, TEST_URI, true, false).run();
}

View File

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 test ensures that adding a bookmark (which has an implicit sync), then
* adding another one that has the same place, we end up with only one entry in
* moz_places.
*/
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const TEST_URI = "http://test.com/";
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
function run_test()
{
// Add the first bookmark
let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
let id1 = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI),
bh.DEFAULT_INDEX, "test");
// Ensure it was added
PlacesBackground.dispatch(new_test_bookmark_uri_event(id1, TEST_URI, true),
Ci.nsIEventTarget.DISPATCH_SYNC);
// Get the place_id
let stmt = db.createStatement(
"SELECT fk " +
"FROM moz_bookmarks " +
"WHERE id = ?"
);
stmt.bindInt64Parameter(0, id1);
do_check_true(stmt.executeStep());
let place_id = stmt.getInt64(0);
stmt.finalize();
stmt = null;
// Now we add another bookmark to a different folder
let id2 = bh.insertBookmark(bh.toolbarFolder, uri(TEST_URI),
bh.DEFAULT_INDEX, "test");
do_check_neq(id1, id2);
// Ensure it was added
PlacesBackground.dispatch(new_test_bookmark_uri_event(id2, TEST_URI, true),
Ci.nsIEventTarget.DISPATCH_SYNC);
// Check to make sure we have the same place_id
stmt = db.createStatement(
"SELECT * " +
"FROM moz_bookmarks " +
"WHERE id = ?1 " +
"AND fk = ?2"
);
stmt.bindInt64Parameter(0, id2);
stmt.bindInt64Parameter(1, place_id);
do_check_true(stmt.executeStep());
stmt.finalize();
stmt = null;
// finish_test() calls do_test_finished, so we call do_test_pending()...
do_test_pending();
finish_test();
}

View File

@ -0,0 +1,127 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 expandtab
* ***** 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 mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 test ensures that when adding a visit, then syncing, and adding another
* visit to the same url creates two visits and that we only end up with one
* entry in moz_places.
*/
Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
const TEST_URI = "http://test.com/";
const kSyncPrefName = "syncDBTableIntervalInSecs";
const SYNC_INTERVAL = 1;
function run_test()
{
// First set the preference for the timer to a small value
let prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("places.");
prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
// Now add the first visit
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
hs.TRANSITION_TYPED, false, 0);
// Check the visit, but after enough time has passed for the DB flush service
// to have fired it's timer.
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback({
notify: function(aTimer)
{
PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true),
Ci.nsIEventTarget.DISPATCH_SYNC);
// Get the place_id and pass it on
let db = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
let stmt = db.createStatement(
"SELECT place_id " +
"FROM moz_historyvisits " +
"WHERE id = ?"
);
stmt.bindInt64Parameter(0, id);
do_check_true(stmt.executeStep());
continue_test(id, stmt.getInt64(0));
stmt.finalize();
stmt = null;
}
}, (SYNC_INTERVAL * 1000) * 2, Ci.nsITimer.TYPE_ONE_SHOT);
do_test_pending();
}
function continue_test(aLastVisitId, aPlaceId)
{
// Now we add another visit
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
hs.TRANSITION_TYPED, false, 0);
do_check_neq(aLastVisitId, id);
// Check the visit, but after enough time has passed for the DB flush service
// to have fired it's timer.
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback({
notify: function(aTimer)
{
PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true),
Ci.nsIEventTarget.DISPATCH_SYNC);
// Check to make sure we have the same place_id
let db = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
let stmt = db.createStatement(
"SELECT * " +
"FROM moz_historyvisits " +
"WHERE id = ?1 " +
"AND place_id = ?2"
);
stmt.bindInt64Parameter(0, id);
stmt.bindInt64Parameter(1, aPlaceId);
do_check_true(stmt.executeStep());
stmt.finalize();
stmt = null;
finish_test();
}
}, (SYNC_INTERVAL * 1000) * 2, Ci.nsITimer.TYPE_ONE_SHOT);
}