From ced4389b1aafcd37370c319b71f0bd1b0dd8a933 Mon Sep 17 00:00:00 2001 From: "Mark Finkle ext:(%2C%20Shawn%20Wilsher%20%3Cme%40shawnwilsher.com%3E%2C%20Marco%20Bonardo%20%3Cmak77%40bonardo.net%3E)" Date: Wed, 25 Mar 2009 15:31:36 -0500 Subject: [PATCH] Bug 483980 - Allow history/bookmark observer components to register with a startup category This changeset allows for history and bookmark observers to be registered via the category manager. This means the bookmarks service does not have to be initialized at startup to register observers with it, as well as the history service. r=sdwilsh r=mak r=dietrich --- .../places/tests/unit/head_bookmarks.js | 10 ++ .../places/tests/unit/tail_bookmarks.js | 7 +- .../components/places/src/nsNavBookmarks.cpp | 63 ++++++------ .../components/places/src/nsNavBookmarks.h | 5 + .../components/places/src/nsNavHistory.cpp | 34 ++++--- toolkit/components/places/src/nsNavHistory.h | 17 +++- .../places/src/nsNavHistoryExpire.cpp | 7 +- .../components/places/src/nsPlacesDBFlush.js | 40 ++++++-- .../components/places/src/nsPlacesMacros.h | 50 ++++++++++ .../places/tests/autocomplete/head_000.js | 10 ++ .../tests/autocomplete/tail_autocomplete.js | 65 +++++++++++++ .../places/tests/bookmarks/head_bookmarks.js | 63 ++++++++++++ .../places/tests/bookmarks/tail_bookmarks.js | 29 +++++- .../places/tests/bookmarks/test_384228.js | 12 +++ .../places/tests/bookmarks/test_448584.js | 6 +- .../places/tests/queries/head_queries.js | 10 ++ .../places/tests/queries/tail_queries.js | 63 +++++++++++- .../components/places/tests/sync/head_sync.js | 12 ++- .../components/places/tests/sync/tail_sync.js | 65 +++++++++++++ .../places/tests/unit/head_bookmarks.js | 10 ++ .../places/tests/unit/nsDummyObserver.js | 97 +++++++++++++++++++ .../places/tests/unit/tail_bookmarks.js | 30 +++++- .../places/tests/unit/test_000_frecency.js | 1 + .../places/tests/unit/test_421180.js | 4 +- .../places/tests/unit/test_454977.js | 1 + .../places/tests/unit/test_bookmark_catobs.js | 81 ++++++++++++++++ .../places/tests/unit/test_history.js | 6 +- .../places/tests/unit/test_history_catobs.js | 81 ++++++++++++++++ .../places/tests/unit/test_migrateFrecency.js | 7 +- 29 files changed, 812 insertions(+), 74 deletions(-) create mode 100644 toolkit/components/places/src/nsPlacesMacros.h create mode 100644 toolkit/components/places/tests/autocomplete/tail_autocomplete.js create mode 100644 toolkit/components/places/tests/sync/tail_sync.js create mode 100644 toolkit/components/places/tests/unit/nsDummyObserver.js create mode 100644 toolkit/components/places/tests/unit/test_bookmark_catobs.js create mode 100644 toolkit/components/places/tests/unit/test_history_catobs.js diff --git a/browser/components/places/tests/unit/head_bookmarks.js b/browser/components/places/tests/unit/head_bookmarks.js index 5d0b12e96991..8fc132e49601 100644 --- a/browser/components/places/tests/unit/head_bookmarks.js +++ b/browser/components/places/tests/unit/head_bookmarks.js @@ -330,3 +330,13 @@ function dump_table(aName) stmt.finalize(); stmt = null; } + +/** + * Flushes any events in the event loop of the main thread. + */ +function flush_main_thread_events() +{ + let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); + while (tm.mainThread.hasPendingEvents()) + tm.mainThread.processNextEvent(false); +} diff --git a/browser/components/places/tests/unit/tail_bookmarks.js b/browser/components/places/tests/unit/tail_bookmarks.js index aedd64b3f6a7..6207283cccff 100644 --- a/browser/components/places/tests/unit/tail_bookmarks.js +++ b/browser/components/places/tests/unit/tail_bookmarks.js @@ -41,9 +41,7 @@ // event loop long before code like this would run. // Not doing so could cause us to close the connection before all tasks have // been completed, and that would crash badly. -let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); -while (tm.mainThread.hasPendingEvents()) - tm.mainThread.processNextEvent(false); +flush_main_thread_events(); // XPCShell doesn't dispatch quit-application, to ensure cleanup we have to // dispatch it after each test run. @@ -52,6 +50,9 @@ var os = Cc['@mozilla.org/observer-service;1']. os.notifyObservers(null, "quit-application-granted", null); os.notifyObservers(null, "quit-application", null); +// Run the event loop, since we enqueue some statement finalization. +flush_main_thread_events(); + // try to close the connection so we can remove places.sqlite var pip = Cc["@mozilla.org/browser/nav-history-service;1"]. getService(Ci.nsINavHistoryService). diff --git a/toolkit/components/places/src/nsNavBookmarks.cpp b/toolkit/components/places/src/nsNavBookmarks.cpp index e9a59465b787..9db718100797 100644 --- a/toolkit/components/places/src/nsNavBookmarks.cpp +++ b/toolkit/components/places/src/nsNavBookmarks.cpp @@ -54,6 +54,7 @@ #include "nsPlacesTriggers.h" #include "nsPlacesTables.h" #include "nsPlacesIndexes.h" +#include "nsPlacesMacros.h" const PRInt32 nsNavBookmarks::kFindBookmarksIndex_ID = 0; const PRInt32 nsNavBookmarks::kFindBookmarksIndex_Type = 1; @@ -88,7 +89,7 @@ nsNavBookmarks* nsNavBookmarks::sInstance = nsnull; nsNavBookmarks::nsNavBookmarks() : mItemCount(0), mRoot(0), mBookmarksRoot(0), mTagRoot(0), mToolbarFolder(0), mBatchLevel(0), - mBatchHasTransaction(PR_FALSE) + mBatchHasTransaction(PR_FALSE), mCanNotify(false), mCacheObservers("bookmark-observers") { NS_ASSERTION(!sInstance, "Multiple nsNavBookmarks instances!"); sInstance = this; @@ -125,6 +126,8 @@ nsNavBookmarks::Init() rv = transaction.Commit(); NS_ENSURE_SUCCESS(rv, rv); + mCanNotify = true; + // Add observers nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); @@ -1106,7 +1109,7 @@ nsNavBookmarks::InsertBookmark(PRInt64 aFolder, nsIURI *aItem, PRInt32 aIndex, AddBookmarkToHash(childID, 0); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemAdded(rowId, aFolder, index)) // If the bookmark has been added to a tag container, notify all @@ -1124,7 +1127,7 @@ nsNavBookmarks::InsertBookmark(PRInt64 aFolder, nsIURI *aItem, PRInt32 aIndex, if (bookmarks.Length()) { for (PRUint32 i = 0; i < bookmarks.Length(); i++) { - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"), PR_FALSE, EmptyCString())) } @@ -1172,7 +1175,7 @@ nsNavBookmarks::RemoveItem(PRInt64 aItemId) return NS_OK; } - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnBeforeItemRemoved(aItemId)) mozStorageTransaction transaction(mDBConn, PR_FALSE); @@ -1211,7 +1214,7 @@ nsNavBookmarks::RemoveItem(PRInt64 aItemId) NS_ENSURE_SUCCESS(rv, rv); } - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemRemoved(aItemId, folderId, childIndex)) if (itemType == TYPE_BOOKMARK) { @@ -1232,7 +1235,7 @@ nsNavBookmarks::RemoveItem(PRInt64 aItemId) if (bookmarks.Length()) { for (PRUint32 i = 0; i < bookmarks.Length(); i++) { - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"), PR_FALSE, EmptyCString())) } @@ -1381,7 +1384,7 @@ nsNavBookmarks::CreateContainerWithID(PRInt64 aItemId, PRInt64 aParent, rv = transaction.Commit(); NS_ENSURE_SUCCESS(rv, rv); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemAdded(id, aParent, index)) *aIndex = index; @@ -1446,7 +1449,7 @@ nsNavBookmarks::InsertSeparator(PRInt64 aParent, PRInt32 aIndex, rv = transaction.Commit(); NS_ENSURE_SUCCESS(rv, rv); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemAdded(rowId, aParent, index)) return NS_OK; @@ -1583,7 +1586,7 @@ nsNavBookmarks::RemoveFolder(PRInt64 aFolderId) { NS_ENSURE_TRUE(aFolderId != mRoot, NS_ERROR_INVALID_ARG); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnBeforeItemRemoved(aFolderId)) mozStorageTransaction transaction(mDBConn, PR_FALSE); @@ -1657,7 +1660,7 @@ nsNavBookmarks::RemoveFolder(PRInt64 aFolderId) mToolbarFolder = 0; } - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemRemoved(aFolderId, parent, index)) return NS_OK; @@ -1775,7 +1778,7 @@ nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolderId) folderChildrenInfo child = folderChildrenArray[i]; // Notify observers that we are about to remove this child. - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnBeforeItemRemoved(child.itemId)) if (child.itemType == TYPE_FOLDER) { @@ -1848,7 +1851,7 @@ nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolderId) for (PRInt32 i = folderChildrenArray.Length() - 1; i >= 0 ; i--) { folderChildrenInfo child = folderChildrenArray[i]; - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemRemoved(child.itemId, child.parentId, child.index)); @@ -1869,7 +1872,7 @@ nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolderId) if (bookmarks.Length()) { for (PRUint32 i = 0; i < bookmarks.Length(); i++) { - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"), PR_FALSE, EmptyCString())) } @@ -2026,7 +2029,7 @@ nsNavBookmarks::MoveItem(PRInt64 aItemId, PRInt64 aNewParent, PRInt32 aIndex) NS_ENSURE_SUCCESS(rv, rv); // notify bookmark observers - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemMoved(aItemId, oldParent, oldIndex, aNewParent, newIndex)) @@ -2101,7 +2104,7 @@ nsNavBookmarks::SetItemDateAdded(PRInt64 aItemId, PRTime aDateAdded) nsresult rv = SetItemDateInternal(mDBSetItemDateAdded, aItemId, aDateAdded); NS_ENSURE_SUCCESS(rv, rv); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(aItemId, NS_LITERAL_CSTRING("dateAdded"), PR_FALSE, nsPrintfCString(16, "%lld", aDateAdded))); return NS_OK; @@ -2132,7 +2135,7 @@ nsNavBookmarks::SetItemLastModified(PRInt64 aItemId, PRTime aLastModified) nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, aLastModified); NS_ENSURE_SUCCESS(rv, rv); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(aItemId, NS_LITERAL_CSTRING("lastModified"), PR_FALSE, nsPrintfCString(16, "%lld", aLastModified))); return NS_OK; @@ -2255,7 +2258,7 @@ nsNavBookmarks::SetItemTitle(PRInt64 aItemId, const nsACString &aTitle) rv = statement->Execute(); NS_ENSURE_SUCCESS(rv, rv); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(aItemId, NS_LITERAL_CSTRING("title"), PR_FALSE, aTitle)); return NS_OK; @@ -2633,7 +2636,7 @@ nsNavBookmarks::ChangeBookmarkURI(PRInt64 aBookmarkId, nsIURI *aNewURI) NS_ENSURE_SUCCESS(rv, rv); // Pass the new URI to OnItemChanged. - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("uri"), PR_FALSE, spec)) return NS_OK; @@ -2760,11 +2763,11 @@ nsNavBookmarks::SetItemIndex(PRInt64 aItemId, PRInt32 aNewIndex) // XXX (bug 484096) this is really inefficient and we should look into using // onItemChanged here! - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnBeforeItemRemoved(aItemId)) - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemRemoved(aItemId, parent, oldIndex)) - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemAdded(aItemId, parent, aNewIndex)) return NS_OK; @@ -2850,7 +2853,7 @@ nsNavBookmarks::SetKeywordForBookmark(PRInt64 aBookmarkId, const nsAString& aKey transaction.Commit(); // Pass the new keyword to OnItemChanged. - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("keyword"), PR_FALSE, NS_ConvertUTF16toUTF8(aKeyword))) @@ -2936,7 +2939,7 @@ nsNavBookmarks::BeginUpdateBatch() if (mBatchHasTransaction) conn->BeginTransaction(); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnBeginUpdateBatch()) } return NS_OK; @@ -2949,7 +2952,7 @@ nsNavBookmarks::EndUpdateBatch() if (mBatchHasTransaction) mDBConn->CommitTransaction(); mBatchHasTransaction = PR_FALSE; - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnEndUpdateBatch()) } return NS_OK; @@ -3022,7 +3025,7 @@ nsNavBookmarks::OnVisit(nsIURI *aURI, PRInt64 aVisitID, PRTime aTime, if (bookmarks.Length()) { for (PRUint32 i = 0; i < bookmarks.Length(); i++) - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemVisited(bookmarks[i], aVisitID, aTime)) } } @@ -3050,7 +3053,7 @@ nsNavBookmarks::OnDeleteURI(nsIURI *aURI) if (bookmarks.Length()) { for (PRUint32 i = 0; i < bookmarks.Length(); i ++) - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("cleartime"), PR_FALSE, EmptyCString())) } @@ -3099,7 +3102,7 @@ nsNavBookmarks::OnPageChanged(nsIURI *aURI, PRUint32 aWhat, NS_ENSURE_STATE(queries.Count() == 1); NS_ENSURE_STATE(queries[0]->Folders().Length() == 1); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(queries[0]->Folders()[0], NS_LITERAL_CSTRING("favicon"), PR_FALSE, NS_ConvertUTF16toUTF8(aValue))); } @@ -3111,7 +3114,7 @@ nsNavBookmarks::OnPageChanged(nsIURI *aURI, PRUint32 aWhat, if (bookmarks.Length()) { for (PRUint32 i = 0; i < bookmarks.Length(); i ++) - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("favicon"), PR_FALSE, NS_ConvertUTF16toUTF8(aValue))); } @@ -3142,7 +3145,7 @@ nsNavBookmarks::OnItemAnnotationSet(PRInt64 aItemId, const nsACString& aName) nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, PR_Now()); NS_ENSURE_SUCCESS(rv, rv); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString())); return NS_OK; @@ -3161,7 +3164,7 @@ nsNavBookmarks::OnItemAnnotationRemoved(PRInt64 aItemId, const nsACString& aName nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, PR_Now()); NS_ENSURE_SUCCESS(rv, rv); - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver, OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString())); return NS_OK; diff --git a/toolkit/components/places/src/nsNavBookmarks.h b/toolkit/components/places/src/nsNavBookmarks.h index 86eb42827550..d79d92ca9197 100644 --- a/toolkit/components/places/src/nsNavBookmarks.h +++ b/toolkit/components/places/src/nsNavBookmarks.h @@ -45,6 +45,7 @@ #include "nsNavHistory.h" #include "nsNavHistoryResult.h" // need for Int64 hashtable #include "nsToolkitCompsCID.h" +#include "nsCategoryCache.h" class nsIOutputStream; @@ -296,6 +297,10 @@ private: nsString mType; PRInt32 mIndex; }; + + // Used to enable and disable the observer notifications. + bool mCanNotify; + nsCategoryCache mCacheObservers; }; struct nsBookmarksUpdateBatcher diff --git a/toolkit/components/places/src/nsNavHistory.cpp b/toolkit/components/places/src/nsNavHistory.cpp index eaebf7d6f612..f4b1fb0bb2e7 100644 --- a/toolkit/components/places/src/nsNavHistory.cpp +++ b/toolkit/components/places/src/nsNavHistory.cpp @@ -53,6 +53,7 @@ #include "nsPlacesTables.h" #include "nsPlacesIndexes.h" #include "nsPlacesTriggers.h" +#include "nsPlacesMacros.h" #include "nsIArray.h" #include "nsTArray.h" @@ -444,7 +445,9 @@ nsNavHistory::nsNavHistory() : mBatchLevel(0), mNumVisitsForFrecency(10), mTagsFolder(-1), mInPrivateBrowsing(PRIVATEBROWSING_NOTINITED), - mDatabaseStatus(DATABASE_STATUS_OK) + mDatabaseStatus(DATABASE_STATUS_OK), + mCanNotify(true), + mCacheObservers("history-observers") { #ifdef LAZY_ADD mLazyTimerSet = PR_TRUE; @@ -2883,7 +2886,7 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRTime aTime, nsIURI* aReferringURI, PRUint32 added = 0; if (!hidden && aTransitionType != TRANSITION_EMBED && aTransitionType != TRANSITION_DOWNLOAD) { - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavHistoryObserver, OnVisit(aURI, *aVisitID, aTime, aSessionID, referringVisitID, aTransitionType, &added)); } @@ -4261,7 +4264,7 @@ nsNavHistory::BeginUpdateBatch() if (mBatchHasTransaction) mDBConn->BeginTransaction(); - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavHistoryObserver, OnBeginUpdateBatch()) } return NS_OK; @@ -4275,7 +4278,8 @@ nsNavHistory::EndUpdateBatch() if (mBatchHasTransaction) mDBConn->CommitTransaction(); mBatchHasTransaction = PR_FALSE; - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, OnEndUpdateBatch()) + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavHistoryObserver, + OnEndUpdateBatch()) } return NS_OK; } @@ -4582,15 +4586,16 @@ nsNavHistory::RemovePage(nsIURI *aURI) NS_ENSURE_ARG(aURI); // Before we remove, we have to notify our observers! - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, - OnBeforeDeleteURI(aURI)) + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, + nsINavHistoryObserver, OnBeforeDeleteURI(aURI)) nsIURI** URIs = &aURI; nsresult rv = RemovePages(URIs, 1, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); // Notify our observers that the URI has been removed. - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, OnDeleteURI(aURI)) + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, + nsINavHistoryObserver, OnDeleteURI(aURI)) return NS_OK; } @@ -4950,7 +4955,7 @@ nsNavHistory::HidePage(nsIURI *aURI) // notify observers, finish transaction first transaction.Commit(); - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavHistoryObserver, OnPageChanged(aURI, nsINavHistoryObserver::ATTRIBUTE_HIDDEN, EmptyString())) @@ -5609,7 +5614,7 @@ NS_IMETHODIMP nsNavHistory::NotifyOnPageExpired(nsIURI *aURI, PRTime aVisitTime, PRBool aWholeEntry) { - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavHistoryObserver, OnPageExpired(aURI, aVisitTime, aWholeEntry)); return NS_OK; } @@ -6858,6 +6863,14 @@ nsNavHistory::BookmarkIdToResultNode(PRInt64 aBookmarkId, nsNavHistoryQueryOptio return RowToResult(stmt, aOptions, aResult); } +void +nsNavHistory::SendPageChangedNotification(nsIURI* aURI, PRUint32 aWhat, + const nsAString& aValue) +{ + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavHistoryObserver, + OnPageChanged(aURI, aWhat, aValue)); +} + // nsNavHistory::TitleForDomain // // This computes the title for a given domain. Normally, this is just the @@ -6987,11 +7000,10 @@ nsNavHistory::SetPageTitleInternal(nsIURI* aURI, const nsAString& aTitle) NS_ENSURE_SUCCESS(rv, rv); // observers (have to check first if it's bookmarked) - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, + ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavHistoryObserver, OnTitleChanged(aURI, aTitle)) return NS_OK; - } nsresult diff --git a/toolkit/components/places/src/nsNavHistory.h b/toolkit/components/places/src/nsNavHistory.h index 7bffb3683c82..25874a0a3602 100644 --- a/toolkit/components/places/src/nsNavHistory.h +++ b/toolkit/components/places/src/nsNavHistory.h @@ -82,6 +82,7 @@ #include "nsTArray.h" #include "nsINavBookmarksService.h" #include "nsMaybeWeakPtr.h" +#include "nsCategoryCache.h" #include "nsNavHistoryExpire.h" #include "nsNavHistoryResult.h" @@ -303,11 +304,7 @@ public: // used by other places components to send history notifications (for example, // when the favicon has changed) void SendPageChangedNotification(nsIURI* aURI, PRUint32 aWhat, - const nsAString& aValue) - { - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, - OnPageChanged(aURI, aWhat, aValue)); - } + const nsAString& aValue); // current time optimization PRTime GetNow(); @@ -397,6 +394,12 @@ public: return NS_OK; } + /** + * Indicates if it is OK to notify history observers or not. + * + * @returns true if it is OK to notify, false otherwise. + */ + bool canNotify() { return mCanNotify; } private: ~nsNavHistory(); @@ -817,6 +820,10 @@ protected: PRBool mInPrivateBrowsing; PRUint16 mDatabaseStatus; + + // Used to enable and disable the observer notifications + bool mCanNotify; + nsCategoryCache mCacheObservers; }; /** diff --git a/toolkit/components/places/src/nsNavHistoryExpire.cpp b/toolkit/components/places/src/nsNavHistoryExpire.cpp index c9a70bf26111..c48b066664dc 100644 --- a/toolkit/components/places/src/nsNavHistoryExpire.cpp +++ b/toolkit/components/places/src/nsNavHistoryExpire.cpp @@ -47,6 +47,7 @@ #include "nsNetUtil.h" #include "nsIAnnotationService.h" #include "nsPrintfCString.h" +#include "nsPlacesMacros.h" struct nsNavHistoryExpireRecord { nsNavHistoryExpireRecord(mozIStorageStatement* statement); @@ -279,7 +280,8 @@ nsNavHistoryExpire::ClearHistory() // forcibly call the "on idle" timer here to do a little work // but the rest will happen on idle. - ENUMERATE_WEAKARRAY(mHistory->mObservers, nsINavHistoryObserver, + ENUMERATE_OBSERVERS(mHistory->canNotify(), mHistory->mCacheObservers, + mHistory->mObservers, nsINavHistoryObserver, OnClearHistory()) return NS_OK; @@ -384,7 +386,8 @@ nsNavHistoryExpire::ExpireItems(PRUint32 aNumToExpire, PRBool* aKeepGoing) // FIXME bug 325241 provide a way to observe hidden elements if (expiredVisits[i].hidden) continue; - ENUMERATE_WEAKARRAY(mHistory->mObservers, nsINavHistoryObserver, + ENUMERATE_OBSERVERS(mHistory->canNotify(), mHistory->mCacheObservers, + mHistory->mObservers, nsINavHistoryObserver, OnPageExpired(uri, expiredVisits[i].visitDate, expiredVisits[i].erased)); } diff --git a/toolkit/components/places/src/nsPlacesDBFlush.js b/toolkit/components/places/src/nsPlacesDBFlush.js index 809c2764167a..f32e4131a80b 100644 --- a/toolkit/components/places/src/nsPlacesDBFlush.js +++ b/toolkit/components/places/src/nsPlacesDBFlush.js @@ -103,10 +103,6 @@ function nsPlacesDBFlush() } // Register observers - this._bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. - getService(Ci.nsINavBookmarksService); - this._bs.addObserver(this, false); - this._os = Cc["@mozilla.org/observer-service;1"]. getService(Ci.nsIObserverService); this._os.addObserver(this, kQuitApplication, false); @@ -140,6 +136,12 @@ function nsPlacesDBFlush() return this._hsn = Cc["@mozilla.org/browser/nav-history-service;1"]. getService(Ci.nsPIPlacesHistoryListenersNotifier); }); + + this.__defineGetter__("_bs", function() { + delete this._bs; + return this._bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + }); } nsPlacesDBFlush.prototype = { @@ -149,7 +151,6 @@ nsPlacesDBFlush.prototype = { observe: function DBFlush_observe(aSubject, aTopic, aData) { if (aTopic == kQuitApplication) { - this._bs.removeObserver(this); this._os.removeObserver(this, kQuitApplication); let (pb2 = this._prefs.QueryInterface(Ci.nsIPrefBranch2)) { pb2.removeObserver(kSyncPrefName, this); @@ -245,6 +246,24 @@ nsPlacesDBFlush.prototype = { onItemVisited: function() { }, onItemMoved: function() { }, + ////////////////////////////////////////////////////////////////////////////// + //// nsINavHistoryObserver + + // We currently only use the history observer to know when the history service + // is activated. At that point, we actually get initialized, and our timer + // to sync history is added. + + // These methods share the name of the ones on nsINavBookmarkObserver, so + // the implementations can be found above. + //onBeginUpdateBatch: function() { }, + //onEndUpdateBatch: function() { }, + onVisit: function(aURI, aVisitID, aTime, aSessionID, aReferringID, aTransitionType) { }, + onTitleChanged: function(aURI, aPageTitle) { }, + onDeleteURI: function(aURI) { }, + onClearHistory: function() { }, + onPageChanged: function(aURI, aWhat, aValue) { }, + onPageExpired: function(aURI, aVisitTime, aWholeEntry) { }, + ////////////////////////////////////////////////////////////////////////////// //// nsITimerCallback @@ -458,13 +477,18 @@ nsPlacesDBFlush.prototype = { 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", - }], + + // Registering in these categories makes us get initialized when either of + // those listeners would be notified. + _xpcom_categories: [ + { category: "bookmark-observers" }, + { category: "history-observers" }, + ], QueryInterface: XPCOMUtils.generateQI([ Ci.nsIObserver, Ci.nsINavBookmarkObserver, + Ci.nsINavHistoryObserver, Ci.nsITimerCallback, Ci.mozIStorageStatementCallback, ]) diff --git a/toolkit/components/places/src/nsPlacesMacros.h b/toolkit/components/places/src/nsPlacesMacros.h new file mode 100644 index 000000000000..aa487e1754df --- /dev/null +++ b/toolkit/components/places/src/nsPlacesMacros.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; 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 Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "prtypes.h" +// Call a method on each observer in a category cache, then call the same +// method on the observer array. + +#define ENUMERATE_OBSERVERS(canFire, cache, array, type, method) \ + PR_BEGIN_MACRO \ + if (canFire) { \ + const nsCOMArray &entries = cache.GetEntries(); \ + for (PRInt32 idx = 0; idx < entries.Count(); ++idx) \ + entries[idx]->method; \ + ENUMERATE_WEAKARRAY(array, type, method) \ + } \ + PR_END_MACRO; diff --git a/toolkit/components/places/tests/autocomplete/head_000.js b/toolkit/components/places/tests/autocomplete/head_000.js index 0423d71eaa95..33a8aea4ec38 100644 --- a/toolkit/components/places/tests/autocomplete/head_000.js +++ b/toolkit/components/places/tests/autocomplete/head_000.js @@ -139,3 +139,13 @@ function dump_table(aName) stmt.finalize(); stmt = null; } + +/** + * Flushes any events in the event loop of the main thread. + */ +function flush_main_thread_events() +{ + let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); + while (tm.mainThread.hasPendingEvents()) + tm.mainThread.processNextEvent(false); +} diff --git a/toolkit/components/places/tests/autocomplete/tail_autocomplete.js b/toolkit/components/places/tests/autocomplete/tail_autocomplete.js new file mode 100644 index 000000000000..820caf6a79bb --- /dev/null +++ b/toolkit/components/places/tests/autocomplete/tail_autocomplete.js @@ -0,0 +1,65 @@ +/* ***** 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 + * Mozilla.org + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Marco Bonardo + * + * 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 ***** */ + +// put cleanup of the bookmarks test here. + +// Run the event loop to be more like the browser, which normally runs the +// event loop long before code like this would run. +// Not doing so could cause us to close the connection before all tasks have +// been completed, and that would crash badly. +flush_main_thread_events(); + +// XPCShell doesn't dispatch quit-application, to ensure cleanup we have to +// dispatch it after each test run. +var os = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); +os.notifyObservers(null, "quit-application-granted", null); +os.notifyObservers(null, "quit-application", null); + +// Run the event loop, since we enqueue some statement finalization. +flush_main_thread_events(); + +// try to close the connection so we can remove places.sqlite +var pip = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService). + QueryInterface(Ci.nsPIPlacesDatabase); +if (pip.DBConnection.connectionReady) { + pip.commitPendingChanges(); + pip.finalizeInternalStatements(); + pip.DBConnection.close(); + do_check_false(pip.DBConnection.connectionReady); +} diff --git a/toolkit/components/places/tests/bookmarks/head_bookmarks.js b/toolkit/components/places/tests/bookmarks/head_bookmarks.js index 8e694569bfe7..8ed221ebfd5e 100644 --- a/toolkit/components/places/tests/bookmarks/head_bookmarks.js +++ b/toolkit/components/places/tests/bookmarks/head_bookmarks.js @@ -94,6 +94,59 @@ function clearDB() { } 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; +} + /* * Removes all bookmarks and checks for correct cleanup */ @@ -126,3 +179,13 @@ function check_no_bookmarks() { do_check_eq(root.childCount, 0); root.containerOpen = false; } + +/** + * Flushes any events in the event loop of the main thread. + */ +function flush_main_thread_events() +{ + let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); + while (tm.mainThread.hasPendingEvents()) + tm.mainThread.processNextEvent(false); +} diff --git a/toolkit/components/places/tests/bookmarks/tail_bookmarks.js b/toolkit/components/places/tests/bookmarks/tail_bookmarks.js index 5165a840b1d3..116ba5054cb3 100644 --- a/toolkit/components/places/tests/bookmarks/tail_bookmarks.js +++ b/toolkit/components/places/tests/bookmarks/tail_bookmarks.js @@ -20,6 +20,7 @@ * * Contributor(s): * Dietrich Ayala + * Marco Bonardo * * 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 @@ -37,5 +38,29 @@ // put cleanup of the bookmarks test here. -// remove bookmarks file -//clearDB(); +// Run the event loop to be more like the browser, which normally runs the +// event loop long before code like this would run. +// Not doing so could cause us to close the connection before all tasks have +// been completed, and that would crash badly. +flush_main_thread_events(); + +// XPCShell doesn't dispatch quit-application, to ensure cleanup we have to +// dispatch it after each test run. +var os = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); +os.notifyObservers(null, "quit-application-granted", null); +os.notifyObservers(null, "quit-application", null); + +// Run the event loop, since we enqueue some statement finalization. +flush_main_thread_events(); + +// try to close the connection so we can remove places.sqlite +var pip = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService). + QueryInterface(Ci.nsPIPlacesDatabase); +if (pip.DBConnection.connectionReady) { + pip.commitPendingChanges(); + pip.finalizeInternalStatements(); + pip.DBConnection.close(); + do_check_false(pip.DBConnection.connectionReady); +} diff --git a/toolkit/components/places/tests/bookmarks/test_384228.js b/toolkit/components/places/tests/bookmarks/test_384228.js index 30816cdf0765..480e56a46308 100644 --- a/toolkit/components/places/tests/bookmarks/test_384228.js +++ b/toolkit/components/places/tests/bookmarks/test_384228.js @@ -59,24 +59,34 @@ function run_test() { // test querying for bookmarks in multiple folders var testFolder1 = bmsvc.createFolder(root, "bug 384228 test folder 1", bmsvc.DEFAULT_INDEX); + do_check_eq(bmsvc.getItemIndex(testFolder1), 0); var testFolder2 = bmsvc.createFolder(root, "bug 384228 test folder 2", bmsvc.DEFAULT_INDEX); + do_check_eq(bmsvc.getItemIndex(testFolder2), 1); var testFolder3 = bmsvc.createFolder(root, "bug 384228 test folder 3", bmsvc.DEFAULT_INDEX); + do_check_eq(bmsvc.getItemIndex(testFolder3), 2); var b1 = bmsvc.insertBookmark(testFolder1, uri("http://foo.tld/"), bmsvc.DEFAULT_INDEX, "title b1 (folder 1)"); + do_check_eq(bmsvc.getItemIndex(b1), 0); var b2 = bmsvc.insertBookmark(testFolder1, uri("http://foo.tld/"), bmsvc.DEFAULT_INDEX, "title b2 (folder 1)"); + do_check_eq(bmsvc.getItemIndex(b2), 1); var b3 = bmsvc.insertBookmark(testFolder2, uri("http://foo.tld/"), bmsvc.DEFAULT_INDEX, "title b3 (folder 2)"); + do_check_eq(bmsvc.getItemIndex(b3), 0); var b4 = bmsvc.insertBookmark(testFolder3, uri("http://foo.tld/"), bmsvc.DEFAULT_INDEX, "title b4 (folder 3)"); + do_check_eq(bmsvc.getItemIndex(b4), 0); // also test recursive search var testFolder1_1 = bmsvc.createFolder(testFolder1, "bug 384228 test folder 1.1", bmsvc.DEFAULT_INDEX); + do_check_eq(bmsvc.getItemIndex(testFolder1_1), 2); var b5 = bmsvc.insertBookmark(testFolder1_1, uri("http://a1.com/"), bmsvc.DEFAULT_INDEX, "title b5 (folder 1.1)"); + do_check_eq(bmsvc.getItemIndex(b5), 0); + var options = histsvc.getNewQueryOptions(); var query = histsvc.getNewQuery(); query.searchTerms = "title"; @@ -94,4 +104,6 @@ function run_test() { do_check_eq(rootNode.getChild(1).itemId, b2); do_check_eq(rootNode.getChild(2).itemId, b3); do_check_eq(rootNode.getChild(3).itemId, b5); + + rootNode.containerOpen = false; } diff --git a/toolkit/components/places/tests/bookmarks/test_448584.js b/toolkit/components/places/tests/bookmarks/test_448584.js index e9359931535a..afa229977088 100644 --- a/toolkit/components/places/tests/bookmarks/test_448584.js +++ b/toolkit/components/places/tests/bookmarks/test_448584.js @@ -121,7 +121,11 @@ function run_test() { var sql = "UPDATE moz_bookmarks SET fk = 1337 WHERE id = ?1"; var stmt = mDBConn.createStatement(sql); stmt.bindUTF8StringParameter(0, aTest._itemId); - stmt.execute(); + try { + stmt.execute(); + } finally { + stmt.finalize(); + } }); // export json to file diff --git a/toolkit/components/places/tests/queries/head_queries.js b/toolkit/components/places/tests/queries/head_queries.js index 318e54f248f8..d9767cd0ed45 100644 --- a/toolkit/components/places/tests/queries/head_queries.js +++ b/toolkit/components/places/tests/queries/head_queries.js @@ -533,3 +533,13 @@ function dump_table(aName) stmt.finalize(); stmt = null; } + +/** + * Flushes any events in the event loop of the main thread. + */ +function flush_main_thread_events() +{ + let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); + while (tm.mainThread.hasPendingEvents()) + tm.mainThread.processNextEvent(false); +} diff --git a/toolkit/components/places/tests/queries/tail_queries.js b/toolkit/components/places/tests/queries/tail_queries.js index f4488b62c215..bc8f7042da13 100644 --- a/toolkit/components/places/tests/queries/tail_queries.js +++ b/toolkit/components/places/tests/queries/tail_queries.js @@ -1 +1,62 @@ -clearDB(); +/* ***** 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 + * Mozilla.org + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Marco Bonardo + * + * 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 ***** */ + +// put cleanup of the bookmarks test here. + +// XPCShell doesn't dispatch quit-application, to ensure cleanup we have to +// dispatch it after each test run. +var os = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); +os.notifyObservers(null, "quit-application-granted", null); +os.notifyObservers(null, "quit-application", null); + +// Run the event loop to be more like the browser, which normally runs the +// event loop long before code like this would run. +// Not doing so could cause us to close the connection before all tasks have +// been completed, and that would crash badly. +flush_main_thread_events(); + +// try to close the connection so we can remove places.sqlite +var pip = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService). + QueryInterface(Ci.nsPIPlacesDatabase); +if (pip.DBConnection.connectionReady) { + pip.commitPendingChanges(); + pip.finalizeInternalStatements(); + pip.DBConnection.close(); + do_check_false(pip.DBConnection.connectionReady); +} diff --git a/toolkit/components/places/tests/sync/head_sync.js b/toolkit/components/places/tests/sync/head_sync.js index b00967730465..9d0ba3f7ee38 100644 --- a/toolkit/components/places/tests/sync/head_sync.js +++ b/toolkit/components/places/tests/sync/head_sync.js @@ -272,6 +272,12 @@ function DBConn() return dbConn; } -// 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); +/** + * Flushes any events in the event loop of the main thread. + */ +function flush_main_thread_events() +{ + let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); + while (tm.mainThread.hasPendingEvents()) + tm.mainThread.processNextEvent(false); +} diff --git a/toolkit/components/places/tests/sync/tail_sync.js b/toolkit/components/places/tests/sync/tail_sync.js new file mode 100644 index 000000000000..820caf6a79bb --- /dev/null +++ b/toolkit/components/places/tests/sync/tail_sync.js @@ -0,0 +1,65 @@ +/* ***** 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 + * Mozilla.org + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Marco Bonardo + * + * 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 ***** */ + +// put cleanup of the bookmarks test here. + +// Run the event loop to be more like the browser, which normally runs the +// event loop long before code like this would run. +// Not doing so could cause us to close the connection before all tasks have +// been completed, and that would crash badly. +flush_main_thread_events(); + +// XPCShell doesn't dispatch quit-application, to ensure cleanup we have to +// dispatch it after each test run. +var os = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); +os.notifyObservers(null, "quit-application-granted", null); +os.notifyObservers(null, "quit-application", null); + +// Run the event loop, since we enqueue some statement finalization. +flush_main_thread_events(); + +// try to close the connection so we can remove places.sqlite +var pip = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService). + QueryInterface(Ci.nsPIPlacesDatabase); +if (pip.DBConnection.connectionReady) { + pip.commitPendingChanges(); + pip.finalizeInternalStatements(); + pip.DBConnection.close(); + do_check_false(pip.DBConnection.connectionReady); +} diff --git a/toolkit/components/places/tests/unit/head_bookmarks.js b/toolkit/components/places/tests/unit/head_bookmarks.js index 8733cad40cd2..4d22fb9d153b 100644 --- a/toolkit/components/places/tests/unit/head_bookmarks.js +++ b/toolkit/components/places/tests/unit/head_bookmarks.js @@ -206,3 +206,13 @@ function finish_test() os.notifyObservers(null, "quit-application", null); do_test_finished(); } + +/** + * Flushes any events in the event loop of the main thread. + */ +function flush_main_thread_events() +{ + let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); + while (tm.mainThread.hasPendingEvents()) + tm.mainThread.processNextEvent(false); +} diff --git a/toolkit/components/places/tests/unit/nsDummyObserver.js b/toolkit/components/places/tests/unit/nsDummyObserver.js new file mode 100644 index 000000000000..22678df3f9cb --- /dev/null +++ b/toolkit/components/places/tests/unit/nsDummyObserver.js @@ -0,0 +1,97 @@ +/* ***** 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 unit test code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle (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"); + +const Cc = Components.classes; +const Ci = Components.interfaces; + +// Dummy boomark/history observer +function DummyObserver() { + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + os.notifyObservers(null, "dummy-observer-created", null); +} + +DummyObserver.prototype = { + // history observer + onBeginUpdateBatch: function() {}, + onEndUpdateBatch: function() {}, + onVisit: function(aURI, aVisitID, aTime, aSessionID, aReferringID, aTransitionType) { + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + os.notifyObservers(null, "dummy-observer-visited", null); + }, + onTitleChanged: function(aURI, aPageTitle) {}, + onDeleteURI: function(aURI) {}, + onClearHistory: function() {}, + onPageChanged: function(aURI, aWhat, aValue) {}, + onPageExpired: function(aURI, aVisitTime, aWholeEntry) {}, + + // bookmark observer + //onBeginUpdateBatch: function() {}, + //onEndUpdateBatch: function() {}, + onItemAdded: function(aItemId, aParentId, aIndex) { + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + os.notifyObservers(null, "dummy-observer-item-added", null); + }, + onItemChanged: function (aItemId, aProperty, aIsAnnotationProperty, aValue) {}, + onBeforeItemRemoved: function() {}, + onItemRemoved: function() {}, + onItemVisited: function() {}, + onItemMoved: function() {}, + + classDescription: "Dummy observer used to test category observers", + classID: Components.ID("62e221d3-68c3-4e1a-8943-a27beb5005fe"), + contractID: "@mozilla.org/places/test/dummy-observer;1", + + // Registering in these categories makes us get initialized when either of + // those listeners would be notified. + _xpcom_categories: [ + { category: "bookmark-observers" }, + { category: "history-observers" } + ], + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsINavBookmarkObserver, + Ci.nsINavHistoryObserver, + ]) +}; + +function NSGetModule(compMgr, fileSpec) { + return XPCOMUtils.generateModule([DummyObserver]); +} diff --git a/toolkit/components/places/tests/unit/tail_bookmarks.js b/toolkit/components/places/tests/unit/tail_bookmarks.js index db9548ae64c4..06ebacd727d9 100644 --- a/toolkit/components/places/tests/unit/tail_bookmarks.js +++ b/toolkit/components/places/tests/unit/tail_bookmarks.js @@ -20,6 +20,7 @@ * * Contributor(s): * Dietrich Ayala + * Marco Bonardo * * 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 @@ -37,5 +38,30 @@ // put cleanup of the bookmarks test here. -// remove bookmarks file -clearDB(); +// Run the event loop to be more like the browser, which normally runs the +// event loop long before code like this would run. +// Not doing so could cause us to close the connection before all tasks have +// been completed, and that would crash badly. +flush_main_thread_events(); + +// XPCShell doesn't dispatch quit-application, to ensure cleanup we have to +// dispatch it after each test run. +var os = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); +os.notifyObservers(null, "quit-application-granted", null); +os.notifyObservers(null, "quit-application", null); + +// Run the event loop, since we enqueue some statement finalization. +flush_main_thread_events(); + +// try to close the connection so we can remove places.sqlite +var pip = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService). + QueryInterface(Ci.nsPIPlacesDatabase); +if (pip.DBConnection.connectionReady) { + pip.commitPendingChanges(); + pip.finalizeInternalStatements(); + pip.DBConnection.close(); + do_check_false(pip.DBConnection.connectionReady); +} + diff --git a/toolkit/components/places/tests/unit/test_000_frecency.js b/toolkit/components/places/tests/unit/test_000_frecency.js index 8ac447943aae..984dcf4fd274 100644 --- a/toolkit/components/places/tests/unit/test_000_frecency.js +++ b/toolkit/components/places/tests/unit/test_000_frecency.js @@ -283,6 +283,7 @@ function run_test() { // undefined), so check if frecency matches. This is okay because we // can still ensure the correct number of expected frecencies. let getFrecency = function(aURL) aURL.match(/frecency:(-?\d+)$/)[1]; + print("### searchURL: '"+searchURL+"', expectedURL: '"+expectedURL+"'"); do_check_eq(getFrecency(searchURL), getFrecency(expectURL)); } } diff --git a/toolkit/components/places/tests/unit/test_421180.js b/toolkit/components/places/tests/unit/test_421180.js index 22b06d5752cd..e8309da02979 100644 --- a/toolkit/components/places/tests/unit/test_421180.js +++ b/toolkit/components/places/tests/unit/test_421180.js @@ -79,7 +79,7 @@ function run_test() { var stmt = mDBConn.createStatement(sql); stmt.bindUTF8StringParameter(0, keyword); do_check_false(stmt.executeStep()); - + stmt.finalize(); // TEST 2 // 1. add 2 bookmarks @@ -106,5 +106,5 @@ function run_test() { var stmt = mDBConn.createStatement(sql); stmt.bindUTF8StringParameter(0, keyword); do_check_true(stmt.executeStep()); - + stmt.finalize(); } diff --git a/toolkit/components/places/tests/unit/test_454977.js b/toolkit/components/places/tests/unit/test_454977.js index 68c63c96bcc4..57c0b028c60b 100644 --- a/toolkit/components/places/tests/unit/test_454977.js +++ b/toolkit/components/places/tests/unit/test_454977.js @@ -65,6 +65,7 @@ function add_visit(aURI, aVisitDate, aVisitType) { stmt.bindInt64Parameter(0, visitId); do_check_true(stmt.executeStep()); var placeId = stmt.getInt64(0); + stmt.finalize(); do_check_true(placeId > 0); return placeId; } diff --git a/toolkit/components/places/tests/unit/test_bookmark_catobs.js b/toolkit/components/places/tests/unit/test_bookmark_catobs.js new file mode 100644 index 000000000000..10cdd2349052 --- /dev/null +++ b/toolkit/components/places/tests/unit/test_bookmark_catobs.js @@ -0,0 +1,81 @@ +/* ***** 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 unit test code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle (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"); + +// Get services. +let bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); +let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + +let gDummyCreated = false; +let gDummyAdded = false; + +let observer = { + observe: function(subject, topic, data) { + if (topic == "dummy-observer-created") + gDummyCreated = true; + else if (topic == "dummy-observer-item-added") + gDummyAdded = true; + }, + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIObserver, + Ci.nsISupportsWeakReference, + ]) +}; + +function verify() { + do_check_true(gDummyCreated); + do_check_true(gDummyAdded); + do_test_finished(); +} + +// main +function run_test() { + do_load_module("nsDummyObserver.js"); + + os.addObserver(observer, "dummy-observer-created", true); + os.addObserver(observer, "dummy-observer-item-added", true); + + // Add a bookmark + bs.insertBookmark(bs.unfiledBookmarksFolder, uri("http://typed.mozilla.org"), + bs.DEFAULT_INDEX, "bookmark"); + + do_test_pending(); + do_timeout(1000, "verify();"); +} diff --git a/toolkit/components/places/tests/unit/test_history.js b/toolkit/components/places/tests/unit/test_history.js index c0c6e53a54da..a2486561d2a0 100644 --- a/toolkit/components/places/tests/unit/test_history.js +++ b/toolkit/components/places/tests/unit/test_history.js @@ -198,11 +198,15 @@ function run_test() { // get direct db connection var db = histsvc.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection; var q = "SELECT id FROM moz_bookmarks"; + var statement; try { - var statement = db.createStatement(q); + statement = db.createStatement(q); } catch(ex) { do_throw("bookmarks table does not have id field, schema is too old!"); } + finally { + statement.finalize(); + } // bug 394741 - regressed history text searches add_visit(uri("http://mozilla.com")); diff --git a/toolkit/components/places/tests/unit/test_history_catobs.js b/toolkit/components/places/tests/unit/test_history_catobs.js new file mode 100644 index 000000000000..a158b0cebba2 --- /dev/null +++ b/toolkit/components/places/tests/unit/test_history_catobs.js @@ -0,0 +1,81 @@ +/* ***** 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 unit test code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Finkle (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"); + +// Get services. +let hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); +let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + +let gDummyCreated = false; +let gDummyVisited = false; + +let observer = { + observe: function(subject, topic, data) { + if (topic == "dummy-observer-created") + gDummyCreated = true; + else if (topic == "dummy-observer-visited") + gDummyVisited = true; + }, + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIObserver, + Ci.nsISupportsWeakReference, + ]) +}; + +function verify() { + do_check_true(gDummyCreated); + do_check_true(gDummyVisited); + do_test_finished(); +} + +// main +function run_test() { + do_load_module("nsDummyObserver.js"); + + os.addObserver(observer, "dummy-observer-created", true); + os.addObserver(observer, "dummy-observer-visited", true); + + // Add a visit + hs.addVisit(uri("http://typed.mozilla.org"), Date.now(), null, + hs.TRANSITION_TYPED, false, 0); + + do_test_pending(); + do_timeout(1000, "verify();"); +} diff --git a/toolkit/components/places/tests/unit/test_migrateFrecency.js b/toolkit/components/places/tests/unit/test_migrateFrecency.js index 1358916e45e8..4dd087401221 100644 --- a/toolkit/components/places/tests/unit/test_migrateFrecency.js +++ b/toolkit/components/places/tests/unit/test_migrateFrecency.js @@ -71,9 +71,9 @@ function run_test() { deleteMork(); _("Now that places has migrated, check that it calculated frecencies"); - places.DBConnection.createStatement( - "SELECT COUNT(*) FROM moz_places_view WHERE frecency < 0"). - executeAsync({ + var stmt = places.DBConnection.createStatement( + "SELECT COUNT(*) FROM moz_places_view WHERE frecency < 0"); + stmt.executeAsync({ handleResult: function(results) { _("Should always get a result from COUNT(*)"); let row = results.getNextRow(); @@ -85,6 +85,7 @@ function run_test() { handleCompletion: do_test_finished, handleError: do_throw, }); + stmt.finalize(); break; } },