зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1209027 - Reduce queries load on visits addition. r=adw
MozReview-Commit-ID: AvW7WB2LXZE --HG-- extra : rebase_source : bb5ab637dfe69f2b4587932bd1c506e18b81bca2
This commit is contained in:
Родитель
c8e28547cf
Коммит
22a82c8e00
|
@ -1037,6 +1037,8 @@ Database::InitFunctions()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = FrecencyNotificationFunction::create(mMainConn);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = StoreLastInsertedIdFunction::create(mMainConn);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -73,10 +73,14 @@ struct VisitData {
|
|||
: placeId(0)
|
||||
, visitId(0)
|
||||
, hidden(true)
|
||||
, shouldUpdateHidden(true)
|
||||
, typed(false)
|
||||
, transitionType(UINT32_MAX)
|
||||
, visitTime(0)
|
||||
, frecency(-1)
|
||||
, lastVisitId(0)
|
||||
, lastVisitTime(0)
|
||||
, referrerVisitId(0)
|
||||
, titleChanged(false)
|
||||
, shouldUpdateFrecency(true)
|
||||
{
|
||||
|
@ -89,10 +93,14 @@ struct VisitData {
|
|||
: placeId(0)
|
||||
, visitId(0)
|
||||
, hidden(true)
|
||||
, shouldUpdateHidden(true)
|
||||
, typed(false)
|
||||
, transitionType(UINT32_MAX)
|
||||
, visitTime(0)
|
||||
, frecency(-1)
|
||||
, lastVisitId(0)
|
||||
, lastVisitTime(0)
|
||||
, referrerVisitId(0)
|
||||
, titleChanged(false)
|
||||
, shouldUpdateFrecency(true)
|
||||
{
|
||||
|
@ -121,36 +129,19 @@ struct VisitData {
|
|||
transitionType = aTransitionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this refers to the same url as aOther, and updates aOther
|
||||
* with missing information if so.
|
||||
*
|
||||
* @param aOther
|
||||
* The other place to check against.
|
||||
* @return true if this is a visit for the same place as aOther, false
|
||||
* otherwise.
|
||||
*/
|
||||
bool IsSamePlaceAs(VisitData& aOther)
|
||||
{
|
||||
if (!spec.Equals(aOther.spec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aOther.placeId = placeId;
|
||||
aOther.guid = guid;
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t placeId;
|
||||
nsCString guid;
|
||||
int64_t visitId;
|
||||
nsCString spec;
|
||||
nsString revHost;
|
||||
bool hidden;
|
||||
bool shouldUpdateHidden;
|
||||
bool typed;
|
||||
uint32_t transitionType;
|
||||
PRTime visitTime;
|
||||
int32_t frecency;
|
||||
int64_t lastVisitId;
|
||||
PRTime lastVisitTime;
|
||||
|
||||
/**
|
||||
* Stores the title. If this is empty (IsEmpty() returns true), then the
|
||||
|
@ -161,6 +152,7 @@ struct VisitData {
|
|||
nsString title;
|
||||
|
||||
nsCString referrerSpec;
|
||||
int64_t referrerVisitId;
|
||||
|
||||
// TODO bug 626836 hook up hidden and typed change tracking too!
|
||||
bool titleChanged;
|
||||
|
@ -623,10 +615,8 @@ NS_IMPL_ISUPPORTS_INHERITED(
|
|||
class NotifyVisitObservers : public Runnable
|
||||
{
|
||||
public:
|
||||
NotifyVisitObservers(VisitData& aPlace,
|
||||
VisitData& aReferrer)
|
||||
NotifyVisitObservers(VisitData& aPlace)
|
||||
: mPlace(aPlace)
|
||||
, mReferrer(aReferrer)
|
||||
, mHistory(History::GetService())
|
||||
{
|
||||
}
|
||||
|
@ -657,7 +647,7 @@ public:
|
|||
// to the database, thus cannot be queried and we don't notify them.
|
||||
if (mPlace.transitionType != nsINavHistoryService::TRANSITION_EMBED) {
|
||||
navHistory->NotifyOnVisit(uri, mPlace.visitId, mPlace.visitTime,
|
||||
mReferrer.visitId, mPlace.transitionType,
|
||||
mPlace.referrerVisitId, mPlace.transitionType,
|
||||
mPlace.guid, mPlace.hidden);
|
||||
}
|
||||
|
||||
|
@ -678,7 +668,6 @@ public:
|
|||
}
|
||||
private:
|
||||
VisitData mPlace;
|
||||
VisitData mReferrer;
|
||||
RefPtr<History> mHistory;
|
||||
};
|
||||
|
||||
|
@ -927,7 +916,6 @@ public:
|
|||
VisitData* lastFetchedPlace = nullptr;
|
||||
for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
|
||||
VisitData& place = mPlaces.ElementAt(i);
|
||||
VisitData& referrer = mReferrers.ElementAt(i);
|
||||
|
||||
// Fetching from the database can overwrite this information, so save it
|
||||
// apart.
|
||||
|
@ -936,7 +924,7 @@ public:
|
|||
|
||||
// We can avoid a database lookup if it's the same place as the last
|
||||
// visit we added.
|
||||
bool known = lastFetchedPlace && lastFetchedPlace->IsSamePlaceAs(place);
|
||||
bool known = lastFetchedPlace && lastFetchedPlace->spec.Equals(place.spec);
|
||||
if (!known) {
|
||||
nsresult rv = mHistory->FetchPageInfo(place, &known);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -948,6 +936,14 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
lastFetchedPlace = &mPlaces.ElementAt(i);
|
||||
} else {
|
||||
// Copy over the data from the already known place.
|
||||
place.placeId = lastFetchedPlace->placeId;
|
||||
place.guid = lastFetchedPlace->guid;
|
||||
place.lastVisitId = lastFetchedPlace->visitId;
|
||||
place.lastVisitTime = lastFetchedPlace->visitTime;
|
||||
place.titleChanged = !lastFetchedPlace->title.Equals(place.title);
|
||||
place.frecency = lastFetchedPlace->frecency;
|
||||
}
|
||||
|
||||
// If any transition is typed, ensure the page is marked as typed.
|
||||
|
@ -960,9 +956,15 @@ public:
|
|||
place.hidden = false;
|
||||
}
|
||||
|
||||
FetchReferrerInfo(referrer, place);
|
||||
// If this is a new page, or the existing page was already visible,
|
||||
// there's no need to try to unhide it.
|
||||
if (!known || !lastFetchedPlace->hidden) {
|
||||
place.shouldUpdateHidden = false;
|
||||
}
|
||||
|
||||
nsresult rv = DoDatabaseInserts(known, place, referrer);
|
||||
FetchReferrerInfo(place);
|
||||
|
||||
nsresult rv = DoDatabaseInserts(known, place);
|
||||
if (!!mCallback) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
new NotifyPlaceInfoCallback(mCallback, place, true, rv);
|
||||
|
@ -971,7 +973,7 @@ public:
|
|||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(place, referrer);
|
||||
nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(place);
|
||||
rv = NS_DispatchToMainThread(event);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -999,18 +1001,15 @@ private:
|
|||
MOZ_ASSERT(NS_IsMainThread(), "This should be called on the main thread");
|
||||
|
||||
mPlaces.SwapElements(aPlaces);
|
||||
mReferrers.SetLength(mPlaces.Length());
|
||||
|
||||
for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
|
||||
mReferrers[i].spec = mPlaces[i].referrerSpec;
|
||||
|
||||
#ifdef DEBUG
|
||||
for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), mPlaces[i].spec)));
|
||||
NS_ASSERTION(CanAddURI(uri),
|
||||
"Passed a VisitData with a URI we cannot add to history!");
|
||||
#endif
|
||||
MOZ_ASSERT(CanAddURI(uri),
|
||||
"Passed a VisitData with a URI we cannot add to history!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1022,12 +1021,9 @@ private:
|
|||
* otherwise.
|
||||
* @param aPlace
|
||||
* The place we are adding a visit for.
|
||||
* @param aReferrer
|
||||
* The referrer for aPlace.
|
||||
*/
|
||||
nsresult DoDatabaseInserts(bool aKnown,
|
||||
VisitData& aPlace,
|
||||
VisitData& aReferrer)
|
||||
VisitData& aPlace)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "This should not be called on the main thread");
|
||||
|
||||
|
@ -1041,22 +1037,11 @@ private:
|
|||
else {
|
||||
rv = mHistory->InsertPlace(aPlace);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We need the place id and guid of the page we just inserted when we
|
||||
// have a callback or when the GUID isn't known. No point in doing the
|
||||
// disk I/O if we do not need it.
|
||||
if (!!mCallback || aPlace.guid.IsEmpty()) {
|
||||
bool exists;
|
||||
rv = mHistory->FetchPageInfo(aPlace, &exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!exists) {
|
||||
NS_NOTREACHED("should have an entry in moz_places");
|
||||
}
|
||||
}
|
||||
aPlace.placeId = nsNavHistory::sLastInsertedPlaceId;
|
||||
}
|
||||
MOZ_ASSERT(aPlace.placeId > 0);
|
||||
|
||||
rv = AddVisit(aPlace, aReferrer);
|
||||
rv = AddVisit(aPlace);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// TODO (bug 623969) we shouldn't update this after each visit, but
|
||||
|
@ -1071,102 +1056,42 @@ private:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads visit information about the page into _place.
|
||||
*
|
||||
* @param _place
|
||||
* The VisitData for the place we need to know visit information about.
|
||||
* @param [optional] aThresholdStart
|
||||
* The timestamp of a new visit (not represented by _place) used to
|
||||
* determine if the page was recently visited or not.
|
||||
* @return true if the page was recently (determined with aThresholdStart)
|
||||
* visited, false otherwise.
|
||||
*/
|
||||
bool FetchVisitInfo(VisitData& _place,
|
||||
PRTime aThresholdStart = 0)
|
||||
{
|
||||
NS_PRECONDITION(!_place.spec.IsEmpty(), "must have a non-empty spec!");
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
// If we have a visitTime, we want information on that specific visit.
|
||||
if (_place.visitTime) {
|
||||
stmt = mHistory->GetStatement(
|
||||
"SELECT id, visit_date "
|
||||
"FROM moz_historyvisits "
|
||||
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) "
|
||||
"AND visit_date = :visit_date "
|
||||
);
|
||||
NS_ENSURE_TRUE(stmt, false);
|
||||
|
||||
mozStorageStatementScoper scoper(stmt);
|
||||
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("visit_date"),
|
||||
_place.visitTime);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
scoper.Abandon();
|
||||
}
|
||||
// Otherwise, we want information about the most recent visit.
|
||||
else {
|
||||
stmt = mHistory->GetStatement(
|
||||
"SELECT id, visit_date "
|
||||
"FROM moz_historyvisits "
|
||||
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) "
|
||||
"ORDER BY visit_date DESC "
|
||||
);
|
||||
NS_ENSURE_TRUE(stmt, false);
|
||||
}
|
||||
mozStorageStatementScoper scoper(stmt);
|
||||
|
||||
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
|
||||
_place.spec);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
bool hasResult;
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
if (!hasResult) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rv = stmt->GetInt64(0, &_place.visitId);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
rv = stmt->GetInt64(1, reinterpret_cast<int64_t*>(&_place.visitTime));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// If we have been given a visit threshold start time, go ahead and
|
||||
// calculate if we have been recently visited.
|
||||
if (aThresholdStart &&
|
||||
aThresholdStart - _place.visitTime <= RECENT_EVENT_THRESHOLD) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches information about a referrer for aPlace if it was a recent
|
||||
* visit or not.
|
||||
*
|
||||
* @param aReferrer
|
||||
* The VisitData for the referrer. This will be populated with
|
||||
* FetchVisitInfo.
|
||||
* @param aPlace
|
||||
* The VisitData for the visit we will eventually add.
|
||||
*
|
||||
*/
|
||||
void FetchReferrerInfo(VisitData& aReferrer,
|
||||
VisitData& aPlace)
|
||||
void FetchReferrerInfo(VisitData& aPlace)
|
||||
{
|
||||
if (aReferrer.spec.IsEmpty()) {
|
||||
if (aPlace.referrerSpec.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FetchVisitInfo(aReferrer, aPlace.visitTime)) {
|
||||
// We must change both the place and referrer to indicate that we will
|
||||
// not be using the referrer's data. This behavior has test coverage, so
|
||||
// if this invariant changes, we'll know.
|
||||
VisitData referrer;
|
||||
referrer.spec = aPlace.referrerSpec;
|
||||
// If the referrer is the same as the page, we don't need to fetch it.
|
||||
if (aPlace.referrerSpec.Equals(aPlace.spec)) {
|
||||
referrer = aPlace;
|
||||
// The page last visit id is also the referrer visit id.
|
||||
aPlace.referrerVisitId = aPlace.lastVisitId;
|
||||
} else {
|
||||
bool exists = false;
|
||||
if (NS_SUCCEEDED(mHistory->FetchPageInfo(referrer, &exists)) && exists) {
|
||||
// Copy the referrer last visit id.
|
||||
aPlace.referrerVisitId = referrer.lastVisitId;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the page has effectively been visited recently, otherwise
|
||||
// discard the referrer info.
|
||||
if (!aPlace.referrerVisitId || !referrer.lastVisitTime ||
|
||||
aPlace.visitTime - referrer.lastVisitTime > RECENT_EVENT_THRESHOLD) {
|
||||
// We will not be using the referrer data.
|
||||
aPlace.referrerSpec.Truncate();
|
||||
aReferrer.visitId = 0;
|
||||
aPlace.referrerVisitId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1175,36 +1100,25 @@ private:
|
|||
*
|
||||
* @param _place
|
||||
* The VisitData for the place we need to know visit information about.
|
||||
* @param aReferrer
|
||||
* A reference to the referrer's visit data.
|
||||
*/
|
||||
nsresult AddVisit(VisitData& _place,
|
||||
const VisitData& aReferrer)
|
||||
nsresult AddVisit(VisitData& _place)
|
||||
{
|
||||
MOZ_ASSERT(_place.placeId > 0);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
if (_place.placeId) {
|
||||
stmt = mHistory->GetStatement(
|
||||
"INSERT INTO moz_historyvisits "
|
||||
"(from_visit, place_id, visit_date, visit_type, session) "
|
||||
"VALUES (:from_visit, :page_id, :visit_date, :visit_type, 0) "
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), _place.placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
stmt = mHistory->GetStatement(
|
||||
"INSERT INTO moz_historyvisits "
|
||||
"(from_visit, place_id, visit_date, visit_type, session) "
|
||||
"VALUES (:from_visit, (SELECT id FROM moz_places WHERE url = :page_url), :visit_date, :visit_type, 0) "
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), _place.spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
stmt = mHistory->GetStatement(
|
||||
"INSERT INTO moz_historyvisits "
|
||||
"(from_visit, place_id, visit_date, visit_type, session) "
|
||||
"VALUES (:from_visit, :page_id, :visit_date, :visit_type, 0) "
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
mozStorageStatementScoper scoper(stmt);
|
||||
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), _place.placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("from_visit"),
|
||||
aReferrer.visitId);
|
||||
_place.referrerVisitId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("visit_date"),
|
||||
_place.visitTime);
|
||||
|
@ -1217,13 +1131,11 @@ private:
|
|||
transitionType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozStorageStatementScoper scoper(stmt);
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Now that it should be in the database, we need to obtain the id of the
|
||||
// visit we just added.
|
||||
(void)FetchVisitInfo(_place);
|
||||
_place.visitId = nsNavHistory::sLastInsertedVisitId;
|
||||
MOZ_ASSERT(_place.visitId > 0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1237,66 +1149,43 @@ private:
|
|||
nsresult UpdateFrecency(const VisitData& aPlace)
|
||||
{
|
||||
MOZ_ASSERT(aPlace.shouldUpdateFrecency);
|
||||
MOZ_ASSERT(aPlace.placeId > 0);
|
||||
|
||||
nsresult rv;
|
||||
{ // First, set our frecency to the proper value.
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
if (aPlace.placeId) {
|
||||
stmt = mHistory->GetStatement(
|
||||
"UPDATE moz_places "
|
||||
"SET frecency = NOTIFY_FRECENCY("
|
||||
"CALCULATE_FRECENCY(:page_id), "
|
||||
"url, guid, hidden, last_visit_date"
|
||||
") "
|
||||
"WHERE id = :page_id"
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlace.placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
stmt = mHistory->GetStatement(
|
||||
"UPDATE moz_places "
|
||||
"SET frecency = NOTIFY_FRECENCY("
|
||||
"CALCULATE_FRECENCY(id), url, guid, hidden, last_visit_date"
|
||||
") "
|
||||
"WHERE url = :page_url"
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aPlace.spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
stmt = mHistory->GetStatement(
|
||||
"UPDATE moz_places "
|
||||
"SET frecency = NOTIFY_FRECENCY("
|
||||
"CALCULATE_FRECENCY(:page_id), "
|
||||
"url, guid, hidden, last_visit_date"
|
||||
") "
|
||||
"WHERE id = :page_id"
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
mozStorageStatementScoper scoper(stmt);
|
||||
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlace.placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!aPlace.hidden) {
|
||||
if (!aPlace.hidden && aPlace.shouldUpdateHidden) {
|
||||
// Mark the page as not hidden if the frecency is now nonzero.
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
if (aPlace.placeId) {
|
||||
stmt = mHistory->GetStatement(
|
||||
"UPDATE moz_places "
|
||||
"SET hidden = 0 "
|
||||
"WHERE id = :page_id AND frecency <> 0"
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlace.placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
stmt = mHistory->GetStatement(
|
||||
"UPDATE moz_places "
|
||||
"SET hidden = 0 "
|
||||
"WHERE url = :page_url AND frecency <> 0"
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aPlace.spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
stmt = mHistory->GetStatement(
|
||||
"UPDATE moz_places "
|
||||
"SET hidden = 0 "
|
||||
"WHERE id = :page_id AND frecency <> 0"
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
mozStorageStatementScoper scoper(stmt);
|
||||
|
||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlace.placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -1307,7 +1196,6 @@ private:
|
|||
mozIStorageConnection* mDBConn;
|
||||
|
||||
nsTArray<VisitData> mPlaces;
|
||||
nsTArray<VisitData> mReferrers;
|
||||
|
||||
nsMainThreadPtrHandle<mozIVisitInfoCallback> mCallback;
|
||||
|
||||
|
@ -1428,8 +1316,8 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mPlace.placeId > 0,
|
||||
"We somehow have an invalid place id here!");
|
||||
MOZ_ASSERT(mPlace.placeId > 0,
|
||||
"We somehow have an invalid place id here!");
|
||||
|
||||
// Now we can update our database record.
|
||||
nsCOMPtr<mozIStorageStatement> stmt =
|
||||
|
@ -1954,8 +1842,7 @@ StoreAndNotifyEmbedVisit(VisitData& aPlace,
|
|||
(void)NS_DispatchToMainThread(event);
|
||||
}
|
||||
|
||||
VisitData noReferrer;
|
||||
nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(aPlace, noReferrer);
|
||||
nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(aPlace);
|
||||
(void)NS_DispatchToMainThread(event);
|
||||
}
|
||||
|
||||
|
@ -2135,10 +2022,11 @@ History::GetIsVisitedStatement(mozIStorageCompletionCallback* aCallback)
|
|||
}
|
||||
|
||||
nsresult
|
||||
History::InsertPlace(const VisitData& aPlace)
|
||||
History::InsertPlace(VisitData& aPlace)
|
||||
{
|
||||
NS_PRECONDITION(aPlace.placeId == 0, "should not have a valid place id!");
|
||||
NS_PRECONDITION(!NS_IsMainThread(), "must be called off of the main thread!");
|
||||
MOZ_ASSERT(aPlace.placeId == 0, "should not have a valid place id!");
|
||||
MOZ_ASSERT(!aPlace.shouldUpdateHidden, "We should not need to update hidden");
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "must be called off of the main thread!");
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt = GetStatement(
|
||||
"INSERT INTO moz_places "
|
||||
|
@ -2172,12 +2060,11 @@ History::InsertPlace(const VisitData& aPlace)
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), aPlace.hidden);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsAutoCString guid(aPlace.guid);
|
||||
if (aPlace.guid.IsVoid()) {
|
||||
rv = GenerateGUID(guid);
|
||||
rv = GenerateGUID(aPlace.guid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aPlace.guid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -2185,7 +2072,8 @@ History::InsertPlace(const VisitData& aPlace)
|
|||
// Post an onFrecencyChanged observer notification.
|
||||
const nsNavHistory* navHistory = nsNavHistory::GetConstHistoryService();
|
||||
NS_ENSURE_STATE(navHistory);
|
||||
navHistory->DispatchFrecencyChangedNotification(aPlace.spec, frecency, guid,
|
||||
navHistory->DispatchFrecencyChangedNotification(aPlace.spec, frecency,
|
||||
aPlace.guid,
|
||||
aPlace.hidden,
|
||||
aPlace.visitTime);
|
||||
|
||||
|
@ -2195,9 +2083,9 @@ History::InsertPlace(const VisitData& aPlace)
|
|||
nsresult
|
||||
History::UpdatePlace(const VisitData& aPlace)
|
||||
{
|
||||
NS_PRECONDITION(!NS_IsMainThread(), "must be called off of the main thread!");
|
||||
NS_PRECONDITION(aPlace.placeId > 0, "must have a valid place id!");
|
||||
NS_PRECONDITION(!aPlace.guid.IsVoid(), "must have a guid!");
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "must be called off of the main thread!");
|
||||
MOZ_ASSERT(aPlace.placeId > 0, "must have a valid place id!");
|
||||
MOZ_ASSERT(!aPlace.guid.IsVoid(), "must have a guid!");
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt = GetStatement(
|
||||
"UPDATE moz_places "
|
||||
|
@ -2238,8 +2126,8 @@ History::UpdatePlace(const VisitData& aPlace)
|
|||
nsresult
|
||||
History::FetchPageInfo(VisitData& _place, bool* _exists)
|
||||
{
|
||||
NS_PRECONDITION(!_place.spec.IsEmpty() || !_place.guid.IsEmpty(), "must have either a non-empty spec or guid!");
|
||||
NS_PRECONDITION(!NS_IsMainThread(), "must be called off of the main thread!");
|
||||
MOZ_ASSERT(!_place.spec.IsEmpty() || !_place.guid.IsEmpty(), "must have either a non-empty spec or guid!");
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "must be called off of the main thread!");
|
||||
|
||||
nsresult rv;
|
||||
|
||||
|
@ -2248,8 +2136,10 @@ History::FetchPageInfo(VisitData& _place, bool* _exists)
|
|||
bool selectByURI = !_place.spec.IsEmpty();
|
||||
if (selectByURI) {
|
||||
stmt = GetStatement(
|
||||
"SELECT guid, id, title, hidden, typed, frecency "
|
||||
"FROM moz_places "
|
||||
"SELECT guid, id, title, hidden, typed, frecency, last_visit_date, "
|
||||
"(SELECT id FROM moz_historyvisits "
|
||||
"WHERE place_id = h.id AND visit_date = h.last_visit_date) AS last_visit_id "
|
||||
"FROM moz_places h "
|
||||
"WHERE url = :page_url "
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
|
@ -2259,8 +2149,10 @@ History::FetchPageInfo(VisitData& _place, bool* _exists)
|
|||
}
|
||||
else {
|
||||
stmt = GetStatement(
|
||||
"SELECT url, id, title, hidden, typed, frecency "
|
||||
"FROM moz_places "
|
||||
"SELECT url, id, title, hidden, typed, frecency, last_visit_date, "
|
||||
"(SELECT id FROM moz_historyvisits "
|
||||
"WHERE place_id = h.id AND visit_date = h.last_visit_date) AS last_visit_id "
|
||||
"FROM moz_places h "
|
||||
"WHERE guid = :guid "
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
|
@ -2323,6 +2215,11 @@ History::FetchPageInfo(VisitData& _place, bool* _exists)
|
|||
|
||||
rv = stmt->GetInt32(5, &_place.frecency);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->GetInt64(6, &_place.lastVisitTime);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->GetInt64(7, &_place.lastVisitId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ public:
|
|||
* @param aVisitData
|
||||
* The visit data to use to populate a new row in moz_places.
|
||||
*/
|
||||
nsresult InsertPlace(const VisitData& aVisitData);
|
||||
nsresult InsertPlace(VisitData& aVisitData);
|
||||
|
||||
/**
|
||||
* Updates an entry in moz_places with the data in aVisitData.
|
||||
|
|
|
@ -844,5 +844,55 @@ namespace places {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Store Last Inserted Id Function
|
||||
|
||||
StoreLastInsertedIdFunction::~StoreLastInsertedIdFunction()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
StoreLastInsertedIdFunction::create(mozIStorageConnection *aDBConn)
|
||||
{
|
||||
RefPtr<StoreLastInsertedIdFunction> function =
|
||||
new StoreLastInsertedIdFunction();
|
||||
nsresult rv = aDBConn->CreateFunction(
|
||||
NS_LITERAL_CSTRING("store_last_inserted_id"), 2, function
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(
|
||||
StoreLastInsertedIdFunction,
|
||||
mozIStorageFunction
|
||||
)
|
||||
|
||||
NS_IMETHODIMP
|
||||
StoreLastInsertedIdFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
|
||||
nsIVariant **_result)
|
||||
{
|
||||
uint32_t numArgs;
|
||||
nsresult rv = aArgs->GetNumEntries(&numArgs);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(numArgs == 2);
|
||||
|
||||
nsAutoCString table;
|
||||
rv = aArgs->GetUTF8String(0, table);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int64_t lastInsertedId = aArgs->AsInt64(1);
|
||||
|
||||
nsNavHistory::StoreLastInsertedId(table, lastInsertedId);
|
||||
|
||||
RefPtr<nsVariant> result = new nsVariant();
|
||||
rv = result->SetAsInt64(lastInsertedId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
result.forget(_result);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace places
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -324,6 +324,34 @@ public:
|
|||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Store Last Inserted Id Function
|
||||
|
||||
/**
|
||||
* Store the last inserted id for reference purpose.
|
||||
*
|
||||
* @param tableName
|
||||
* The table name.
|
||||
* @param id
|
||||
* The last inserted id.
|
||||
* @return null
|
||||
*/
|
||||
class StoreLastInsertedIdFunction final : public mozIStorageFunction
|
||||
{
|
||||
~StoreLastInsertedIdFunction();
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGEFUNCTION
|
||||
|
||||
/**
|
||||
* Registers the function with the specified database connection.
|
||||
*
|
||||
* @param aDBConn
|
||||
* The database connection to register with.
|
||||
*/
|
||||
static nsresult create(mozIStorageConnection *aDBConn);
|
||||
};
|
||||
|
||||
} // namespace places
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -405,8 +405,8 @@ nsNavHistory::GetOrCreateIdForPage(nsIURI* aURI,
|
|||
|
||||
// Create a new hidden, untyped and unvisited entry.
|
||||
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
|
||||
"INSERT OR IGNORE INTO moz_places (url, rev_host, hidden, frecency, guid) "
|
||||
"VALUES (:page_url, :rev_host, :hidden, :frecency, GENERATE_GUID()) "
|
||||
"INSERT INTO moz_places (url, rev_host, hidden, frecency, guid) "
|
||||
"VALUES (:page_url, :rev_host, :hidden, :frecency, :guid) "
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
mozStorageStatementScoper scoper(stmt);
|
||||
|
@ -431,28 +431,16 @@ nsNavHistory::GetOrCreateIdForPage(nsIURI* aURI,
|
|||
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("frecency"),
|
||||
IsQueryURI(spec) ? 0 : -1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsAutoCString guid;
|
||||
rv = GenerateGUID(_GUID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), _GUID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> getIdStmt = mDB->GetStatement(
|
||||
"SELECT id, guid FROM moz_places WHERE url = :page_url "
|
||||
);
|
||||
NS_ENSURE_STATE(getIdStmt);
|
||||
mozStorageStatementScoper getIdScoper(getIdStmt);
|
||||
|
||||
rv = URIBinder::Bind(getIdStmt, NS_LITERAL_CSTRING("page_url"), aURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasResult = false;
|
||||
rv = getIdStmt->ExecuteStep(&hasResult);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
|
||||
*_pageId = getIdStmt->AsInt64(0);
|
||||
rv = getIdStmt->GetUTF8String(1, _GUID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
*_pageId = sLastInsertedPlaceId;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -613,6 +601,21 @@ nsNavHistory::DispatchFrecencyChangedNotification(const nsACString& aSpec,
|
|||
(void)NS_DispatchToMainThread(notif);
|
||||
}
|
||||
|
||||
Atomic<int64_t> nsNavHistory::sLastInsertedPlaceId(0);
|
||||
Atomic<int64_t> nsNavHistory::sLastInsertedVisitId(0);
|
||||
|
||||
void // static
|
||||
nsNavHistory::StoreLastInsertedId(const nsACString& aTable,
|
||||
const int64_t aLastInsertedId) {
|
||||
if (aTable.Equals(NS_LITERAL_CSTRING("moz_places"))) {
|
||||
nsNavHistory::sLastInsertedPlaceId = aLastInsertedId;
|
||||
} else if (aTable.Equals(NS_LITERAL_CSTRING("moz_historyvisits"))) {
|
||||
nsNavHistory::sLastInsertedVisitId = aLastInsertedId;
|
||||
} else {
|
||||
MOZ_ASSERT(false, "Trying to store the insert id for an unknown table?");
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsNavHistory::GetDaysOfHistory() {
|
||||
MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread");
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "nsNavHistoryQuery.h"
|
||||
#include "Database.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
|
||||
#define QUERYUPDATE_TIME 0
|
||||
#define QUERYUPDATE_SIMPLE 1
|
||||
|
@ -467,6 +468,15 @@ public:
|
|||
bool aHidden,
|
||||
PRTime aLastVisitDate) const;
|
||||
|
||||
/**
|
||||
* Store last insterted id for a table.
|
||||
*/
|
||||
static mozilla::Atomic<int64_t> sLastInsertedPlaceId;
|
||||
static mozilla::Atomic<int64_t> sLastInsertedVisitId;
|
||||
|
||||
static void StoreLastInsertedId(const nsACString& aTable,
|
||||
const int64_t aLastInsertedId);
|
||||
|
||||
bool isBatching() {
|
||||
return mBatchLevel > 0;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
"CREATE TEMP TRIGGER moz_historyvisits_afterinsert_v2_trigger " \
|
||||
"AFTER INSERT ON moz_historyvisits FOR EACH ROW " \
|
||||
"BEGIN " \
|
||||
"SELECT store_last_inserted_id('moz_historyvisits', NEW.id); " \
|
||||
"UPDATE moz_places SET " \
|
||||
"visit_count = visit_count + (SELECT NEW.visit_type NOT IN (" EXCLUDED_VISIT_TYPES ")), "\
|
||||
"last_visit_date = MAX(IFNULL(last_visit_date, 0), NEW.visit_date) " \
|
||||
|
@ -94,20 +95,20 @@
|
|||
#define CREATE_PLACES_AFTERINSERT_TRIGGER NS_LITERAL_CSTRING( \
|
||||
"CREATE TEMP TRIGGER moz_places_afterinsert_trigger " \
|
||||
"AFTER INSERT ON moz_places FOR EACH ROW " \
|
||||
"WHEN LENGTH(NEW.rev_host) > 1 " \
|
||||
"BEGIN " \
|
||||
"SELECT store_last_inserted_id('moz_places', NEW.id); " \
|
||||
"INSERT OR REPLACE INTO moz_hosts (id, host, frecency, typed, prefix) " \
|
||||
"VALUES (" \
|
||||
"(SELECT id FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), " \
|
||||
"fixup_url(get_unreversed_host(NEW.rev_host)), " \
|
||||
"MAX(IFNULL((SELECT frecency FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), -1), NEW.frecency), " \
|
||||
"MAX(IFNULL((SELECT typed FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), 0), NEW.typed), " \
|
||||
"(" HOSTS_PREFIX_PRIORITY_FRAGMENT \
|
||||
"FROM ( " \
|
||||
"SELECT fixup_url(get_unreversed_host(NEW.rev_host)) AS host " \
|
||||
") AS match " \
|
||||
") " \
|
||||
"); " \
|
||||
"SELECT " \
|
||||
"(SELECT id FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), " \
|
||||
"fixup_url(get_unreversed_host(NEW.rev_host)), " \
|
||||
"MAX(IFNULL((SELECT frecency FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), -1), NEW.frecency), " \
|
||||
"MAX(IFNULL((SELECT typed FROM moz_hosts WHERE host = fixup_url(get_unreversed_host(NEW.rev_host))), 0), NEW.typed), " \
|
||||
"(" HOSTS_PREFIX_PRIORITY_FRAGMENT \
|
||||
"FROM ( " \
|
||||
"SELECT fixup_url(get_unreversed_host(NEW.rev_host)) AS host " \
|
||||
") AS match " \
|
||||
") " \
|
||||
" WHERE LENGTH(NEW.rev_host) > 1; " \
|
||||
"END" \
|
||||
)
|
||||
|
||||
|
|
|
@ -57,11 +57,16 @@ this.PlacesTestUtils = Object.freeze({
|
|||
if (typeof place.uri == "string") {
|
||||
place.uri = NetUtil.newURI(place.uri);
|
||||
} else if (place.uri instanceof URL) {
|
||||
place.uri = NetUtil.newURI(place.href);
|
||||
place.uri = NetUtil.newURI(place.uri.href);
|
||||
}
|
||||
if (typeof place.title != "string") {
|
||||
place.title = "test visit for " + place.uri.spec;
|
||||
}
|
||||
if (typeof place.referrer == "string") {
|
||||
place.referrer = NetUtil.newURI(place.referrer);
|
||||
} else if (place.referrer instanceof URL) {
|
||||
place.referrer = NetUtil.newURI(place.referrer.href);
|
||||
}
|
||||
place.visits = [{
|
||||
transitionType: place.transition === undefined ? Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
: place.transition,
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// Test that repeated additions of the same URI through updatePlaces, properly
|
||||
// update from_visit and notify titleChanged.
|
||||
|
||||
add_task(function* test() {
|
||||
let uri = "http://test.com/";
|
||||
|
||||
let promiseTitleChangedNotifications = new Promise(resolve => {
|
||||
let historyObserver = {
|
||||
_count: 0,
|
||||
__proto__: NavHistoryObserver.prototype,
|
||||
onTitleChanged(aURI, aTitle, aGUID) {
|
||||
Assert.equal(aURI.spec, uri, "Should notify the proper url");
|
||||
if (++this._count == 2) {
|
||||
PlacesUtils.history.removeObserver(historyObserver);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
};
|
||||
PlacesUtils.history.addObserver(historyObserver, false);
|
||||
});
|
||||
|
||||
// This repeats the url on purpose, don't merge it into a single place entry.
|
||||
yield PlacesTestUtils.addVisits([
|
||||
{ uri, title: "test" },
|
||||
{ uri, referrer: uri, title: "test2" },
|
||||
]);
|
||||
|
||||
let options = PlacesUtils.history.getNewQueryOptions();
|
||||
let query = PlacesUtils.history.getNewQuery();
|
||||
query.uri = NetUtil.newURI(uri);
|
||||
options.resultType = options.RESULTS_AS_VISIT;
|
||||
let root = PlacesUtils.history.executeQuery(query, options).root;
|
||||
root.containerOpen = true;
|
||||
|
||||
Assert.equal(root.childCount, 2);
|
||||
|
||||
let child = root.getChild(0);
|
||||
Assert.equal(child.visitType, TRANSITION_LINK, "Visit type should be TRANSITION_LINK");
|
||||
Assert.equal(child.visitId, 1, "Visit ID should be 1");
|
||||
Assert.equal(child.fromVisitId, -1, "Should have no referrer visit ID");
|
||||
Assert.equal(child.title, "test2", "Should have the correct title");
|
||||
|
||||
child = root.getChild(1);
|
||||
Assert.equal(child.visitType, TRANSITION_LINK, "Visit type should be TRANSITION_LINK");
|
||||
Assert.equal(child.visitId, 2, "Visit ID should be 2");
|
||||
Assert.equal(child.fromVisitId, 1, "First visit should be the referring visit");
|
||||
Assert.equal(child.title, "test2", "Should have the correct title");
|
||||
|
||||
root.containerOpen = false;
|
||||
|
||||
yield promiseTitleChangedNotifications;
|
||||
});
|
|
@ -6,3 +6,4 @@ tail =
|
|||
[test_remove.js]
|
||||
[test_removeVisits.js]
|
||||
[test_removeVisitsByFilter.js]
|
||||
[test_updatePlaces_sameUri_titleChanged.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче