--- a/toolkit/components/places/Database.h
+++ b/toolkit/components/places/Database.h
@@ -39,16 +39,22 @@
#define TOPIC_PLACES_WILL_CLOSE_CONNECTION "places-will-close-connection"
// Fired when the connection has gone, nothing will work from now on.
#define TOPIC_PLACES_CONNECTION_CLOSED "places-connection-closed"
// Simulate profile-before-change. This topic may only be used by
// calling `observe` directly on the database. Used for testing only.
#define TOPIC_SIMULATE_PLACES_SHUTDOWN "test-simulate-places-shutdown"
+// A pref that controls whether testing conveniences are enabled. If true,
+// the annotation, bookmark, favicon, and history services will not hold
+// strong references to the database singleton, allowing tests to completely
+// shut down and restart Places.
+#define PREF_DATABASE_TESTING_ENABLED "places.database.testing.enabled"
+
class nsIRunnable;
namespace mozilla {
namespace places {
enum JournalMode {
// Default SQLite journal mode.
JOURNAL_DELETE = 0
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -3,16 +3,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "nsXULAppAPI.h"
#include "History.h"
#include "nsNavHistory.h"
#include "nsNavBookmarks.h"
@@ -1865,28 +1866,31 @@ StoreAndNotifyEmbedVisit(VisitData& aPla
////////////////////////////////////////////////////////////////////////////////
//// History
History* History::gService = nullptr;
History::History()
: mShuttingDown(false)
+ , mTestingEnabled(false)
, mShutdownMutex("History::mShutdownMutex")
, mObservers(VISIT_OBSERVERS_INITIAL_CACHE_LENGTH)
, mRecentlyVisitedURIs(RECENTLY_VISITED_URIS_SIZE)
{
NS_ASSERTION(!gService, "Ruh-roh! This service has already been created!");
gService = this;
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
NS_WARNING_ASSERTION(os, "Observer service was not found!");
if (os) {
(void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, false);
}
+
+ mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
}
History::~History()
{
UnregisterWeakMemoryReporter(this);
gService = nullptr;
@@ -2288,26 +2292,40 @@ History::GetSingleton()
NS_ENSURE_TRUE(gService, nullptr);
gService->InitMemoryReporter();
}
NS_ADDREF(gService);
return gService;
}
+already_AddRefed<Database>
+History::GetDatabase()
+{
+ if (mShuttingDown) {
+ return nullptr;
+ }
+ RefPtr<Database> DB;
+ if (mTestingEnabled) {
+ DB = Database::GetDatabase();
+ } else {
+ if (!mDB) {
+ mDB = Database::GetDatabase();
+ }
+ DB = mDB;
+ }
+ return DB.forget();
+}
+
mozIStorageConnection*
History::GetDBConn()
{
- if (mShuttingDown)
- return nullptr;
- if (!mDB) {
- mDB = Database::GetDatabase();
- NS_ENSURE_TRUE(mDB, nullptr);
- }
- return mDB->MainConn();
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_TRUE(DB, nullptr);
+ return DB->MainConn();
}
void
History::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
// Prevent other threads from scheduling uses of the DB while we mark
--- a/toolkit/components/places/History.h
+++ b/toolkit/components/places/History.h
@@ -105,27 +105,25 @@ public:
* manager only.
*/
static History* GetSingleton();
template<int N>
already_AddRefed<mozIStorageStatement>
GetStatement(const char (&aQuery)[N])
{
- mozIStorageConnection* dbConn = GetDBConn();
- NS_ENSURE_TRUE(dbConn, nullptr);
- return mDB->GetStatement(aQuery);
+ RefPtr<mozilla::places::Database> DB = GetDatabase();
+ return DB->GetStatement(aQuery);
}
already_AddRefed<mozIStorageStatement>
GetStatement(const nsACString& aQuery)
{
- mozIStorageConnection* dbConn = GetDBConn();
- NS_ENSURE_TRUE(dbConn, nullptr);
- return mDB->GetStatement(aQuery);
+ RefPtr<mozilla::places::Database> DB = GetDatabase();
+ return DB->GetStatement(aQuery);
}
bool IsShuttingDown() const {
return mShuttingDown;
}
Mutex& GetShutdownMutex() {
return mShutdownMutex;
}
@@ -136,16 +134,18 @@ public:
*/
void AppendToRecentlyVisitedURIs(nsIURI* aURI);
private:
virtual ~History();
void InitMemoryReporter();
+ already_AddRefed<Database> GetDatabase();
+
/**
* Obtains a read-write database connection.
*/
mozIStorageConnection* GetDBConn();
/**
* The database handle. This is initialized lazily by the first call to
* GetDBConn(), so never use it directly, or, if you really need, always
@@ -159,16 +159,18 @@ private:
* Remove any memory references to tasks and do not take on any more.
*/
void Shutdown();
static History* gService;
// Ensures new tasks aren't started on destruction.
bool mShuttingDown;
+ // Controls whether we hold a strong reference to the database handle.
+ bool mTestingEnabled;
// This mutex guards mShuttingDown. Code running in other threads that might
// schedule tasks that use the database should grab it and check the value of
// mShuttingDown. If we are already shutting down, the code must gracefully
// avoid using the db. If we are not, the lock will prevent shutdown from
// starting in an unexpected moment.
Mutex mShutdownMutex;
typedef nsTObserverArray<mozilla::dom::Link* > ObserverArray;
--- a/toolkit/components/places/nsAnnotationService.cpp
+++ b/toolkit/components/places/nsAnnotationService.cpp
@@ -1,14 +1,15 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
+#include "mozilla/Preferences.h"
#include "nsAnnotationService.h"
#include "nsNavHistory.h"
#include "nsPlacesTables.h"
#include "nsPlacesIndexes.h"
#include "nsPlacesMacros.h"
#include "Helpers.h"
@@ -115,56 +116,77 @@ PLACES_FACTORY_SINGLETON_IMPLEMENTATION(
NS_IMPL_ISUPPORTS(nsAnnotationService
, nsIAnnotationService
, nsIObserver
, nsISupportsWeakReference
)
nsAnnotationService::nsAnnotationService()
- : mHasSessionAnnotations(false)
+ : mTestingEnabled(false)
+ , mHasSessionAnnotations(false)
{
NS_ASSERTION(!gAnnotationService,
"Attempting to create two instances of the service!");
gAnnotationService = this;
+ mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
}
nsAnnotationService::~nsAnnotationService()
{
NS_ASSERTION(gAnnotationService == this,
"Deleting a non-singleton instance of the service");
if (gAnnotationService == this)
gAnnotationService = nullptr;
}
nsresult
nsAnnotationService::Init()
{
- mDB = Database::GetDatabase();
- NS_ENSURE_STATE(mDB);
+ mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
if (obsSvc) {
(void)obsSvc->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true);
}
return NS_OK;
}
+
+already_AddRefed<Database>
+nsAnnotationService::GetDatabase()
+{
+ RefPtr<Database> DB;
+ if (mTestingEnabled) {
+ DB = Database::GetDatabase();
+ } else {
+ if (!mDB) {
+ mDB = Database::GetDatabase();
+ }
+ DB = mDB;
+ }
+ return DB.forget();
+}
+
+
nsresult
nsAnnotationService::SetAnnotationStringInternal(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName,
const nsAString& aValue,
int32_t aFlags,
uint16_t aExpiration)
{
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
nsCOMPtr<mozIStorageStatement> statement;
nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
nsIAnnotationService::TYPE_STRING,
statement);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scoper(statement);
rv = statement->BindStringByName(NS_LITERAL_CSTRING("content"), aValue);
@@ -388,17 +410,20 @@ nsAnnotationService::SetItemAnnotationSt
nsresult
nsAnnotationService::SetAnnotationInt32Internal(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName,
int32_t aValue,
int32_t aFlags,
uint16_t aExpiration)
{
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
nsCOMPtr<mozIStorageStatement> statement;
nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
nsIAnnotationService::TYPE_INT32,
statement);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scoper(statement);
rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("content"), aValue);
@@ -459,17 +484,20 @@ nsAnnotationService::SetItemAnnotationIn
nsresult
nsAnnotationService::SetAnnotationInt64Internal(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName,
int64_t aValue,
int32_t aFlags,
uint16_t aExpiration)
{
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
nsCOMPtr<mozIStorageStatement> statement;
nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
nsIAnnotationService::TYPE_INT64,
statement);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scoper(statement);
rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("content"), aValue);
@@ -530,17 +558,20 @@ nsAnnotationService::SetItemAnnotationIn
nsresult
nsAnnotationService::SetAnnotationDoubleInternal(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName,
double aValue,
int32_t aFlags,
uint16_t aExpiration)
{
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
nsCOMPtr<mozIStorageStatement> statement;
nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
nsIAnnotationService::TYPE_DOUBLE,
statement);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scoper(statement);
rv = statement->BindDoubleByName(NS_LITERAL_CSTRING("content"), aValue);
@@ -988,17 +1019,20 @@ nsAnnotationService::GetPagesWithAnnotat
return NS_OK;
}
nsresult
nsAnnotationService::GetPagesWithAnnotationCOMArray(const nsACString& aName,
nsCOMArray<nsIURI>* _results)
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT h.url "
"FROM moz_anno_attributes n "
"JOIN moz_annos a ON n.id = a.anno_attribute_id "
"JOIN moz_places h ON h.id = a.place_id "
"WHERE n.name = :anno_name"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -1068,17 +1102,20 @@ nsAnnotationService::GetAnnotationsWithN
{
NS_ENSURE_ARG(!aName.IsEmpty());
NS_ENSURE_ARG_POINTER(_annotations);
*_count = 0;
*_annotations = nullptr;
nsCOMArray<mozIAnnotatedResult> annotations;
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT h.guid, h.url, -1, a.type, a.content "
"FROM moz_anno_attributes n "
"JOIN moz_annos a ON n.id = a.anno_attribute_id "
"JOIN moz_places h ON h.id = a.place_id "
"WHERE n.name = :anno_name "
"UNION ALL "
"SELECT b.guid, h.url, b.id, a.type, a.content "
"FROM moz_anno_attributes n "
@@ -1159,17 +1196,20 @@ nsAnnotationService::GetAnnotationsWithN
return NS_OK;
}
nsresult
nsAnnotationService::GetItemsWithAnnotationTArray(const nsACString& aName,
nsTArray<int64_t>* _results)
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT a.item_id "
"FROM moz_anno_attributes n "
"JOIN moz_items_annos a ON n.id = a.anno_attribute_id "
"WHERE n.name = :anno_name"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -1231,28 +1271,31 @@ nsAnnotationService::GetPageAnnotationNa
nsresult
nsAnnotationService::GetAnnotationNamesTArray(nsIURI* aURI,
int64_t aItemId,
nsTArray<nsCString>* _result)
{
_result->Clear();
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
bool isItemAnnotation = (aItemId > 0);
nsCOMPtr<mozIStorageStatement> statement;
if (isItemAnnotation) {
- statement = mDB->GetStatement(
+ statement = DB->GetStatement(
"SELECT n.name "
"FROM moz_anno_attributes n "
"JOIN moz_items_annos a ON a.anno_attribute_id = n.id "
"WHERE a.item_id = :item_id"
);
}
else {
- statement = mDB->GetStatement(
+ statement = DB->GetStatement(
"SELECT n.name "
"FROM moz_anno_attributes n "
"JOIN moz_annos a ON a.anno_attribute_id = n.id "
"JOIN moz_places h ON h.id = a.place_id "
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
}
NS_ENSURE_STATE(statement);
@@ -1356,28 +1399,31 @@ nsAnnotationService::ItemHasAnnotation(i
* delete the last item of a given name, that item really should go away.
* It will be cleaned up by expiration.
*/
nsresult
nsAnnotationService::RemoveAnnotationInternal(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName)
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
bool isItemAnnotation = (aItemId > 0);
nsCOMPtr<mozIStorageStatement> statement;
if (isItemAnnotation) {
- statement = mDB->GetStatement(
+ statement = DB->GetStatement(
"DELETE FROM moz_items_annos "
"WHERE item_id = :item_id "
"AND anno_attribute_id = "
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)"
);
}
else {
- statement = mDB->GetStatement(
+ statement = DB->GetStatement(
"DELETE FROM moz_annos "
"WHERE place_id = "
"(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
"AND anno_attribute_id = "
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)"
);
}
NS_ENSURE_STATE(statement);
@@ -1431,18 +1477,21 @@ nsAnnotationService::RemoveItemAnnotatio
}
NS_IMETHODIMP
nsAnnotationService::RemovePageAnnotations(nsIURI* aURI)
{
NS_ENSURE_ARG(aURI);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Should this be precompiled or a getter?
- nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
"DELETE FROM moz_annos WHERE place_id = "
"(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url)"
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
nsresult rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("page_url"), aURI);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1458,18 +1507,21 @@ nsAnnotationService::RemovePageAnnotatio
NS_IMETHODIMP
nsAnnotationService::RemoveItemAnnotations(int64_t aItemId,
uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Should this be precompiled or a getter?
- nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
"DELETE FROM moz_items_annos WHERE item_id = :item_id"
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1491,19 +1543,22 @@ nsAnnotationService::RemoveItemAnnotatio
NS_IMETHODIMP
nsAnnotationService::CopyPageAnnotations(nsIURI* aSourceURI,
nsIURI* aDestURI,
bool aOverwriteDest)
{
NS_ENSURE_ARG(aSourceURI);
NS_ENSURE_ARG(aDestURI);
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
- nsCOMPtr<mozIStorageStatement> sourceStmt = mDB->GetStatement(
+ mozStorageTransaction transaction(DB->MainConn(), false);
+
+ nsCOMPtr<mozIStorageStatement> sourceStmt = DB->GetStatement(
"SELECT h.id, n.id, n.name, a2.id "
"FROM moz_places h "
"JOIN moz_annos a ON a.place_id = h.id "
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
"LEFT JOIN moz_annos a2 ON a2.place_id = "
"(SELECT id FROM moz_places WHERE url_hash = hash(:dest_url) AND url = :dest_url) "
"AND a2.anno_attribute_id = n.id "
"WHERE url = :source_url"
@@ -1511,17 +1566,17 @@ nsAnnotationService::CopyPageAnnotations
NS_ENSURE_STATE(sourceStmt);
mozStorageStatementScoper sourceScoper(sourceStmt);
nsresult rv = URIBinder::Bind(sourceStmt, NS_LITERAL_CSTRING("source_url"), aSourceURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = URIBinder::Bind(sourceStmt, NS_LITERAL_CSTRING("dest_url"), aDestURI);
NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<mozIStorageStatement> copyStmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> copyStmt = DB->GetStatement(
"INSERT INTO moz_annos "
"(place_id, anno_attribute_id, content, flags, expiration, "
"type, dateAdded, lastModified) "
"SELECT (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url), "
"anno_attribute_id, content, flags, expiration, type, "
":date, :date "
"FROM moz_annos "
"WHERE place_id = :page_id "
@@ -1574,36 +1629,39 @@ NS_IMETHODIMP
nsAnnotationService::CopyItemAnnotations(int64_t aSourceItemId,
int64_t aDestItemId,
bool aOverwriteDest,
uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aSourceItemId, 1);
NS_ENSURE_ARG_MIN(aDestItemId, 1);
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
- nsCOMPtr<mozIStorageStatement> sourceStmt = mDB->GetStatement(
+ mozStorageTransaction transaction(DB->MainConn(), false);
+
+ nsCOMPtr<mozIStorageStatement> sourceStmt = DB->GetStatement(
"SELECT n.id, n.name, a2.id "
"FROM moz_bookmarks b "
"JOIN moz_items_annos a ON a.item_id = b.id "
"JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
"LEFT JOIN moz_items_annos a2 ON a2.item_id = :dest_item_id "
"AND a2.anno_attribute_id = n.id "
"WHERE b.id = :source_item_id"
);
NS_ENSURE_STATE(sourceStmt);
mozStorageStatementScoper sourceScoper(sourceStmt);
nsresult rv = sourceStmt->BindInt64ByName(NS_LITERAL_CSTRING("source_item_id"), aSourceItemId);
NS_ENSURE_SUCCESS(rv, rv);
rv = sourceStmt->BindInt64ByName(NS_LITERAL_CSTRING("dest_item_id"), aDestItemId);
NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<mozIStorageStatement> copyStmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> copyStmt = DB->GetStatement(
"INSERT OR REPLACE INTO moz_items_annos "
"(item_id, anno_attribute_id, content, flags, expiration, "
"type, dateAdded, lastModified) "
"SELECT :dest_item_id, anno_attribute_id, content, flags, expiration, "
"type, :date, :date "
"FROM moz_items_annos "
"WHERE item_id = :source_item_id "
"AND anno_attribute_id = :name_id"
@@ -1695,31 +1753,34 @@ nsAnnotationService::GetObservers(uint32
}
nsresult
nsAnnotationService::HasAnnotationInternal(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName,
bool* _hasAnno)
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
bool isItemAnnotation = (aItemId > 0);
nsCOMPtr<mozIStorageStatement> stmt;
if (isItemAnnotation) {
- stmt = mDB->GetStatement(
+ stmt = DB->GetStatement(
"SELECT b.id, "
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
"a.id, a.dateAdded "
"FROM moz_bookmarks b "
"LEFT JOIN moz_items_annos a ON a.item_id = b.id "
"AND a.anno_attribute_id = nameid "
"WHERE b.id = :item_id"
);
}
else {
- stmt = mDB->GetStatement(
+ stmt = DB->GetStatement(
"SELECT h.id, "
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
"a.id, a.dateAdded "
"FROM moz_places h "
"LEFT JOIN moz_annos a ON a.place_id = h.id "
"AND a.anno_attribute_id = nameid "
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
@@ -1764,30 +1825,33 @@ nsAnnotationService::HasAnnotationIntern
*/
nsresult
nsAnnotationService::StartGetAnnotation(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName,
nsCOMPtr<mozIStorageStatement>& aStatement)
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
bool isItemAnnotation = (aItemId > 0);
if (isItemAnnotation) {
- aStatement = mDB->GetStatement(
+ aStatement = DB->GetStatement(
"SELECT a.id, a.item_id, :anno_name, a.content, a.flags, "
"a.expiration, a.type "
"FROM moz_anno_attributes n "
"JOIN moz_items_annos a ON a.anno_attribute_id = n.id "
"WHERE a.item_id = :item_id "
"AND n.name = :anno_name"
);
}
else {
- aStatement = mDB->GetStatement(
+ aStatement = DB->GetStatement(
"SELECT a.id, a.place_id, :anno_name, a.content, a.flags, "
"a.expiration, a.type "
"FROM moz_anno_attributes n "
"JOIN moz_annos a ON n.id = a.anno_attribute_id "
"JOIN moz_places h ON h.id = a.place_id "
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url "
"AND n.name = :anno_name"
);
@@ -1831,24 +1895,27 @@ nsresult
nsAnnotationService::StartSetAnnotation(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName,
int32_t aFlags,
uint16_t aExpiration,
uint16_t aType,
nsCOMPtr<mozIStorageStatement>& aStatement)
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
bool isItemAnnotation = (aItemId > 0);
if (aExpiration == EXPIRE_SESSION) {
mHasSessionAnnotations = true;
}
// Ensure the annotation name exists.
- nsCOMPtr<mozIStorageStatement> addNameStmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> addNameStmt = DB->GetStatement(
"INSERT OR IGNORE INTO moz_anno_attributes (name) VALUES (:anno_name)"
);
NS_ENSURE_STATE(addNameStmt);
mozStorageStatementScoper scoper(addNameStmt);
nsresult rv = addNameStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName);
NS_ENSURE_SUCCESS(rv, rv);
rv = addNameStmt->Execute();
@@ -1859,28 +1926,28 @@ nsAnnotationService::StartSetAnnotation(
// - we should not allow setting annotations on invalid URIs or itemIds.
// This query will tell us:
// - whether the item or page exists.
// - whether the annotation already exists.
// - the nameID associated with the annotation name.
// - the id and dateAdded of the old annotation, if it exists.
nsCOMPtr<mozIStorageStatement> stmt;
if (isItemAnnotation) {
- stmt = mDB->GetStatement(
+ stmt = DB->GetStatement(
"SELECT b.id, "
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
"a.id, a.dateAdded "
"FROM moz_bookmarks b "
"LEFT JOIN moz_items_annos a ON a.item_id = b.id "
"AND a.anno_attribute_id = nameid "
"WHERE b.id = :item_id"
);
}
else {
- stmt = mDB->GetStatement(
+ stmt = DB->GetStatement(
"SELECT h.id, "
"(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
"a.id, a.dateAdded "
"FROM moz_places h "
"LEFT JOIN moz_annos a ON a.place_id = h.id "
"AND a.anno_attribute_id = nameid "
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
@@ -1906,26 +1973,26 @@ nsAnnotationService::StartSetAnnotation(
}
int64_t fkId = stmt->AsInt64(0);
int64_t nameID = stmt->AsInt64(1);
int64_t oldAnnoId = stmt->AsInt64(2);
int64_t oldAnnoDate = stmt->AsInt64(3);
if (isItemAnnotation) {
- aStatement = mDB->GetStatement(
+ aStatement = DB->GetStatement(
"INSERT OR REPLACE INTO moz_items_annos "
"(id, item_id, anno_attribute_id, content, flags, "
"expiration, type, dateAdded, lastModified) "
"VALUES (:id, :fk, :name_id, :content, :flags, "
":expiration, :type, :date_added, :last_modified)"
);
}
else {
- aStatement = mDB->GetStatement(
+ aStatement = DB->GetStatement(
"INSERT OR REPLACE INTO moz_annos "
"(id, place_id, anno_attribute_id, content, flags, "
"expiration, type, dateAdded, lastModified) "
"VALUES (:id, :fk, :name_id, :content, :flags, "
":expiration, :type, :date_added, :last_modified)"
);
}
NS_ENSURE_STATE(aStatement);
@@ -1971,41 +2038,44 @@ nsAnnotationService::StartSetAnnotation(
NS_IMETHODIMP
nsAnnotationService::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) {
// Remove all session annotations, if any.
if (mHasSessionAnnotations) {
- nsCOMPtr<mozIStorageAsyncStatement> pageAnnoStmt = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> pageAnnoStmt = DB->GetAsyncStatement(
"DELETE FROM moz_annos WHERE expiration = :expire_session"
);
NS_ENSURE_STATE(pageAnnoStmt);
nsresult rv = pageAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"),
EXPIRE_SESSION);
NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<mozIStorageAsyncStatement> itemAnnoStmt = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> itemAnnoStmt = DB->GetAsyncStatement(
"DELETE FROM moz_items_annos WHERE expiration = :expire_session"
);
NS_ENSURE_STATE(itemAnnoStmt);
rv = itemAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"),
EXPIRE_SESSION);
NS_ENSURE_SUCCESS(rv, rv);
mozIStorageBaseStatement *stmts[] = {
pageAnnoStmt.get()
, itemAnnoStmt.get()
};
nsCOMPtr<mozIStoragePendingStatement> ps;
- rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
+ rv = DB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
--- a/toolkit/components/places/nsAnnotationService.h
+++ b/toolkit/components/places/nsAnnotationService.h
@@ -80,16 +80,17 @@ public:
return gAnnotationService;
}
private:
~nsAnnotationService();
protected:
RefPtr<mozilla::places::Database> mDB;
+ bool mTestingEnabled;
nsCOMArray<nsIAnnotationObserver> mObservers;
bool mHasSessionAnnotations;
static nsAnnotationService* gAnnotationService;
static const int kAnnoIndex_ID;
static const int kAnnoIndex_PageOrItem;
@@ -143,16 +144,18 @@ protected:
double aValue,
int32_t aFlags,
uint16_t aExpiration);
nsresult RemoveAnnotationInternal(nsIURI* aURI,
int64_t aItemId,
const nsACString& aName);
+ already_AddRefed<Database> GetDatabase();
+
public:
nsresult GetPagesWithAnnotationCOMArray(const nsACString& aName,
nsCOMArray<nsIURI>* _results);
nsresult GetItemsWithAnnotationTArray(const nsACString& aName,
nsTArray<int64_t>* _result);
nsresult GetAnnotationNamesTArray(nsIURI* aURI,
int64_t aItemId,
nsTArray<nsCString>* _result);
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -75,17 +75,18 @@ NS_IMPL_CLASSINFO(nsFaviconService, null
NS_IMPL_ISUPPORTS_CI(
nsFaviconService
, nsIFaviconService
, mozIAsyncFavicons
, nsITimerCallback
)
nsFaviconService::nsFaviconService()
- : mFailedFaviconSerial(0)
+ : mTestingEnabled(false)
+ , mFailedFaviconSerial(0)
, mFailedFavicons(MAX_FAILED_FAVICONS / 2)
, mUnassociatedIcons(UNASSOCIATED_FAVICONS_LENGTH)
{
NS_ASSERTION(!gFaviconService,
"Attempting to create two instances of the service!");
gFaviconService = this;
}
@@ -97,49 +98,66 @@ nsFaviconService::~nsFaviconService()
if (gFaviconService == this)
gFaviconService = nullptr;
}
nsresult
nsFaviconService::Init()
{
- mDB = Database::GetDatabase();
- NS_ENSURE_STATE(mDB);
+ mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
mExpireUnassociatedIconsTimer = do_CreateInstance("@mozilla.org/timer;1");
NS_ENSURE_STATE(mExpireUnassociatedIconsTimer);
return NS_OK;
}
+already_AddRefed<Database>
+nsFaviconService::GetDatabase()
+{
+ RefPtr<Database> DB;
+ if (mTestingEnabled) {
+ DB = Database::GetDatabase();
+ } else {
+ if (!mDB) {
+ mDB = Database::GetDatabase();
+ }
+ DB = mDB;
+ }
+ return DB.forget();
+}
+
NS_IMETHODIMP
nsFaviconService::ExpireAllFavicons()
{
- nsCOMPtr<mozIStorageAsyncStatement> unlinkIconsStmt = mDB->GetAsyncStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageAsyncStatement> unlinkIconsStmt = DB->GetAsyncStatement(
"UPDATE moz_places "
"SET favicon_id = NULL "
"WHERE favicon_id NOT NULL"
);
NS_ENSURE_STATE(unlinkIconsStmt);
- nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = DB->GetAsyncStatement(
"DELETE FROM moz_favicons WHERE id NOT IN ("
"SELECT favicon_id FROM moz_places WHERE favicon_id NOT NULL "
")"
);
NS_ENSURE_STATE(removeIconsStmt);
mozIStorageBaseStatement* stmts[] = {
unlinkIconsStmt.get()
, removeIconsStmt.get()
};
nsCOMPtr<mozIStoragePendingStatement> ps;
RefPtr<ExpireFaviconsStatementCallbackNotifier> callback =
new ExpireFaviconsStatementCallbackNotifier();
- nsresult rv = mDB->MainConn()->ExecuteAsync(
+ nsresult rv = DB->MainConn()->ExecuteAsync(
stmts, ArrayLength(stmts), callback, getter_AddRefs(ps)
);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
@@ -281,17 +299,17 @@ nsFaviconService::SetAndFetchFaviconForP
}
RefPtr<AsyncFetchAndSetIconForPage> event =
new AsyncFetchAndSetIconForPage(icon, page, loadPrivate,
aCallback, aLoadingPrincipal);
// Get the target thread and start the work.
// DB will be updated and observers notified when data has finished loading.
- RefPtr<Database> DB = Database::GetDatabase();
+ RefPtr<Database> DB = GetDatabase();
NS_ENSURE_STATE(DB);
DB->DispatchToAsyncThread(event);
// Return this event to the caller to allow aborting an eventual fetch.
event.forget(_canceler);
return NS_OK;
}
@@ -352,17 +370,17 @@ nsFaviconService::ReplaceFaviconData(nsI
iconData->mimeType.Assign(aMimeType);
iconData->data.Assign(TO_CHARBUFFER(aData), aDataLen);
}
// If the database contains an icon at the given url, we will update the
// database immediately so that the associated pages are kept in sync.
// Otherwise, do nothing and let the icon be picked up from the memory hash.
RefPtr<AsyncReplaceFaviconData> event = new AsyncReplaceFaviconData(*iconData);
- RefPtr<Database> DB = Database::GetDatabase();
+ RefPtr<Database> DB = GetDatabase();
NS_ENSURE_STATE(DB);
DB->DispatchToAsyncThread(event);
return NS_OK;
}
NS_IMETHODIMP
nsFaviconService::ReplaceFaviconDataFromDataURL(nsIURI* aFaviconURI,
@@ -468,17 +486,17 @@ nsFaviconService::GetFaviconURLForPage(n
nsAutoCString pageSpec;
nsresult rv = aPageURI->GetSpec(pageSpec);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<AsyncGetFaviconURLForPage> event =
new AsyncGetFaviconURLForPage(pageSpec, aCallback);
- RefPtr<Database> DB = Database::GetDatabase();
+ RefPtr<Database> DB = GetDatabase();
NS_ENSURE_STATE(DB);
DB->DispatchToAsyncThread(event);
return NS_OK;
}
NS_IMETHODIMP
nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI,
@@ -489,17 +507,17 @@ nsFaviconService::GetFaviconDataForPage(
NS_ENSURE_ARG(aCallback);
nsAutoCString pageSpec;
nsresult rv = aPageURI->GetSpec(pageSpec);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<AsyncGetFaviconDataForPage> event =
new AsyncGetFaviconDataForPage(pageSpec, aCallback);
- RefPtr<Database> DB = Database::GetDatabase();
+ RefPtr<Database> DB = GetDatabase();
NS_ENSURE_STATE(DB);
DB->DispatchToAsyncThread(event);
return NS_OK;
}
nsresult
nsFaviconService::GetFaviconLinkForIcon(nsIURI* aFaviconURI,
@@ -666,18 +684,21 @@ nsFaviconService::OptimizeFaviconImage(c
return NS_OK;
}
nsresult
nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI,
mozIStorageStatementCallback *aCallback)
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
NS_ASSERTION(aCallback, "Doesn't make sense to call this without a callback");
- nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
"SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url"
);
NS_ENSURE_STATE(stmt);
// Ignore the ref part of the URI before querying the database because
// we may have added a media fragment for rendering purposes.
nsAutoCString faviconURI;
--- a/toolkit/components/places/nsFaviconService.h
+++ b/toolkit/components/places/nsFaviconService.h
@@ -115,30 +115,34 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONSERVICE
NS_DECL_MOZIASYNCFAVICONS
NS_DECL_NSITIMERCALLBACK
private:
~nsFaviconService();
+ already_AddRefed<mozilla::places::Database> GetDatabase();
+
RefPtr<mozilla::places::Database> mDB;
nsCOMPtr<nsITimer> mExpireUnassociatedIconsTimer;
static nsFaviconService* gFaviconService;
/**
* A cached URI for the default icon. We return this a lot, and don't want to
* re-parse and normalize our unchanging string many times. Important: do
* not return this directly; use Clone() since callers may change the object
* they get back. May be null, in which case it needs initialization.
*/
nsCOMPtr<nsIURI> mDefaultIcon;
+ bool mTestingEnabled;
+
uint32_t mFailedFaviconSerial;
nsDataHashtable<nsCStringHashKey, uint32_t> mFailedFavicons;
// This class needs access to the icons cache.
friend class mozilla::places::AsyncReplaceFaviconData;
nsTHashtable<UnassociatedIconHashKey> mUnassociatedIcons;
};
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -158,16 +158,17 @@ NeedsTombstone(const BookmarkData& aBook
nsNavBookmarks::nsNavBookmarks()
: mItemCount(0)
, mRoot(0)
, mMenuRoot(0)
, mTagsRoot(0)
, mUnfiledRoot(0)
, mToolbarRoot(0)
, mMobileRoot(0)
+ , mTestingEnabled(false)
, mCanNotify(false)
, mCacheObservers("bookmark-observers")
, mBatching(false)
{
NS_ASSERTION(!gBookmarksService,
"Attempting to create two instances of the service!");
gBookmarksService = this;
}
@@ -200,18 +201,17 @@ nsNavBookmarks::StoreLastInsertedId(cons
MOZ_ASSERT(aTable.EqualsLiteral("moz_bookmarks"));
sLastInsertedItemId = aLastInsertedId;
}
nsresult
nsNavBookmarks::Init()
{
- mDB = Database::GetDatabase();
- NS_ENSURE_STATE(mDB);
+ ReadTestingEnabled();
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
(void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true);
(void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true);
}
nsresult rv = ReadRoots();
@@ -231,21 +231,44 @@ nsNavBookmarks::Init()
NS_ENSURE_STATE(history);
history->AddObserver(this, true);
// DO NOT PUT STUFF HERE that can fail. See observer comment above.
return NS_OK;
}
+already_AddRefed<Database>
+nsNavBookmarks::GetDatabase()
+{
+ RefPtr<Database> DB;
+ if (mTestingEnabled) {
+ DB = Database::GetDatabase();
+ } else {
+ if (!mDB) {
+ mDB = Database::GetDatabase();
+ }
+ DB = mDB;
+ }
+ return DB.forget();
+}
+
+void
+nsNavBookmarks::ReadTestingEnabled() {
+ mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
+}
+
nsresult
nsNavBookmarks::ReadRoots()
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
nsCOMPtr<mozIStorageStatement> stmt;
- nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING(
+ nsresult rv = DB->MainConn()->CreateStatement(NS_LITERAL_CSTRING(
"SELECT guid, id FROM moz_bookmarks WHERE guid IN ( "
"'root________', 'menu________', 'toolbar_____', "
"'tags________', 'unfiled_____', 'mobile______' )"
), getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
bool hasResult;
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
@@ -286,17 +309,20 @@ nsNavBookmarks::ReadRoots()
// nsNavBookmarks::IsBookmarkedInDatabase
//
// This checks to see if the specified place_id is actually bookmarked.
nsresult
nsNavBookmarks::IsBookmarkedInDatabase(int64_t aPlaceId,
bool* aIsBookmarked)
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT 1 FROM moz_bookmarks WHERE fk = :page_id"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->ExecuteStep(aIsBookmarked);
@@ -309,17 +335,20 @@ nsresult
nsNavBookmarks::AdjustIndices(int64_t aFolderId,
int32_t aStartIndex,
int32_t aEndIndex,
int32_t aDelta)
{
NS_ASSERTION(aStartIndex >= 0 && aEndIndex <= INT32_MAX &&
aStartIndex <= aEndIndex, "Bad indices");
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"UPDATE moz_bookmarks SET position = position + :delta "
"WHERE parent = :parent "
"AND position BETWEEN :from_index AND :to_index"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
@@ -343,17 +372,20 @@ nsNavBookmarks::AdjustSeparatorsSyncCoun
int32_t aStartIndex,
int64_t aSyncChangeDelta)
{
MOZ_ASSERT(aStartIndex >= 0, "Bad start position");
if (!aSyncChangeDelta) {
return NS_OK;
}
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"UPDATE moz_bookmarks SET syncChangeCounter = syncChangeCounter + :delta "
"WHERE parent = :parent AND position >= :start_index "
"AND type = :item_type "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("delta"), aSyncChangeDelta);
@@ -435,17 +467,20 @@ nsNavBookmarks::InsertBookmarkInDB(int64
int64_t* _itemId,
nsACString& _guid)
{
// Check for a valid itemId.
MOZ_ASSERT(_itemId && (*_itemId == -1 || *_itemId > 0));
// Check for a valid placeId.
MOZ_ASSERT(aPlaceId && (aPlaceId == -1 || aPlaceId > 0));
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"INSERT INTO moz_bookmarks "
"(id, fk, type, parent, position, title, "
"dateAdded, lastModified, guid, syncStatus, syncChangeCounter) "
"VALUES (:item_id, :page_id, :item_type, :parent, :item_index, "
":item_title, :date_added, :last_modified, "
":item_guid, :sync_status, :change_counter)"
);
NS_ENSURE_STATE(stmt);
@@ -592,17 +627,20 @@ nsNavBookmarks::InsertBookmark(int64_t a
{
NS_ENSURE_ARG(aURI);
NS_ENSURE_ARG_POINTER(aNewBookmarkId);
NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX);
if (!aGUID.IsEmpty() && !IsValidGUID(aGUID))
return NS_ERROR_INVALID_ARG;
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
nsNavHistory* history = nsNavHistory::GetHistoryService();
NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
int64_t placeId;
nsAutoCString placeGuid;
nsresult rv = history->GetOrCreateIdForPage(aURI, &placeId, placeGuid);
NS_ENSURE_SUCCESS(rv, rv);
@@ -689,34 +727,37 @@ nsNavBookmarks::RemoveItem(int64_t aItem
js::ProfileEntry::Category::OTHER);
NS_ENSURE_ARG(!IsRoot(aItemId));
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aItemId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
// First, if not a tag, remove item annotations.
bool isUntagging = bookmark.grandParentId == mTagsRoot;
if (bookmark.parentId != mTagsRoot && !isUntagging) {
nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
rv = annosvc->RemoveItemAnnotations(bookmark.id, aSource);
NS_ENSURE_SUCCESS(rv, rv);
}
if (bookmark.type == TYPE_FOLDER) {
// Remove all of the folder's children.
rv = RemoveFolderChildren(bookmark.id, aSource);
NS_ENSURE_SUCCESS(rv, rv);
}
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"DELETE FROM moz_bookmarks WHERE id = :item_id"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
@@ -861,17 +902,20 @@ nsNavBookmarks::CreateContainerWithID(in
// Get the correct index for insertion. This also ensures the parent exists.
int32_t index, folderCount;
int64_t grandParentId;
nsAutoCString folderGuid;
nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId);
NS_ENSURE_SUCCESS(rv, rv);
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
if (*aIndex == nsINavBookmarksService::DEFAULT_INDEX ||
*aIndex >= folderCount) {
index = folderCount;
} else {
index = *aIndex;
// Create space for the insertion.
rv = AdjustIndices(aParent, index, INT32_MAX, 1);
@@ -919,17 +963,20 @@ nsNavBookmarks::InsertSeparator(int64_t
// Get the correct index for insertion. This also ensures the parent exists.
int32_t index, folderCount;
int64_t grandParentId;
nsAutoCString folderGuid;
nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId);
NS_ENSURE_SUCCESS(rv, rv);
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
if (aIndex == nsINavBookmarksService::DEFAULT_INDEX ||
aIndex >= folderCount) {
index = folderCount;
}
else {
index = aIndex;
// Create space for the insertion.
@@ -960,17 +1007,20 @@ nsNavBookmarks::InsertSeparator(int64_t
nsresult
nsNavBookmarks::GetLastChildId(int64_t aFolderId, int64_t* aItemId)
{
NS_ASSERTION(aFolderId > 0, "Invalid folder id");
*aItemId = -1;
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT id FROM moz_bookmarks WHERE parent = :parent "
"ORDER BY position DESC LIMIT 1"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
NS_ENSURE_SUCCESS(rv, rv);
@@ -991,25 +1041,28 @@ nsNavBookmarks::GetIdForItemAt(int64_t a
int32_t aIndex,
int64_t* aItemId)
{
NS_ENSURE_ARG_MIN(aFolder, 1);
NS_ENSURE_ARG_POINTER(aItemId);
*aItemId = -1;
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
nsresult rv;
if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
// Get last item within aFolder.
rv = GetLastChildId(aFolder, aItemId);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
// Get the item in aFolder with position aIndex.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT id, fk, type FROM moz_bookmarks "
"WHERE parent = :parent AND position = :item_index"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolder);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1047,21 +1100,24 @@ nsNavBookmarks::GetRemoveFolderTransacti
NS_ADDREF(*aResult = rft);
return NS_OK;
}
nsresult
nsNavBookmarks::GetDescendantFolders(int64_t aFolderId,
nsTArray<int64_t>& aDescendantFoldersArray) {
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
nsresult rv;
// New descendant folders will be added from this index on.
uint32_t startIndex = aDescendantFoldersArray.Length();
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT id "
"FROM moz_bookmarks "
"WHERE parent = :parent "
"AND type = :item_type "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -1091,27 +1147,30 @@ nsNavBookmarks::GetDescendantFolders(int
}
nsresult
nsNavBookmarks::GetDescendantChildren(int64_t aFolderId,
const nsACString& aFolderGuid,
int64_t aGrandParentId,
nsTArray<BookmarkData>& aFolderChildrenArray) {
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// New children will be added from this index on.
uint32_t startIndex = aFolderChildrenArray.Length();
nsresult rv;
{
// Collect children informations.
// Select all children of a given folder, sorted by position.
// This is a LEFT JOIN because not all bookmarks types have a place.
// We construct a result where the first columns exactly match
// kGetInfoIndex_* order, and additionally contains columns for position,
// item_child, and folder_child from moz_bookmarks.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
"h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
"b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
"b.guid, b.position, b.type, b.fk, b.syncStatus "
"FROM moz_bookmarks b "
"LEFT JOIN moz_places h ON b.fk = h.id "
"LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
"WHERE b.parent = :parent "
@@ -1202,34 +1261,37 @@ nsNavBookmarks::RemoveFolderChildren(int
if (child.type == TYPE_FOLDER) {
foldersToRemove.Append(',');
foldersToRemove.AppendInt(child.id);
}
}
int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Delete items from the database now.
- mozStorageTransaction transaction(mDB->MainConn(), false);
-
- nsCOMPtr<mozIStorageStatement> deleteStatement = mDB->GetStatement(
+ mozStorageTransaction transaction(DB->MainConn(), false);
+
+ nsCOMPtr<mozIStorageStatement> deleteStatement = DB->GetStatement(
NS_LITERAL_CSTRING(
"DELETE FROM moz_bookmarks "
"WHERE parent IN (:parent") + foldersToRemove + NS_LITERAL_CSTRING(")")
);
NS_ENSURE_STATE(deleteStatement);
mozStorageStatementScoper deleteStatementScoper(deleteStatement);
rv = deleteStatement->BindInt64ByName(NS_LITERAL_CSTRING("parent"), folder.id);
NS_ENSURE_SUCCESS(rv, rv);
rv = deleteStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
// Clean up orphan items annotations.
- rv = mDB->MainConn()->ExecuteSimpleSQL(
+ rv = DB->MainConn()->ExecuteSimpleSQL(
NS_LITERAL_CSTRING(
"DELETE FROM moz_items_annos "
"WHERE id IN ("
"SELECT a.id from moz_items_annos a "
"LEFT JOIN moz_bookmarks b ON a.item_id = b.id "
"WHERE b.id ISNULL)"));
NS_ENSURE_SUCCESS(rv, rv);
@@ -1335,17 +1397,20 @@ nsNavBookmarks::MoveItem(int64_t aItemId
NS_ENSURE_ARG(!IsRoot(aItemId));
NS_ENSURE_ARG_MIN(aItemId, 1);
NS_ENSURE_ARG_MIN(aNewParent, 1);
// -1 is append, but no other negative number is allowed.
NS_ENSURE_ARG_MIN(aIndex, -1);
// Disallow making an item its own parent.
NS_ENSURE_ARG(aItemId != aNewParent);
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aItemId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
// if parent and index are the same, nothing to do
if (bookmark.parentId == aNewParent && bookmark.position == aIndex)
return NS_OK;
@@ -1424,17 +1489,17 @@ nsNavBookmarks::MoveItem(int64_t aItemId
rv = AdjustIndices(aNewParent, newIndex, INT32_MAX, 1);
NS_ENSURE_SUCCESS(rv, rv);
}
int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
{
// Update parent and position.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"UPDATE moz_bookmarks SET parent = :parent, position = :item_index "
"WHERE id = :item_id "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aNewParent);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1504,18 +1569,21 @@ nsNavBookmarks::MoveItem(int64_t aItemId
aSource));
return NS_OK;
}
nsresult
nsNavBookmarks::FetchItemInfo(int64_t aItemId,
BookmarkData& _bookmark)
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// LEFT JOIN since not all bookmarks have an associated place.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT b.id, h.url, b.title, b.position, b.fk, b.parent, b.type, "
"b.dateAdded, b.lastModified, b.guid, t.guid, t.parent, "
"b.syncStatus "
"FROM moz_bookmarks b "
"LEFT JOIN moz_bookmarks t ON t.id = b.parent "
"LEFT JOIN moz_places h ON h.id = b.fk "
"WHERE b.id = :item_id"
);
@@ -1580,29 +1648,32 @@ nsNavBookmarks::FetchItemInfo(int64_t aI
nsresult
nsNavBookmarks::SetItemDateInternal(enum BookmarkDate aDateType,
int64_t aSyncChangeDelta,
int64_t aItemId,
PRTime aValue)
{
aValue = RoundToMilliseconds(aValue);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
nsCOMPtr<mozIStorageStatement> stmt;
if (aDateType == DATE_ADDED) {
// lastModified is set to the same value as dateAdded. We do this for
// performance reasons, since it will allow us to use an index to sort items
// by date.
- stmt = mDB->GetStatement(
+ stmt = DB->GetStatement(
"UPDATE moz_bookmarks SET dateAdded = :date, lastModified = :date, "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE id = :item_id"
);
}
else {
- stmt = mDB->GetStatement(
+ stmt = DB->GetStatement(
"UPDATE moz_bookmarks SET lastModified = :date, "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE id = :item_id"
);
}
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -1638,17 +1709,20 @@ nsNavBookmarks::SetItemDateAdded(int64_t
// Round here so that we notify with the right value.
bookmark.dateAdded = RoundToMilliseconds(aDateAdded);
if (isTagging) {
// If we're changing a tag, bump the change counter for all tagged
// bookmarks. We use a separate code path to avoid a transaction for
// non-tags.
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
rv = SetItemDateInternal(DATE_ADDED, syncChangeDelta, bookmark.id,
bookmark.dateAdded);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddSyncChangesForBookmarksWithURL(bookmark.url, syncChangeDelta);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1708,17 +1782,20 @@ nsNavBookmarks::SetItemLastModified(int6
// Round here so that we notify with the right value.
bookmark.lastModified = RoundToMilliseconds(aLastModified);
if (isTagging) {
// If we're changing a tag, bump the change counter for all tagged
// bookmarks. We use a separate code path to avoid a transaction for
// non-tags.
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
rv = SetItemDateInternal(LAST_MODIFIED, syncChangeDelta, bookmark.id,
bookmark.lastModified);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddSyncChangesForBookmarksWithURL(bookmark.url, syncChangeDelta);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1784,17 +1861,20 @@ nsresult
nsNavBookmarks::AddSyncChangesForBookmarksWithURI(nsIURI* aURI,
int64_t aSyncChangeDelta)
{
if (NS_WARN_IF(!aURI) || !aSyncChangeDelta) {
// Ignore sync changes for invalid URIs.
return NS_OK;
}
- nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
"UPDATE moz_bookmarks SET "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE type = :type AND "
"fk = (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND "
"url = :url)"
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
@@ -1815,17 +1895,20 @@ nsNavBookmarks::AddSyncChangesForBookmar
nsresult
nsNavBookmarks::AddSyncChangesForBookmarksInFolder(int64_t aFolderId,
int64_t aSyncChangeDelta)
{
if (!aSyncChangeDelta) {
return NS_OK;
}
- nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
"UPDATE moz_bookmarks SET "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE type = :type AND "
"fk = (SELECT fk FROM moz_bookmarks WHERE parent = :parent)"
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
@@ -1846,17 +1929,21 @@ nsNavBookmarks::AddSyncChangesForBookmar
nsresult
nsNavBookmarks::InsertTombstone(const BookmarkData& aBookmark)
{
if (!NeedsTombstone(aBookmark)) {
return NS_OK;
}
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"INSERT INTO moz_bookmarks_deleted (guid, dateRemoved) "
"VALUES (:guid, :date_removed)"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
aBookmark.guid);
@@ -1874,33 +1961,36 @@ nsNavBookmarks::InsertTombstone(const Bo
nsresult
nsNavBookmarks::InsertTombstones(const nsTArray<TombstoneData>& aTombstones)
{
if (aTombstones.IsEmpty()) {
return NS_OK;
}
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
size_t maxRowsPerChunk = SQLITE_MAX_VARIABLE_NUMBER / 2;
for (uint32_t startIndex = 0; startIndex < aTombstones.Length(); startIndex += maxRowsPerChunk) {
size_t rowsPerChunk = std::min(maxRowsPerChunk, aTombstones.Length() - startIndex);
// Build a query to insert all tombstones in a single statement, chunking to
// avoid the SQLite bound parameter limit.
nsAutoCString tombstonesToInsert;
tombstonesToInsert.AppendLiteral("VALUES (?, ?)");
for (uint32_t i = 1; i < rowsPerChunk; ++i) {
tombstonesToInsert.AppendLiteral(", (?, ?)");
}
#ifdef DEBUG
MOZ_ASSERT(tombstonesToInsert.CountChar('?') == rowsPerChunk * 2,
"Expected one binding param per column for each tombstone");
#endif
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_deleted "
"(guid, dateRemoved) ") +
tombstonesToInsert
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
uint32_t paramIndex = 0;
@@ -1919,33 +2009,39 @@ nsNavBookmarks::InsertTombstones(const n
return NS_OK;
}
nsresult
nsNavBookmarks::RemoveTombstone(const nsACString& aGUID)
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"DELETE FROM moz_bookmarks_deleted WHERE guid = :guid"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
NS_ENSURE_SUCCESS(rv, rv);
return stmt->Execute();
}
nsresult
nsNavBookmarks::PreventSyncReparenting(const BookmarkData& aBookmark)
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"DELETE FROM moz_items_annos WHERE "
"item_id = :item_id AND "
"anno_attribute_id = (SELECT id FROM moz_anno_attributes "
"WHERE name = :orphan_anno)"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -1977,17 +2073,20 @@ nsNavBookmarks::SetItemTitle(int64_t aIt
nsAutoCString title;
TruncateTitle(aTitle, title);
if (isChangingTagFolder) {
// If we're changing the title of a tag folder, bump the change counter
// for all tagged bookmarks. We use a separate code path to avoid a
// transaction for non-tags.
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
rv = SetItemTitleInternal(bookmark, aTitle, syncChangeDelta);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddSyncChangesForBookmarksInFolder(bookmark.id, syncChangeDelta);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
@@ -2014,17 +2113,20 @@ nsNavBookmarks::SetItemTitle(int64_t aIt
}
nsresult
nsNavBookmarks::SetItemTitleInternal(BookmarkData& aBookmark,
const nsACString& aTitle,
int64_t aSyncChangeDelta)
{
- nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
"UPDATE moz_bookmarks SET "
"title = :item_title, lastModified = :date, "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE id = :item_id"
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
@@ -2135,22 +2237,25 @@ nsresult
nsNavBookmarks::QueryFolderChildren(
int64_t aFolderId,
nsNavHistoryQueryOptions* aOptions,
nsCOMArray<nsNavHistoryResultNode>* aChildren)
{
NS_ENSURE_ARG_POINTER(aOptions);
NS_ENSURE_ARG_POINTER(aChildren);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Select all children of a given folder, sorted by position.
// This is a LEFT JOIN because not all bookmarks types have a place.
// We construct a result where the first columns exactly match those returned
// by mDBGetURLPageInfo, and additionally contains columns for position,
// item_child, and folder_child from moz_bookmarks.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
"h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
"b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
"b.guid, b.position, b.type, b.fk "
"FROM moz_bookmarks b "
"LEFT JOIN moz_places h ON b.fk = h.id "
"LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
"WHERE b.parent = :parent "
@@ -2273,22 +2378,25 @@ nsresult
nsNavBookmarks::QueryFolderChildrenAsync(
nsNavHistoryFolderResultNode* aNode,
int64_t aFolderId,
mozIStoragePendingStatement** _pendingStmt)
{
NS_ENSURE_ARG_POINTER(aNode);
NS_ENSURE_ARG_POINTER(_pendingStmt);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Select all children of a given folder, sorted by position.
// This is a LEFT JOIN because not all bookmarks types have a place.
// We construct a result where the first columns exactly match those returned
// by mDBGetURLPageInfo, and additionally contains columns for position,
// item_child, and folder_child from moz_bookmarks.
- nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
"SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
"h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
"b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
"b.guid, b.position, b.type, b.fk "
"FROM moz_bookmarks b "
"LEFT JOIN moz_places h ON b.fk = h.id "
"LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
"WHERE b.parent = :parent "
@@ -2312,19 +2420,22 @@ nsresult
nsNavBookmarks::FetchFolderInfo(int64_t aFolderId,
int32_t* _folderCount,
nsACString& _guid,
int64_t* _parentId)
{
*_folderCount = 0;
*_parentId = -1;
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// This query has to always return results, so it can't be written as a join,
// though a left join of 2 subqueries would have the same cost.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT count(*), "
"(SELECT guid FROM moz_bookmarks WHERE id = :parent), "
"(SELECT parent FROM moz_bookmarks WHERE id = :parent) "
"FROM moz_bookmarks "
"WHERE parent = :parent"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -2358,17 +2469,20 @@ nsNavBookmarks::FetchFolderInfo(int64_t
NS_IMETHODIMP
nsNavBookmarks::IsBookmarked(nsIURI* aURI, bool* aBookmarked)
{
NS_ENSURE_ARG(aURI);
NS_ENSURE_ARG_POINTER(aBookmarked);
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT 1 FROM moz_bookmarks b "
"JOIN moz_places h ON b.fk = h.id "
"WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
@@ -2394,16 +2508,19 @@ nsNavBookmarks::GetBookmarkedURIFor(nsIU
nsAutoCString placeGuid;
nsresult rv = history->GetIdForPage(aURI, &placeId, placeGuid);
NS_ENSURE_SUCCESS(rv, rv);
if (!placeId) {
// This URI is unknown, just return null.
return NS_OK;
}
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Check if a bookmark exists in the redirects chain for this URI.
// The query will also check if the page is directly bookmarked, and return
// the first found bookmark in case. The check is directly on moz_bookmarks
// without special filtering.
// The next query finds the bookmarked ancestors in a redirects chain.
// It won't go further than 3 levels of redirects (a->b->c->your_place_id).
// To make this path 100% correct (up to any level) we would need either:
// - A separate hash, build through recursive querying of the database.
@@ -2442,17 +2559,17 @@ nsNavBookmarks::GetBookmarkedURIFor(nsIU
"LIMIT 1 "
")",
nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
);
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(query);
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(query);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), placeId);
NS_ENSURE_SUCCESS(rv, rv);
bool hasBookmarkedOrigin;
if (NS_SUCCEEDED(stmt->ExecuteStep(&hasBookmarkedOrigin)) &&
hasBookmarkedOrigin) {
@@ -2475,31 +2592,34 @@ nsNavBookmarks::ChangeBookmarkURI(int64_
NS_ENSURE_ARG_MIN(aBookmarkId, 1);
NS_ENSURE_ARG(aNewURI);
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aBookmarkId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_ARG(bookmark.type == TYPE_BOOKMARK);
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
bool isTagging = bookmark.grandParentId == mTagsRoot;
int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
nsNavHistory* history = nsNavHistory::GetHistoryService();
NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
int64_t newPlaceId;
nsAutoCString newPlaceGuid;
rv = history->GetOrCreateIdForPage(aNewURI, &newPlaceId, newPlaceGuid);
NS_ENSURE_SUCCESS(rv, rv);
if (!newPlaceId)
return NS_ERROR_INVALID_ARG;
- nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
"UPDATE moz_bookmarks SET "
"fk = :page_id, lastModified = :date, "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE id = :item_id "
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
@@ -2577,20 +2697,23 @@ nsNavBookmarks::GetFolderIdForItem(int64
nsresult
nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI* aURI,
nsTArray<int64_t>& aResult,
bool aSkipTags)
{
NS_ENSURE_ARG(aURI);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Double ordering covers possible lastModified ties, that could happen when
// importing, syncing or due to extensions.
// Note: not using a JOIN is cheaper in this case.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"/* do not warn (bug 1175249) */ "
"SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent "
"FROM moz_bookmarks b "
"JOIN moz_bookmarks t on t.id = b.parent "
"WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
"ORDER BY b.lastModified DESC, b.id DESC "
);
NS_ENSURE_STATE(stmt);
@@ -2621,20 +2744,23 @@ nsNavBookmarks::GetBookmarkIdsForURITArr
}
nsresult
nsNavBookmarks::GetBookmarksForURI(nsIURI* aURI,
nsTArray<BookmarkData>& aBookmarks)
{
NS_ENSURE_ARG(aURI);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Double ordering covers possible lastModified ties, that could happen when
// importing, syncing or due to extensions.
// Note: not using a JOIN is cheaper in this case.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"/* do not warn (bug 1175249) */ "
"SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent, b.syncStatus "
"FROM moz_bookmarks b "
"JOIN moz_bookmarks t on t.id = b.parent "
"WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
"ORDER BY b.lastModified DESC, b.id DESC "
);
NS_ENSURE_STATE(stmt);
@@ -2742,20 +2868,23 @@ nsNavBookmarks::SetItemIndex(int64_t aIt
int64_t grandParentId;
nsAutoCString folderGuid;
rv = FetchFolderInfo(bookmark.parentId, &folderCount, folderGuid, &grandParentId);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(aNewIndex < folderCount, NS_ERROR_INVALID_ARG);
// Check the parent's guid is the expected one.
MOZ_ASSERT(bookmark.parentGuid == folderGuid);
- mozStorageTransaction transaction(mDB->MainConn(), false);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false);
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"UPDATE moz_bookmarks SET "
"position = :item_index "
"WHERE id = :item_id"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
@@ -2767,17 +2896,17 @@ nsNavBookmarks::SetItemIndex(int64_t aIt
NS_ENSURE_SUCCESS(rv, rv);
}
int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
{
// Sync stores child indices in the parent's record, so we only need to
// bump the parent's change counter.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"UPDATE moz_bookmarks SET "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE id = :parent_id"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent_id"),
@@ -2831,21 +2960,24 @@ nsNavBookmarks::SetKeywordForBookmark(in
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), bookmark.url);
NS_ENSURE_SUCCESS(rv, rv);
// Shortcuts are always lowercased internally.
nsAutoString keyword(aUserCasedKeyword);
ToLowerCase(keyword);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// The same URI can be associated to more than one keyword, provided the post
// data differs. Check if there are already keywords associated to this uri.
nsTArray<nsString> oldKeywords;
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT keyword FROM moz_keywords WHERE place_id = :place_id"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("place_id"), bookmark.placeId);
NS_ENSURE_SUCCESS(rv, rv);
bool hasMore;
@@ -2860,21 +2992,21 @@ nsNavBookmarks::SetKeywordForBookmark(in
// Trying to remove a non-existent keyword is a no-op.
if (keyword.IsEmpty() && oldKeywords.Length() == 0) {
return NS_OK;
}
int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
if (keyword.IsEmpty()) {
- mozStorageTransaction removeTxn(mDB->MainConn(), false);
+ mozStorageTransaction removeTxn(DB->MainConn(), false);
// We are removing the existing keywords.
for (uint32_t i = 0; i < oldKeywords.Length(); ++i) {
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"DELETE FROM moz_keywords WHERE keyword = :old_keyword"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("old_keyword"),
oldKeywords[i]);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
@@ -2890,17 +3022,17 @@ nsNavBookmarks::SetKeywordForBookmark(in
nsAutoCString changedIds;
changedIds.AppendInt(bookmarks[0].id);
for (uint32_t i = 1; i < bookmarks.Length(); ++i) {
changedIds.Append(',');
changedIds.AppendInt(bookmarks[i].id);
}
// Update the sync change counter for all bookmarks with the removed
// keyword.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
NS_LITERAL_CSTRING(
"UPDATE moz_bookmarks SET "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE id IN (") + changedIds + NS_LITERAL_CSTRING(")")
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -2934,17 +3066,17 @@ nsNavBookmarks::SetKeywordForBookmark(in
return NS_OK;
}
// A keyword can only be associated to a single URI. Check if the requested
// keyword was already associated, in such a case we will need to notify about
// the change.
nsCOMPtr<nsIURI> oldUri;
{
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT url "
"FROM moz_keywords "
"JOIN moz_places h ON h.id = place_id "
"WHERE keyword = :keyword"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword);
@@ -2958,17 +3090,17 @@ nsNavBookmarks::SetKeywordForBookmark(in
rv = NS_NewURI(getter_AddRefs(oldUri), spec);
NS_ENSURE_SUCCESS(rv, rv);
}
}
// If another uri is using the new keyword, we must update the keyword entry.
// Note we cannot use INSERT OR REPLACE cause it wouldn't invoke the delete
// trigger.
- mozStorageTransaction updateTxn(mDB->MainConn(), false);
+ mozStorageTransaction updateTxn(DB->MainConn(), false);
nsCOMPtr<mozIStorageStatement> stmt;
if (oldUri) {
// In both cases, notify about the change.
nsTArray<BookmarkData> bookmarks;
rv = GetBookmarksForURI(oldUri, bookmarks);
NS_ENSURE_SUCCESS(rv, rv);
for (uint32_t i = 0; i < bookmarks.Length(); ++i) {
@@ -2982,23 +3114,23 @@ nsNavBookmarks::SetKeywordForBookmark(in
TYPE_BOOKMARK,
bookmarks[i].parentId,
bookmarks[i].guid,
bookmarks[i].parentGuid,
EmptyCString(),
aSource));
}
- stmt = mDB->GetStatement(
+ stmt = DB->GetStatement(
"UPDATE moz_keywords SET place_id = :place_id WHERE keyword = :keyword"
);
NS_ENSURE_STATE(stmt);
}
else {
- stmt = mDB->GetStatement(
+ stmt = DB->GetStatement(
"INSERT INTO moz_keywords (keyword, place_id) "
"VALUES (:keyword, :place_id)"
);
}
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("place_id"), bookmark.placeId);
@@ -3016,17 +3148,17 @@ nsNavBookmarks::SetKeywordForBookmark(in
// Build a query to update all bookmarks in a single statement.
nsAutoCString changedIds;
changedIds.AppendInt(bookmarks[0].id);
for (uint32_t i = 1; i < bookmarks.Length(); ++i) {
changedIds.Append(',');
changedIds.AppendInt(bookmarks[i].id);
}
// Update the sync change counter for all bookmarks with the new keyword.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
NS_LITERAL_CSTRING(
"UPDATE moz_bookmarks SET "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE id IN (") + changedIds + NS_LITERAL_CSTRING(")")
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -3062,19 +3194,22 @@ nsNavBookmarks::SetKeywordForBookmark(in
NS_IMETHODIMP
nsNavBookmarks::GetKeywordForBookmark(int64_t aBookmarkId, nsAString& aKeyword)
{
NS_ENSURE_ARG_MIN(aBookmarkId, 1);
aKeyword.Truncate(0);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// We can have multiple keywords for the same uri, here we'll just return the
// last created one.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(NS_LITERAL_CSTRING(
"SELECT k.keyword "
"FROM moz_bookmarks b "
"JOIN moz_keywords k ON k.place_id = b.fk "
"WHERE b.id = :item_id "
"ORDER BY k.ROWID DESC "
"LIMIT 1"
));
NS_ENSURE_STATE(stmt);
--- a/toolkit/components/places/nsNavBookmarks.h
+++ b/toolkit/components/places/nsNavBookmarks.h
@@ -464,26 +464,32 @@ private:
int64_t mID;
uint16_t mSource;
MOZ_INIT_OUTSIDE_CTOR int64_t mParent;
nsCString mTitle;
MOZ_INIT_OUTSIDE_CTOR int32_t mIndex;
};
+ bool mTestingEnabled;
+
// Used to enable and disable the observer notifications.
bool mCanNotify;
nsCategoryCache<nsINavBookmarkObserver> mCacheObservers;
// Tracks whether we are in batch mode.
// Note: this is only tracking bookmarks batches, not history ones.
bool mBatching;
/**
* This function must be called every time a bookmark is removed.
*
* @param aURI
* Uri to test.
*/
nsresult UpdateKeywordsForRemovedBookmark(const BookmarkData& aBookmark);
+
+ already_AddRefed<mozilla::places::Database> GetDatabase();
+
+ void ReadTestingEnabled();
};
#endif // nsNavBookmarks_h_
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -275,16 +275,17 @@ PLACES_FACTORY_SINGLETON_IMPLEMENTATION(
nsNavHistory::nsNavHistory()
: mBatchLevel(0)
, mBatchDBTransaction(nullptr)
, mCachedNow(0)
, mRecentTyped(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
, mRecentLink(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
, mRecentBookmark(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
, mEmbedVisits(EMBED_VISITS_INITIAL_CACHE_LENGTH)
+ , mTestingEnabled(false)
, mHistoryEnabled(true)
, mNumVisitsForFrecency(10)
, mTagsFolder(-1)
, mDaysOfHistory(-1)
, mLastCachedStartOfDay(INT64_MAX)
, mLastCachedEndOfDay(0)
, mCanNotify(true)
, mCacheObservers("history-observers")
@@ -304,21 +305,19 @@ nsNavHistory::~nsNavHistory()
if (gHistoryService == this)
gHistoryService = nullptr;
}
nsresult
nsNavHistory::Init()
{
+ ReadTestingEnabled();
LoadPrefs();
- mDB = Database::GetDatabase();
- NS_ENSURE_STATE(mDB);
-
/*****************************************************************************
*** IMPORTANT NOTICE!
***
*** Nothing after these add observer calls should return anything but NS_OK.
*** If a failure code is returned, this nsNavHistory object will be held onto
*** by the observer service and the preference service.
****************************************************************************/
@@ -335,21 +334,43 @@ nsNavHistory::Init()
}
// Don't add code that can fail here! Do it up above, before we add our
// observers.
return NS_OK;
}
+void
+nsNavHistory::ReadTestingEnabled() {
+ mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
+}
+
+already_AddRefed<Database>
+nsNavHistory::GetDatabase()
+{
+ RefPtr<Database> DB;
+ if (mTestingEnabled) {
+ DB = Database::GetDatabase();
+ } else {
+ if (!mDB) {
+ mDB = Database::GetDatabase();
+ }
+ DB = mDB;
+ }
+ return DB.forget();
+}
+
NS_IMETHODIMP
nsNavHistory::GetDatabaseStatus(uint16_t *aDatabaseStatus)
{
NS_ENSURE_ARG_POINTER(aDatabaseStatus);
- *aDatabaseStatus = mDB->GetDatabaseStatus();
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+ *aDatabaseStatus = DB->GetDatabaseStatus();
return NS_OK;
}
uint32_t
nsNavHistory::GetRecentFlags(nsIURI *aURI)
{
uint32_t result = 0;
nsAutoCString spec;
@@ -370,17 +391,20 @@ nsNavHistory::GetRecentFlags(nsIURI *aUR
nsresult
nsNavHistory::GetIdForPage(nsIURI* aURI,
int64_t* _pageId,
nsCString& _GUID)
{
*_pageId = 0;
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT id, url, title, rev_host, visit_count, guid "
"FROM moz_places "
"WHERE url_hash = hash(:page_url) AND url = :page_url "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
@@ -407,18 +431,21 @@ nsNavHistory::GetOrCreateIdForPage(nsIUR
{
nsresult rv = GetIdForPage(aURI, _pageId, _GUID);
NS_ENSURE_SUCCESS(rv, rv);
if (*_pageId != 0) {
return NS_OK;
}
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Create a new hidden, untyped and unvisited entry.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"INSERT INTO moz_places (url, url_hash, rev_host, hidden, frecency, guid) "
"VALUES (:page_url, hash(:page_url), :rev_host, :hidden, :frecency, :guid) "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
NS_ENSURE_SUCCESS(rv, rv);
@@ -630,21 +657,24 @@ nsNavHistory::StoreLastInsertedId(const
int32_t
nsNavHistory::GetDaysOfHistory() {
MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread");
if (mDaysOfHistory != -1)
return mDaysOfHistory;
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_TRUE(DB, 0);
+
// SQLite doesn't have a CEIL() function, so we must do that later.
// We should also take into account timers resolution, that may be as bad as
// 16ms on Windows, so in some cases the difference may be 0, if the
// check is done near the visit. Thus remember to check for NULL separately.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT CAST(( "
"strftime('%s','now','localtime','utc') - "
"(SELECT MIN(visit_date)/1000000 FROM moz_historyvisits) "
") AS DOUBLE) "
"/86400, "
"strftime('%s','now','localtime','+1 day','start of day','utc') * 1000000"
);
NS_ENSURE_TRUE(stmt, 0);
@@ -1061,17 +1091,20 @@ nsNavHistory::invalidateFrecencies(const
invalidFrecenciesSQLFragment.AppendLiteral("AND id IN(");
invalidFrecenciesSQLFragment.Append(aPlaceIdsQueryString);
invalidFrecenciesSQLFragment.Append(')');
}
RefPtr<InvalidateAllFrecenciesCallback> cb =
aPlaceIdsQueryString.IsEmpty() ? new InvalidateAllFrecenciesCallback()
: nullptr;
- nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
invalidFrecenciesSQLFragment
);
NS_ENSURE_STATE(stmt);
nsCOMPtr<mozIStoragePendingStatement> ps;
nsresult rv = stmt->ExecuteAsync(cb, getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
@@ -1129,21 +1162,23 @@ nsNavHistory::CanAddURI(nsIURI* aURI, bo
// Default to false.
*canAdd = false;
// If history is disabled, don't add any entry.
if (IsHistoryDisabled()) {
return NS_OK;
}
+ RefPtr<Database> DB = GetDatabase();
+
// If the url length is over a threshold, don't add it.
nsCString spec;
nsresult rv = aURI->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
- if (!mDB || spec.Length() > mDB->MaxUrlLength()) {
+ if (!DB || spec.Length() > DB->MaxUrlLength()) {
return NS_OK;
}
nsAutoCString scheme;
rv = aURI->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
// first check the most common cases (HTTP, HTTPS) to allow in to avoid most
@@ -2216,24 +2251,27 @@ nsNavHistory::GetQueryResults(nsNavHisto
nsCString queryString;
bool paramsPresent = false;
nsNavHistory::StringHash addParams(HISTORY_DATE_CONT_LENGTH);
nsresult rv = ConstructQueryString(aQueries, aOptions, queryString,
paramsPresent, addParams);
NS_ENSURE_SUCCESS(rv,rv);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// create statement
- nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(queryString);
+ nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(queryString);
#ifdef DEBUG
if (!statement) {
nsAutoCString lastErrorString;
- (void)mDB->MainConn()->GetLastErrorString(lastErrorString);
+ (void)DB->MainConn()->GetLastErrorString(lastErrorString);
int32_t lastError = 0;
- (void)mDB->MainConn()->GetLastError(&lastError);
+ (void)DB->MainConn()->GetLastError(&lastError);
printf("Places failed to create a statement from this query:\n%s\nStorage error (%d): %s\n",
queryString.get(), lastError, lastErrorString.get());
}
#endif
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
if (paramsPresent) {
@@ -2327,18 +2365,21 @@ nsNavHistory::GetObservers(uint32_t* _co
return NS_OK;
}
// See RunInBatchMode
nsresult
nsNavHistory::BeginUpdateBatch()
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
if (mBatchLevel++ == 0) {
- mBatchDBTransaction = new mozStorageTransaction(mDB->MainConn(), false,
+ mBatchDBTransaction = new mozStorageTransaction(DB->MainConn(), false,
mozIStorageConnection::TRANSACTION_DEFERRED,
true);
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavHistoryObserver, OnBeginUpdateBatch());
}
return NS_OK;
}
@@ -2396,22 +2437,25 @@ nsNavHistory::GetHistoryDisabled(bool *_
nsresult
nsNavHistory::RemovePagesInternal(const nsCString& aPlaceIdsQueryString)
{
// Return early if there is nothing to delete.
if (aPlaceIdsQueryString.IsEmpty())
return NS_OK;
- mozStorageTransaction transaction(mDB->MainConn(), false,
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ mozStorageTransaction transaction(DB->MainConn(), false,
mozIStorageConnection::TRANSACTION_DEFERRED,
true);
// Delete all visits for the specified place ids.
- nsresult rv = mDB->MainConn()->ExecuteSimpleSQL(
+ nsresult rv = DB->MainConn()->ExecuteSimpleSQL(
NS_LITERAL_CSTRING(
"DELETE FROM moz_historyvisits WHERE place_id IN (") +
aPlaceIdsQueryString +
NS_LITERAL_CSTRING(")")
);
NS_ENSURE_SUCCESS(rv, rv);
rv = CleanupPlacesOnVisitsDelete(aPlaceIdsQueryString);
@@ -2436,18 +2480,21 @@ nsNavHistory::RemovePagesInternal(const
*/
nsresult
nsNavHistory::CleanupPlacesOnVisitsDelete(const nsCString& aPlaceIdsQueryString)
{
// Return early if there is nothing to delete.
if (aPlaceIdsQueryString.IsEmpty())
return NS_OK;
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Collect about-to-be-deleted URIs to notify onDeleteURI.
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(NS_LITERAL_CSTRING(
"SELECT h.id, h.url, h.guid, "
"(SUBSTR(h.url, 1, 6) <> 'place:' "
" AND NOT EXISTS (SELECT b.id FROM moz_bookmarks b "
"WHERE b.fk = h.id LIMIT 1)) as whole_entry "
"FROM moz_places h "
"WHERE h.id IN ( ") + aPlaceIdsQueryString + NS_LITERAL_CSTRING(")")
);
NS_ENSURE_STATE(stmt);
@@ -2486,28 +2533,28 @@ nsNavHistory::CleanupPlacesOnVisitsDelet
OnDeleteVisits(uri, 0, guid, nsINavHistoryObserver::REASON_DELETED, 0));
}
}
// if the entry is not bookmarked and is not a place: uri
// then we can remove it from moz_places.
// Note that we do NOT delete favicons. Any unreferenced favicons will be
// deleted next time the browser is shut down.
- nsresult rv = mDB->MainConn()->ExecuteSimpleSQL(
+ nsresult rv = DB->MainConn()->ExecuteSimpleSQL(
NS_LITERAL_CSTRING(
"DELETE FROM moz_places WHERE id IN ( "
) + filteredPlaceIds + NS_LITERAL_CSTRING(
") "
)
);
NS_ENSURE_SUCCESS(rv, rv);
// Hosts accumulated during the places delete are updated through a trigger
// (see nsPlacesTriggers.h).
- rv = mDB->MainConn()->ExecuteSimpleSQL(
+ rv = DB->MainConn()->ExecuteSimpleSQL(
NS_LITERAL_CSTRING("DELETE FROM moz_updatehosts_temp")
);
NS_ENSURE_SUCCESS(rv, rv);
// Invalidate frecencies of touched places, since they need recalculation.
rv = invalidateFrecencies(aPlaceIdsQueryString);
NS_ENSURE_SUCCESS(rv, rv);
@@ -2637,18 +2684,21 @@ nsNavHistory::RemovePagesFromHost(const
// build condition string based on host selection type
nsAutoCString conditionString;
if (aEntireDomain)
conditionString.AssignLiteral("rev_host >= ?1 AND rev_host < ?2 ");
else
conditionString.AssignLiteral("rev_host = ?1 ");
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// create statement depending on delete type
- nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
NS_LITERAL_CSTRING("SELECT id FROM moz_places WHERE ") + conditionString
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
rv = statement->BindStringByIndex(0, revHostDot);
NS_ENSURE_SUCCESS(rv, rv);
if (aEntireDomain) {
@@ -2692,19 +2742,22 @@ NS_IMETHODIMP
nsNavHistory::RemovePagesByTimeframe(PRTime aBeginTime, PRTime aEndTime)
{
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
nsresult rv;
// build a list of place ids to delete
nsCString deletePlaceIdsQueryString;
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// we only need to know if a place has a visit into the given timeframe
// this query is faster than actually selecting in moz_historyvisits
- nsCOMPtr<mozIStorageStatement> selectByTime = mDB->GetStatement(
+ nsCOMPtr<mozIStorageStatement> selectByTime = DB->GetStatement(
"SELECT h.id FROM moz_places h WHERE "
"EXISTS "
"(SELECT id FROM moz_historyvisits v WHERE v.place_id = h.id "
"AND v.visit_date >= :from_date AND v.visit_date <= :to_date LIMIT 1)"
);
NS_ENSURE_STATE(selectByTime);
mozStorageStatementScoper selectByTimeScoper(selectByTime);
@@ -2807,17 +2860,20 @@ nsNavHistory::GetPageTitle(nsIURI* aURI,
{
PLACES_WARN_DEPRECATED();
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
NS_ENSURE_ARG(aURI);
aTitle.Truncate(0);
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
"SELECT id, url, title, rev_host, visit_count, guid "
"FROM moz_places "
"WHERE url_hash = hash(:page_url) AND url = :page_url "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
@@ -2847,19 +2903,20 @@ nsNavHistory::GetDatabaseConnection(mozI
{
return GetDBConnection(_DBConnection);
}
NS_IMETHODIMP
nsNavHistory::GetExpectedDatabasePageSize(int32_t* _expectedPageSize)
{
- NS_ENSURE_STATE(mDB);
- NS_ENSURE_STATE(mDB->MainConn());
- return mDB->MainConn()->GetDefaultPageSize(_expectedPageSize);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+ NS_ENSURE_STATE(DB->MainConn());
+ return DB->MainConn()->GetDefaultPageSize(_expectedPageSize);
}
NS_IMETHODIMP
nsNavHistory::OnBeginVacuum(bool* _vacuumGranted)
{
// TODO: Check if we have to deny the vacuum in some heavy-load case.
// We could maybe want to do that during batches?
@@ -2878,27 +2935,31 @@ nsNavHistory::OnEndVacuum(bool aSucceede
////////////////////////////////////////////////////////////////////////////////
//// nsPIPlacesDatabase
NS_IMETHODIMP
nsNavHistory::GetDBConnection(mozIStorageConnection **_DBConnection)
{
NS_ENSURE_ARG_POINTER(_DBConnection);
- RefPtr<mozIStorageConnection> connection = mDB->MainConn();
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+ RefPtr<mozIStorageConnection> connection = DB->MainConn();
connection.forget(_DBConnection);
return NS_OK;
}
NS_IMETHODIMP
nsNavHistory::GetShutdownClient(nsIAsyncShutdownClient **_shutdownClient)
{
NS_ENSURE_ARG_POINTER(_shutdownClient);
- RefPtr<nsIAsyncShutdownClient> client = mDB->GetClientsShutdown();
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+ RefPtr<nsIAsyncShutdownClient> client = DB->GetClientsShutdown();
MOZ_ASSERT(client);
client.forget(_shutdownClient);
return NS_OK;
}
NS_IMETHODIMP
nsNavHistory::AsyncExecuteLegacyQueries(nsINavHistoryQuery** aQueries,
@@ -2926,26 +2987,29 @@ nsNavHistory::AsyncExecuteLegacyQueries(
nsCString queryString;
bool paramsPresent = false;
nsNavHistory::StringHash addParams(HISTORY_DATE_CONT_LENGTH);
nsresult rv = ConstructQueryString(queries, options, queryString,
paramsPresent, addParams);
NS_ENSURE_SUCCESS(rv,rv);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
nsCOMPtr<mozIStorageAsyncStatement> statement =
- mDB->GetAsyncStatement(queryString);
+ DB->GetAsyncStatement(queryString);
NS_ENSURE_STATE(statement);
#ifdef DEBUG
if (NS_FAILED(rv)) {
nsAutoCString lastErrorString;
- (void)mDB->MainConn()->GetLastErrorString(lastErrorString);
+ (void)DB->MainConn()->GetLastErrorString(lastErrorString);
int32_t lastError = 0;
- (void)mDB->MainConn()->GetLastError(&lastError);
+ (void)DB->MainConn()->GetLastError(&lastError);
printf("Places failed to create a statement from this query:\n%s\nStorage error (%d): %s\n",
queryString.get(), lastError, lastErrorString.get());
}
#endif
NS_ENSURE_SUCCESS(rv, rv);
if (paramsPresent) {
// bind parameters
@@ -3003,17 +3067,19 @@ nsNavHistory::Observe(nsISupports *aSubj
const char16_t *aData)
{
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
if (strcmp(aTopic, TOPIC_PROFILE_TEARDOWN) == 0 ||
strcmp(aTopic, TOPIC_PROFILE_CHANGE) == 0 ||
strcmp(aTopic, TOPIC_SIMULATE_PLACES_SHUTDOWN) == 0) {
// These notifications are used by tests to simulate a Places shutdown.
// They should just be forwarded to the Database handle.
- mDB->Observe(aSubject, aTopic, aData);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+ DB->Observe(aSubject, aTopic, aData);
}
else if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) {
// Don't even try to notify observers from this point on, the category
// cache would init services that could try to use our APIs.
mCanNotify = false;
mObservers.Clear();
}
@@ -3099,54 +3165,57 @@ public:
nsresult
nsNavHistory::DecayFrecency()
{
nsresult rv = FixInvalidFrecencies();
NS_ENSURE_SUCCESS(rv, rv);
float decayRate = Preferences::GetFloat(PREF_FREC_DECAY_RATE, PREF_FREC_DECAY_RATE_DEF);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
// Globally decay places frecency rankings to estimate reduced frecency
// values of pages that haven't been visited for a while, i.e., they do
// not get an updated frecency. A scaling factor of .975 results in .5 the
// original value after 28 days.
// When changing the scaling factor, ensure that the barrier in
// moz_places_afterupdate_frecency_trigger still ignores these changes.
- nsCOMPtr<mozIStorageAsyncStatement> decayFrecency = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> decayFrecency = DB->GetAsyncStatement(
"UPDATE moz_places SET frecency = ROUND(frecency * :decay_rate) "
"WHERE frecency > 0"
);
NS_ENSURE_STATE(decayFrecency);
rv = decayFrecency->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
static_cast<double>(decayRate));
NS_ENSURE_SUCCESS(rv, rv);
// Decay potentially unused adaptive entries (e.g. those that are at 1)
// to allow better chances for new entries that will start at 1.
- nsCOMPtr<mozIStorageAsyncStatement> decayAdaptive = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> decayAdaptive = DB->GetAsyncStatement(
"UPDATE moz_inputhistory SET use_count = use_count * .975"
);
NS_ENSURE_STATE(decayAdaptive);
// Delete any adaptive entries that won't help in ordering anymore.
- nsCOMPtr<mozIStorageAsyncStatement> deleteAdaptive = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> deleteAdaptive = DB->GetAsyncStatement(
"DELETE FROM moz_inputhistory WHERE use_count < .01"
);
NS_ENSURE_STATE(deleteAdaptive);
mozIStorageBaseStatement *stmts[] = {
decayFrecency.get(),
decayAdaptive.get(),
deleteAdaptive.get()
};
nsCOMPtr<mozIStoragePendingStatement> ps;
RefPtr<DecayFrecencyCallback> cb = new DecayFrecencyCallback();
- rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb,
- getter_AddRefs(ps));
+ rv = DB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb,
+ getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// Query stuff *****************************************************************
@@ -4000,39 +4069,42 @@ nsresult
nsNavHistory::VisitIdToResultNode(int64_t visitId,
nsNavHistoryQueryOptions* aOptions,
nsNavHistoryResultNode** aResult)
{
nsAutoCString tagsFragment;
GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
true, tagsFragment);
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
nsCOMPtr<mozIStorageStatement> statement;
switch (aOptions->ResultType())
{
case nsNavHistoryQueryOptions::RESULTS_AS_VISIT:
case nsNavHistoryQueryOptions::RESULTS_AS_FULL_VISIT:
// visit query - want exact visit time
// Should match kGetInfoIndex_* (see GetQueryResults)
- statement = mDB->GetStatement(NS_LITERAL_CSTRING(
+ statement = DB->GetStatement(NS_LITERAL_CSTRING(
"SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
"v.visit_date, f.url, null, null, null, null, "
) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
"v.id, v.from_visit, v.visit_type "
"FROM moz_places h "
"JOIN moz_historyvisits v ON h.id = v.place_id "
"LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
"WHERE v.id = :visit_id ")
);
break;
case nsNavHistoryQueryOptions::RESULTS_AS_URI:
// URL results - want last visit time
// Should match kGetInfoIndex_* (see GetQueryResults)
- statement = mDB->GetStatement(NS_LITERAL_CSTRING(
+ statement = DB->GetStatement(NS_LITERAL_CSTRING(
"SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
"h.last_visit_date, f.url, null, null, null, null, "
) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
"null, null, null "
"FROM moz_places h "
"JOIN moz_historyvisits v ON h.id = v.place_id "
"LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
"WHERE v.id = :visit_id ")
@@ -4064,21 +4136,24 @@ nsNavHistory::VisitIdToResultNode(int64_
return RowToResult(row, aOptions, aResult);
}
nsresult
nsNavHistory::BookmarkIdToResultNode(int64_t aBookmarkId, nsNavHistoryQueryOptions* aOptions,
nsNavHistoryResultNode** aResult)
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
nsAutoCString tagsFragment;
GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
true, tagsFragment);
// Should match kGetInfoIndex_*
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(NS_LITERAL_CSTRING(
"SELECT b.fk, h.url, COALESCE(b.title, h.title), "
"h.rev_host, h.visit_count, h.last_visit_date, f.url, b.id, "
"b.dateAdded, b.lastModified, b.parent, "
) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
"null, null, null, b.guid, b.position, b.type, b.fk "
"FROM moz_bookmarks b "
"JOIN moz_places h ON b.fk = h.id "
"LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
@@ -4105,21 +4180,24 @@ nsNavHistory::BookmarkIdToResultNode(int
return RowToResult(row, aOptions, aResult);
}
nsresult
nsNavHistory::URIToResultNode(nsIURI* aURI,
nsNavHistoryQueryOptions* aOptions,
nsNavHistoryResultNode** aResult)
{
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
nsAutoCString tagsFragment;
GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
true, tagsFragment);
// Should match kGetInfoIndex_*
- nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
+ nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(NS_LITERAL_CSTRING(
"SELECT h.id, :page_url, COALESCE(b.title, h.title), "
"h.rev_host, h.visit_count, h.last_visit_date, f.url, "
"b.id, b.dateAdded, b.lastModified, b.parent, "
) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
"null, null, null, b.guid, b.position, b.type, b.fk "
"FROM moz_places h "
"LEFT JOIN moz_bookmarks b ON b.fk = h.id "
"LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
@@ -4350,28 +4428,31 @@ void ParseSearchTermsFromQueries(const n
}
} // namespace
nsresult
nsNavHistory::UpdateFrecency(int64_t aPlaceId)
{
- nsCOMPtr<mozIStorageAsyncStatement> updateFrecencyStmt = mDB->GetAsyncStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageAsyncStatement> updateFrecencyStmt = DB->GetAsyncStatement(
"UPDATE moz_places "
"SET frecency = NOTIFY_FRECENCY("
"CALCULATE_FRECENCY(:page_id), url, guid, hidden, last_visit_date"
") "
"WHERE id = :page_id"
);
NS_ENSURE_STATE(updateFrecencyStmt);
nsresult rv = updateFrecencyStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
aPlaceId);
NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<mozIStorageAsyncStatement> updateHiddenStmt = mDB->GetAsyncStatement(
+ nsCOMPtr<mozIStorageAsyncStatement> updateHiddenStmt = DB->GetAsyncStatement(
"UPDATE moz_places "
"SET hidden = 0 "
"WHERE id = :page_id AND frecency <> 0"
);
NS_ENSURE_STATE(updateHiddenStmt);
rv = updateHiddenStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
aPlaceId);
NS_ENSURE_SUCCESS(rv, rv);
@@ -4379,18 +4460,18 @@ nsNavHistory::UpdateFrecency(int64_t aPl
mozIStorageBaseStatement *stmts[] = {
updateFrecencyStmt.get()
, updateHiddenStmt.get()
};
RefPtr<AsyncStatementCallbackNotifier> cb =
new AsyncStatementCallbackNotifier(TOPIC_FRECENCY_UPDATED);
nsCOMPtr<mozIStoragePendingStatement> ps;
- rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb,
- getter_AddRefs(ps));
+ rv = DB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb,
+ getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
namespace {
@@ -4415,17 +4496,20 @@ public:
}
};
} // namespace
nsresult
nsNavHistory::FixInvalidFrecencies()
{
- nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
"UPDATE moz_places "
"SET frecency = CALCULATE_FRECENCY(id) "
"WHERE frecency < 0"
);
NS_ENSURE_STATE(stmt);
RefPtr<FixInvalidFrecenciesCallback> callback =
new FixInvalidFrecenciesCallback();
@@ -4437,17 +4521,20 @@ nsNavHistory::FixInvalidFrecencies()
#ifdef MOZ_XUL
nsresult
nsNavHistory::AutoCompleteFeedback(int32_t aIndex,
nsIAutoCompleteController *aController)
{
- nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+ RefPtr<Database> DB = GetDatabase();
+ NS_ENSURE_STATE(DB);
+
+ nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
"INSERT OR REPLACE INTO moz_inputhistory "
// use_count will asymptotically approach the max of 10.
"SELECT h.id, IFNULL(i.input, :input_text), IFNULL(i.use_count, 0) * .9 + 1 "
"FROM moz_places h "
"LEFT JOIN moz_inputhistory i ON i.place_id = h.id AND i.input = :input_text "
"WHERE url_hash = hash(:page_url) AND url = :page_url "
);
NS_ENSURE_STATE(stmt);
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -594,16 +594,20 @@ protected:
const nsACString& url);
void ExpireNonrecentEvents(RecentEventHash* hashTable);
#ifdef MOZ_XUL
nsresult AutoCompleteFeedback(int32_t aIndex,
nsIAutoCompleteController *aController);
#endif
+ already_AddRefed<mozilla::places::Database> GetDatabase();
+
+ bool mTestingEnabled;
+
// Whether history is enabled or not.
// Will mimic value of the places.history.enabled preference.
bool mHistoryEnabled;
// Frecency preferences.
int32_t mNumVisitsForFrecency;
int32_t mFirstBucketCutoffInDays;
int32_t mSecondBucketCutoffInDays;
@@ -637,16 +641,18 @@ protected:
int32_t mDaysOfHistory;
int64_t mLastCachedStartOfDay;
int64_t mLastCachedEndOfDay;
// Used to enable and disable the observer notifications
bool mCanNotify;
nsCategoryCache<nsINavHistoryObserver> mCacheObservers;
+
+ void ReadTestingEnabled();
};
#define PLACES_URI_PREFIX "place:"
/* Returns true if the given URI represents a history query. */
inline bool IsQueryURI(const nsCString &uri)
{