--- a/browser/components/extensions/parent/ext-history.js
+++ b/browser/components/extensions/parent/ext-history.js
@@ -89,27 +89,27 @@ const convertNavHistoryContainerResultNo
var _observer;
const getHistoryObserver = () => {
if (!_observer) {
_observer = new class extends EventEmitter {
onDeleteURI(uri, guid, reason) {
this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
}
- onVisits(visits) {
- for (let visit of visits) {
- let data = {
- id: visit.guid,
- url: visit.uri.spec,
- title: visit.lastKnownTitle || "",
- lastVisitTime: visit.time / 1000, // time from Places is microseconds,
- visitCount: visit.visitCount,
- typedCount: visit.typed,
+ handlePlacesEvents(events) {
+ for (let event of events) {
+ let visit = {
+ id: event.pageGuid,
+ url: event.url,
+ title: event.lastKnownTitle || "",
+ lastVisitTime: event.visitTime,
+ visitCount: event.visitCount,
+ typedCount: event.typedCount,
};
- this.emit("visited", data);
+ this.emit("visited", visit);
}
}
onBeginUpdateBatch() {}
onEndUpdateBatch() {}
onTitleChanged(uri, title) {
this.emit("titleChanged", {url: uri.spec, title: title});
}
onClearHistory() {
@@ -117,16 +117,19 @@ const getHistoryObserver = () => {
}
onPageChanged() {}
onFrecencyChanged() {}
onManyFrecenciesChanged() {}
onDeleteVisits(uri, time, guid, reason) {
this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
}
}();
+ PlacesUtils.observers.addListener(
+ ["page-visited"],
+ _observer.handlePlacesEvents.bind(_observer));
PlacesUtils.history.addObserver(_observer);
}
return _observer;
};
this.history = class extends ExtensionAPI {
getAPI(context) {
return {
--- a/browser/extensions/activity-stream/lib/PlacesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/PlacesFeed.jsm
@@ -55,17 +55,16 @@ class HistoryObserver extends Observer {
this.dispatch({type: at.PLACES_HISTORY_CLEARED});
}
// Empty functions to make xpconnect happy
onBeginUpdateBatch() {}
onEndUpdateBatch() {}
- onVisits() {}
onTitleChanged() {}
onFrecencyChanged() {}
onManyFrecenciesChanged() {}
onPageChanged() {}
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -811,17 +811,16 @@ var AeroPeek = {
});
break;
}
},
/* nsINavHistoryObserver implementation */
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
- onVisits() {},
onTitleChanged() {},
onFrecencyChanged() {},
onManyFrecenciesChanged() {},
onDeleteURI() {},
onClearHistory() {},
onDeleteVisits() {},
onPageChanged(uri, changedConst, newValue) {
if (this.enabled && changedConst == Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
--- a/services/sync/modules/engines/history.js
+++ b/services/sync/modules/engines/history.js
@@ -478,21 +478,27 @@ function HistoryTracker(name, engine) {
Tracker.call(this, name, engine);
}
HistoryTracker.prototype = {
__proto__: Tracker.prototype,
onStart() {
this._log.info("Adding Places observer.");
PlacesUtils.history.addObserver(this, true);
+ this._placesObserver =
+ new PlacesWeakCallbackWrapper(this.handlePlacesEvents.bind(this));
+ PlacesObservers.addListener(["page-visited"], this._placesObserver);
},
onStop() {
this._log.info("Removing Places observer.");
PlacesUtils.history.removeObserver(this);
+ if (this._placesObserver) {
+ PlacesObservers.removeListener(["page-visited"], this._placesObserver);
+ }
},
QueryInterface: ChromeUtils.generateQI([
Ci.nsINavHistoryObserver,
Ci.nsISupportsWeakReference
]),
async onDeleteAffectsGUID(uri, guid, reason, source, increment) {
@@ -513,29 +519,30 @@ HistoryTracker.prototype = {
},
onDeleteURI(uri, guid, reason) {
this.asyncObserver.enqueueCall(() =>
this.onDeleteAffectsGUID(uri, guid, reason, "onDeleteURI", SCORE_INCREMENT_XLARGE)
);
},
- onVisits(aVisits) {
- this.asyncObserver.enqueueCall(() => this._onVisits(aVisits));
+ handlePlacesEvents(aEvents) {
+ this.asyncObserver.enqueueCall(() => this._handlePlacesEvents(aEvents));
},
- async _onVisits(aVisits) {
+ async _handlePlacesEvents(aEvents) {
if (this.ignoreAll) {
this._log.trace("ignoreAll: ignoring visits [" +
- aVisits.map(v => v.guid).join(",") + "]");
+ aEvents.map(v => v.guid).join(",") + "]");
return;
}
- for (let {uri, guid} of aVisits) {
- this._log.trace("onVisits: " + uri.spec);
- if (this.engine.shouldSyncURL(uri.spec) && (await this.addChangedID(guid))) {
+ for (let event of aEvents) {
+ this._log.trace("'page-visited': " + event.url);
+ if (this.engine.shouldSyncURL(event.url) &&
+ await this.addChangedID(event.pageGuid)) {
this.score += SCORE_INCREMENT_SMALL;
}
}
},
onClearHistory() {
this._log.trace("onClearHistory");
// Note that we're going to trigger a sync, but none of the cleared
--- a/toolkit/components/downloads/DownloadIntegration.jsm
+++ b/toolkit/components/downloads/DownloadIntegration.jsm
@@ -1017,17 +1017,16 @@ this.DownloadHistoryObserver.prototype =
// nsINavHistoryObserver
onClearHistory: function DL_onClearHistory() {
this._list.removeFinished();
},
onTitleChanged() {},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
- onVisits() {},
onPageChanged() {},
onDeleteVisits() {},
};
/**
* This view can be added to a DownloadList object to trigger a save operation
* in the given DownloadStore object when a relevant change occurs. You should
* call the "initialize" method in order to register the view and load the
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -33,16 +33,20 @@
#include "mozilla/Unused.h"
#include "nsContentUtils.h" // for nsAutoScriptBlocker
#include "nsJSUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "nsPrintfCString.h"
#include "nsTHashtable.h"
#include "jsapi.h"
#include "mozilla/dom/Element.h"
+#include "mozilla/dom/PlacesObservers.h"
+#include "mozilla/dom/PlacesVisit.h"
+#include "mozilla/dom/ProcessGlobal.h"
+#include "mozilla/dom/ScriptSettings.h"
// Initial size for the cache holding visited status observers.
#define VISIT_OBSERVERS_INITIAL_CACHE_LENGTH 64
// Initial length for the visits removal hash.
#define VISITS_REMOVAL_INITIAL_HASH_LENGTH 64
using namespace mozilla::dom;
@@ -661,31 +665,38 @@ public:
mHistory->AppendToRecentlyVisitedURIs(aURI);
}
mHistory->NotifyVisited(aURI);
if (aPlace.titleChanged) {
aNavHistory->NotifyTitleChange(aURI, aPlace.title, aPlace.guid);
}
+ aNavHistory->UpdateDaysOfHistory(aPlace.visitTime);
+
return NS_OK;
}
void AddPlaceForNotify(const VisitData& aPlace,
nsIURI* aURI,
- nsCOMArray<nsIVisitData>& aPlaces) {
+ Sequence<OwningNonNull<PlacesEvent>>& aEvents) {
if (aPlace.transitionType != nsINavHistoryService::TRANSITION_EMBED) {
- nsCOMPtr<nsIVisitData> notifyPlace = new nsVisitData(
- aURI, aPlace.visitId, aPlace.visitTime,
- aPlace.referrerVisitId, aPlace.transitionType,
- aPlace.guid, aPlace.hidden,
- aPlace.visitCount + 1, // Add current visit.
- static_cast<uint32_t>(aPlace.typed),
- aPlace.title);
- aPlaces.AppendElement(notifyPlace.forget());
+ RefPtr<PlacesVisit> vd = new PlacesVisit();
+ vd->mVisitId = aPlace.visitId;
+ vd->mUrl.Assign(NS_ConvertUTF8toUTF16(aPlace.spec));
+ vd->mVisitTime = aPlace.visitTime / 1000;
+ vd->mReferringVisitId = aPlace.referrerVisitId;
+ vd->mTransitionType = aPlace.transitionType;
+ vd->mPageGuid.Assign(aPlace.guid);
+ vd->mHidden = aPlace.hidden;
+ vd->mVisitCount = aPlace.visitCount + 1; // Add current visit
+ vd->mTypedCount = static_cast<uint32_t>(aPlace.typed);
+ vd->mLastKnownTitle.Assign(aPlace.title);
+ bool success = !!aEvents.AppendElement(vd.forget(), fallible);
+ MOZ_RELEASE_ASSERT(success);
}
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread(), "This should be called on the main thread");
// We are in the main thread, no need to lock.
@@ -698,39 +709,40 @@ public:
if (!navHistory) {
NS_WARNING("Trying to notify visits observers but cannot get the history service!");
return NS_OK;
}
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
- nsCOMArray<nsIVisitData> places;
+ Sequence<OwningNonNull<PlacesEvent>> events;
nsCOMArray<nsIURI> uris;
if (mPlaces.Length() > 0) {
for (uint32_t i = 0; i < mPlaces.Length(); ++i) {
nsCOMPtr<nsIURI> uri;
MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), mPlaces[i].spec));
if (!uri) {
return NS_ERROR_UNEXPECTED;
}
- AddPlaceForNotify(mPlaces[i], uri, places);
+ AddPlaceForNotify(mPlaces[i], uri, events);
uris.AppendElement(uri.forget());
}
} else {
nsCOMPtr<nsIURI> uri;
MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), mPlace.spec));
if (!uri) {
return NS_ERROR_UNEXPECTED;
}
- AddPlaceForNotify(mPlace, uri, places);
+ AddPlaceForNotify(mPlace, uri, events);
uris.AppendElement(uri.forget());
}
- if (places.Length() > 0) {
- navHistory->NotifyOnVisits(places.Elements(), places.Length());
+
+ if (events.Length() > 0) {
+ PlacesObservers::NotifyListeners(events);
}
PRTime now = PR_Now();
if (mPlaces.Length() > 0) {
InfallibleTArray<URIParams> serializableUris(mPlaces.Length());
for (uint32_t i = 0; i < mPlaces.Length(); ++i) {
nsresult rv = NotifyVisit(navHistory, obsService, now, uris[i], mPlaces[i]);
NS_ENSURE_SUCCESS(rv, rv);
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -333,16 +333,17 @@ var PlacesUtils = {
TOPIC_FEEDBACK_UPDATED: "places-autocomplete-feedback-updated",
TOPIC_FAVICONS_EXPIRED: "places-favicons-expired",
TOPIC_VACUUM_STARTING: "places-vacuum-starting",
TOPIC_BOOKMARKS_RESTORE_BEGIN: "bookmarks-restore-begin",
TOPIC_BOOKMARKS_RESTORE_SUCCESS: "bookmarks-restore-success",
TOPIC_BOOKMARKS_RESTORE_FAILED: "bookmarks-restore-failed",
ACTION_SCHEME: "moz-action:",
+ observers: PlacesObservers,
/**
* GUIDs associated with virtual queries that are used for displaying the
* top-level folders in the left pane.
*/
virtualAllBookmarksGuid: "allbms_____v",
virtualHistoryGuid: "history____v",
virtualDownloadsGuid: "downloads__v",
--- a/toolkit/components/places/mozIAsyncLivemarks.idl
+++ b/toolkit/components/places/mozIAsyncLivemarks.idl
@@ -58,16 +58,18 @@ interface mozIAsyncLivemarks : nsISuppor
* @param [optional]aForceUpdate
* If set to true forces a reload even if contents are still valid.
*
* @note The update process is asynchronous, observers registered through
* registerForUpdates will be notified of updated contents.
*/
void reloadLivemarks([optional]in boolean aForceUpdate);
+ void handlePlacesEvents(in jsval aEvents);
+
jsval invalidateCachedLivemarks();
};
[scriptable, uuid(3a3c5e8f-ec4a-4086-ae0a-d16420d30c9f)]
interface mozILivemarkInfo : nsISupports
{
/**
* Id of the bookmarks folder representing this livemark.
--- a/toolkit/components/places/nsLivemarkService.js
+++ b/toolkit/components/places/nsLivemarkService.js
@@ -7,18 +7,22 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyGetter(this, "history", function() {
+ let livemarks = PlacesUtils.livemarks;
// Lazily add an history observer when it's actually needed.
- PlacesUtils.history.addObserver(PlacesUtils.livemarks, true);
+ PlacesUtils.history.addObserver(livemarks, true);
+ let listener = new PlacesWeakCallbackWrapper(
+ livemarks.handlePlacesEvents.bind(livemarks));
+ PlacesObservers.addListener(["page-visited"], listener);
return PlacesUtils.history;
});
// Constants
// Delay between reloads of consecute livemarks.
const RELOAD_DELAY_MS = 500;
// Expire livemarks after this time.
@@ -331,16 +335,31 @@ LivemarkService.prototype = {
return promise;
},
invalidateCachedLivemarks() {
return this._invalidateCachedLivemarks();
},
+ handlePlacesEvents(aEvents) {
+ if (!aEvents) {
+ throw new Components.Exception("Invalid arguments",
+ Cr.NS_ERROR_INVALID_ARG);
+ }
+
+ this._withLivemarksMap(livemarksMap => {
+ for (let event of aEvents) {
+ for (let livemark of livemarksMap.values()) {
+ livemark.updateURIVisitedStatus(event.url, true);
+ }
+ }
+ });
+ },
+
// nsINavBookmarkObserver
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onItemVisited() {},
onItemAdded() {},
onItemChanged(id, property, isAnno, value, lastModified, itemType, parentId,
@@ -407,26 +426,16 @@ LivemarkService.prototype = {
onDeleteURI(aURI) {
this._withLivemarksMap(livemarksMap => {
for (let livemark of livemarksMap.values()) {
livemark.updateURIVisitedStatus(aURI, false);
}
});
},
- onVisits(aVisits) {
- this._withLivemarksMap(livemarksMap => {
- for (let {uri} of aVisits) {
- for (let livemark of livemarksMap.values()) {
- livemark.updateURIVisitedStatus(uri, true);
- }
- }
- });
- },
-
// nsISupports
classID: Components.ID("{dca61eb5-c7cd-4df1-b0fb-d0722baba251}"),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(LivemarkService),
QueryInterface: ChromeUtils.generateQI([
Ci.mozIAsyncLivemarks,
@@ -687,36 +696,36 @@ Livemark.prototype = {
for (let [ container, observer ] of this._resultObservers) {
observer.invalidateContainer(container);
}
},
/**
* Updates the visited status of nodes observing this livemark.
*
- * @param aURI
+ * @param href
* If provided will update nodes having the given uri,
* otherwise any node.
- * @param aVisitedStatus
+ * @param visitedStatus
* Whether the nodes should be set as visited.
*/
- updateURIVisitedStatus(aURI, aVisitedStatus) {
+ updateURIVisitedStatus(href, visitedStatus) {
let wasVisited = false;
for (let child of this.children) {
- if (!aURI || child.uri.equals(aURI)) {
+ if (!href || child.uri.spec == href) {
wasVisited = child.visited;
- child.visited = aVisitedStatus;
+ child.visited = visitedStatus;
}
}
for (let [ container, observer ] of this._resultObservers) {
if (this._nodes.has(container)) {
let nodes = this._nodes.get(container);
for (let node of nodes) {
- if (!aURI || node.uri == aURI.spec) {
+ if (!href || node.uri == href) {
Services.tm.dispatchToMainThread(() => {
observer.nodeHistoryDetailsChanged(node, node.time, wasVisited);
});
}
}
}
}
},
@@ -813,16 +822,17 @@ LivemarkLoadListener.prototype = {
}
let title = entry.title ? entry.title.plainText() : "";
livemarkChildren.push({ uri, title, visited: false });
}
this._livemark.children = livemarkChildren;
} catch (ex) {
+ Cu.reportError(ex);
this.abort(ex);
} finally {
this._processor.listener = null;
this._processor = null;
}
},
onDataAvailable(aRequest, aContext, aInputStream, aSourceOffset, aCount) {
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -12,16 +12,18 @@
#include "nsAppDirectoryServiceDefs.h"
#include "nsNetUtil.h"
#include "nsUnicharUtils.h"
#include "nsPrintfCString.h"
#include "nsQueryObject.h"
#include "mozilla/Preferences.h"
#include "mozilla/storage.h"
+#include "mozilla/dom/PlacesObservers.h"
+#include "mozilla/dom/PlacesVisit.h"
#include "GeckoProfiler.h"
using namespace mozilla;
// These columns sit to the right of the kGetInfoIndex_* columns.
const int32_t nsNavBookmarks::kGetChildrenIndex_Guid = 18;
const int32_t nsNavBookmarks::kGetChildrenIndex_Position = 19;
@@ -207,16 +209,19 @@ nsNavBookmarks::Init()
mCanNotify = true;
// Allows us to notify on title changes. MUST BE LAST so it is impossible
// to fail after this call, or the history service will have a reference to
// us and we won't go away.
nsNavHistory* history = nsNavHistory::GetHistoryService();
NS_ENSURE_STATE(history);
history->AddObserver(this, true);
+ AutoTArray<PlacesEventType, 1> events;
+ events.AppendElement(PlacesEventType::Page_visited);
+ PlacesObservers::AddListener(events, this);
// DO NOT PUT STUFF HERE that can fail. See observer comment above.
return NS_OK;
}
nsresult
nsNavBookmarks::AdjustIndices(int64_t aFolderId,
@@ -2048,40 +2053,38 @@ NS_IMETHODIMP
nsNavBookmarks::OnEndUpdateBatch()
{
NOTIFY_OBSERVERS(mCanNotify, mObservers,
nsINavBookmarkObserver, OnEndUpdateBatch());
return NS_OK;
}
-NS_IMETHODIMP
-nsNavBookmarks::OnVisits(nsIVisitData** aVisits, uint32_t aVisitsCount)
+void
+nsNavBookmarks::HandlePlacesEvent(const PlacesEventSequence& aEvents)
{
- NS_ENSURE_ARG(aVisits);
- NS_ENSURE_ARG(aVisitsCount);
+ for (const auto& event : aEvents) {
+ if (NS_WARN_IF(event->Type() != PlacesEventType::Page_visited)) {
+ continue;
+ }
- for (uint32_t i = 0; i < aVisitsCount; ++i) {
- nsIVisitData* place = aVisits[i];
- nsCOMPtr<nsIURI> uri;
- MOZ_ALWAYS_SUCCEEDS(place->GetUri(getter_AddRefs(uri)));
+ const dom::PlacesVisit* visit = event->AsPlacesVisit();
+ if (NS_WARN_IF(!visit)) {
+ continue;
+ }
- // If the page is bookmarked, notify observers for each associated bookmark.
ItemVisitData visitData;
- nsresult rv = uri->GetSpec(visitData.bookmark.url);
- NS_ENSURE_SUCCESS(rv, rv);
- MOZ_ALWAYS_SUCCEEDS(place->GetVisitId(&visitData.visitId));
- MOZ_ALWAYS_SUCCEEDS(place->GetTime(&visitData.time));
- MOZ_ALWAYS_SUCCEEDS(place->GetTransitionType(&visitData.transitionType));
-
+ visitData.visitId = visit->mVisitId;
+ visitData.bookmark.url = NS_ConvertUTF16toUTF8(visit->mUrl);
+ visitData.time = visit->mVisitTime * 1000;
+ visitData.transitionType = visit->mTransitionType;
RefPtr< AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData> > notifier =
new AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData>(this, &nsNavBookmarks::NotifyItemVisited, visitData);
notifier->Init();
}
- return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::OnDeleteURI(nsIURI* aURI,
const nsACString& aGUID,
uint16_t aReason)
{
--- a/toolkit/components/places/nsNavBookmarks.h
+++ b/toolkit/components/places/nsNavBookmarks.h
@@ -73,16 +73,17 @@ namespace places {
} // namespace places
} // namespace mozilla
class nsNavBookmarks final : public nsINavBookmarksService
, public nsINavHistoryObserver
, public nsIObserver
, public nsSupportsWeakReference
+ , public mozilla::places::INativePlacesEventCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINAVBOOKMARKSSERVICE
NS_DECL_NSINAVHISTORYOBSERVER
NS_DECL_NSIOBSERVER
nsNavBookmarks();
@@ -202,16 +203,26 @@ public:
*
* @param aItemId
* The changed item id.
* @param aData
* Details about the change.
*/
void NotifyItemChanged(const ItemChangeData& aData);
+
+ /**
+ * Part of INativePlacesEventCallback - handles events from the places
+ * observer system.
+ * @param aCx
+ * A JSContext for extracting the values from aEvents.
+ * @param aEvents
+ * An array of weakly typed events detailing what changed.
+ */
+ void HandlePlacesEvent(const PlacesEventSequence& aEvents) override;
static const int32_t kGetChildrenIndex_Guid;
static const int32_t kGetChildrenIndex_Position;
static const int32_t kGetChildrenIndex_Type;
static const int32_t kGetChildrenIndex_PlaceID;
static const int32_t kGetChildrenIndex_SyncStatus;
static mozilla::Atomic<int64_t> sLastInsertedItemId;
static void StoreLastInsertedId(const nsACString& aTable,
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -516,35 +516,25 @@ nsNavHistory::LoadPrefs()
FRECENCY_PREF(mThirdBucketWeight, PREF_FREC_THIRD_BUCKET_WEIGHT);
FRECENCY_PREF(mFourthBucketWeight, PREF_FREC_FOURTH_BUCKET_WEIGHT);
FRECENCY_PREF(mDefaultWeight, PREF_FREC_DEFAULT_BUCKET_WEIGHT);
#undef FRECENCY_PREF
}
void
-nsNavHistory::NotifyOnVisits(nsIVisitData** aVisits, uint32_t aVisitsCount)
+nsNavHistory::UpdateDaysOfHistory(PRTime visitTime)
{
- MOZ_ASSERT(aVisits, "Can't call NotifyOnVisits with a NULL aVisits");
- MOZ_ASSERT(aVisitsCount, "Should have at least 1 visit when notifying");
-
if (mDaysOfHistory == 0) {
mDaysOfHistory = 1;
}
- for (uint32_t i = 0; i < aVisitsCount; ++i) {
- PRTime time;
- MOZ_ALWAYS_SUCCEEDS(aVisits[i]->GetTime(&time));
- if (time > mLastCachedEndOfDay || time < mLastCachedStartOfDay) {
- mDaysOfHistory = -1;
- }
+ if (visitTime > mLastCachedEndOfDay || visitTime < mLastCachedStartOfDay) {
+ mDaysOfHistory = -1;
}
-
- NOTIFY_OBSERVERS(mCanNotify, mObservers, nsINavHistoryObserver,
- OnVisits(aVisits, aVisitsCount));
}
void
nsNavHistory::NotifyTitleChange(nsIURI* aURI,
const nsString& aTitle,
const nsACString& aGUID)
{
MOZ_ASSERT(!aGUID.IsEmpty());
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -430,19 +430,20 @@ public:
}
int32_t GetNumVisitsForFrecency() const
{
return mNumVisitsForFrecency;
}
/**
- * Fires onVisits event to nsINavHistoryService observers
+ * Updates and invalidates the mDaysOfHistory cache. Should be
+ * called whenever a visit is added.
*/
- void NotifyOnVisits(nsIVisitData** aVisits, uint32_t aVisitsCount);
+ void UpdateDaysOfHistory(PRTime visitTime);
/**
* Fires onTitleChanged event to nsINavHistoryService observers
*/
void NotifyTitleChange(nsIURI* aURI,
const nsString& title,
const nsACString& aGUID);
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -14,16 +14,18 @@
#include "mozilla/DebugOnly.h"
#include "nsDebug.h"
#include "nsNetUtil.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
#include "prtime.h"
#include "nsQueryObject.h"
+#include "mozilla/dom/PlacesObservers.h"
+#include "mozilla/dom/PlacesVisit.h"
#include "nsCycleCollectionParticipant.h"
// Thanks, Windows.h :(
#undef CompareString
#define TO_ICONTAINER(_node) \
static_cast<nsINavHistoryContainerResultNode*>(_node)
@@ -3991,28 +3993,34 @@ nsNavHistoryResult::StopObserving()
MOBILE_BOOKMARKS_PREF,
this);
mIsMobilePrefObserver = false;
}
if (mIsHistoryObserver) {
nsNavHistory* history = nsNavHistory::GetHistoryService();
if (history) {
history->RemoveObserver(this);
+ AutoTArray<PlacesEventType, 1> events;
+ events.AppendElement(PlacesEventType::Page_visited);
+ PlacesObservers::RemoveListener(events, this);
mIsHistoryObserver = false;
}
}
}
void
nsNavHistoryResult::AddHistoryObserver(nsNavHistoryQueryResultNode* aNode)
{
if (!mIsHistoryObserver) {
nsNavHistory* history = nsNavHistory::GetHistoryService();
NS_ASSERTION(history, "Can't create history service");
history->AddObserver(this, true);
+ AutoTArray<PlacesEventType, 1> events;
+ events.AppendElement(PlacesEventType::Page_visited);
+ PlacesObservers::AddListener(events, this);
mIsHistoryObserver = true;
}
// Don't add duplicate observers. In some case we don't unregister when
// children are cleared (see ClearChildren) and the next FillChildren call
// will try to add the observer again.
if (mHistoryObservers.IndexOf(aNode) == QueryObserverList::NoIndex) {
mHistoryObservers.AppendElement(aNode);
}
@@ -4586,42 +4594,37 @@ nsNavHistoryResult::OnVisit(nsIURI* aURI
// cause changes to the array.
ENUMERATE_QUERY_OBSERVERS(Refresh(), mHistoryObservers, IsContainersQuery());
}
return NS_OK;
}
-NS_IMETHODIMP
-nsNavHistoryResult::OnVisits(nsIVisitData** aVisits,
- uint32_t aVisitsCount) {
- for (uint32_t i = 0; i < aVisitsCount; ++i) {
- nsIVisitData* place = aVisits[i];
+void
+nsNavHistoryResult::HandlePlacesEvent(const PlacesEventSequence& aEvents) {
+ for (const auto& event : aEvents) {
+ if (NS_WARN_IF(event->Type() != PlacesEventType::Page_visited)) {
+ continue;
+ }
+
+ const dom::PlacesVisit* visit = event->AsPlacesVisit();
+ if (NS_WARN_IF(!visit)) {
+ continue;
+ }
+
nsCOMPtr<nsIURI> uri;
- MOZ_ALWAYS_SUCCEEDS(place->GetUri(getter_AddRefs(uri)));
- int64_t visitId;
- MOZ_ALWAYS_SUCCEEDS(place->GetVisitId(&visitId));
- PRTime time;
- MOZ_ALWAYS_SUCCEEDS(place->GetTime(&time));
- uint32_t transitionType;
- MOZ_ALWAYS_SUCCEEDS(place->GetTransitionType(&transitionType));
- nsCString guid;
- MOZ_ALWAYS_SUCCEEDS(place->GetGuid(guid));
- bool hidden;
- MOZ_ALWAYS_SUCCEEDS(place->GetHidden(&hidden));
- uint32_t visitCount;
- MOZ_ALWAYS_SUCCEEDS(place->GetVisitCount(&visitCount));
- nsString lastKnownTitle;
- MOZ_ALWAYS_SUCCEEDS(place->GetLastKnownTitle(lastKnownTitle));
- nsresult rv = OnVisit(uri, visitId, time, transitionType, guid, hidden,
- visitCount, lastKnownTitle);
- NS_ENSURE_SUCCESS(rv, rv);
+ MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), visit->mUrl));
+ if (!uri) {
+ return;
+ }
+ OnVisit(uri, visit->mVisitId, visit->mVisitTime * 1000,
+ visit->mTransitionType, visit->mPageGuid,
+ visit->mHidden, visit->mVisitCount, visit->mLastKnownTitle);
}
- return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryResult::OnTitleChanged(nsIURI* aURI,
const nsAString& aPageTitle,
const nsACString& aGUID)
{
--- a/toolkit/components/places/nsNavHistoryResult.h
+++ b/toolkit/components/places/nsNavHistoryResult.h
@@ -7,16 +7,17 @@
* The definitions of objects that make up a history query result set. This file
* should only be included by nsNavHistory.h, include that if you want these
* classes.
*/
#ifndef nsNavHistoryResult_h_
#define nsNavHistoryResult_h_
+#include "INativePlacesEventCallback.h"
#include "nsTArray.h"
#include "nsInterfaceHashtable.h"
#include "nsDataHashtable.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/storage.h"
#include "Helpers.h"
class nsNavHistory;
@@ -93,27 +94,26 @@ private:
// object initialization.
#define NS_NAVHISTORYRESULT_IID \
{ 0x455d1d40, 0x1b9b, 0x40e6, { 0xa6, 0x41, 0x8b, 0xb7, 0xe8, 0x82, 0x23, 0x87 } }
class nsNavHistoryResult final : public nsSupportsWeakReference,
public nsINavHistoryResult,
public nsINavBookmarkObserver,
- public nsINavHistoryObserver
+ public nsINavHistoryObserver,
+ public mozilla::places::INativePlacesEventCallback
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULT_IID)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSINAVHISTORYRESULT
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsNavHistoryResult, nsINavHistoryResult)
NS_DECL_BOOKMARK_HISTORY_OBSERVER_EXTERNAL(override)
- NS_IMETHOD OnVisits(nsIVisitData** aVisits,
- uint32_t aVisitsCount) override;
void AddHistoryObserver(nsNavHistoryQueryResultNode* aNode);
void AddBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode, int64_t aFolder);
void AddAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode);
void AddMobilePrefsObserver(nsNavHistoryQueryResultNode* aNode);
void RemoveHistoryObserver(nsNavHistoryQueryResultNode* aNode);
void RemoveBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode, int64_t aFolder);
void RemoveAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode);
@@ -169,16 +169,18 @@ public:
bool mBatchInProgress;
nsMaybeWeakPtrArray<nsINavHistoryResultObserver> mObservers;
bool mSuppressNotifications;
ContainerObserverList mRefreshParticipants;
void requestRefresh(nsNavHistoryContainerResultNode* aContainer);
+ void HandlePlacesEvent(const PlacesEventSequence& aEvents) override;
+
void OnMobilePrefChanged();
static void OnMobilePrefChangedCallback(const char* prefName, void* closure);
protected:
virtual ~nsNavHistoryResult();
};
--- a/toolkit/components/places/nsPlacesExpiration.js
+++ b/toolkit/components/places/nsPlacesExpiration.js
@@ -531,17 +531,16 @@ nsPlacesExpiration.prototype = {
this._newTimer();
},
onClearHistory: function PEX_onClearHistory() {
// History status is clean after a clear history.
this.status = STATUS.CLEAN;
},
- onVisits() {},
onTitleChanged() {},
onDeleteURI() {},
onPageChanged() {},
onDeleteVisits() {},
// nsITimerCallback
notify: function PEX_timerCallback() {
--- a/toolkit/components/thumbnails/PageThumbs.jsm
+++ b/toolkit/components/thumbnails/PageThumbs.jsm
@@ -822,15 +822,14 @@ var PageThumbsHistoryObserver = {
onClearHistory() {
PageThumbsStorage.wipe();
},
onTitleChanged() {},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
- onVisits() {},
onPageChanged() {},
onDeleteVisits() {},
QueryInterface: ChromeUtils.generateQI([Ci.nsINavHistoryObserver,
Ci.nsISupportsWeakReference])
};
--- a/toolkit/modules/NewTabUtils.jsm
+++ b/toolkit/modules/NewTabUtils.jsm
@@ -544,16 +544,19 @@ var PlacesProvider = {
*/
maxNumLinks: HISTORY_RESULTS_LIMIT,
/**
* Must be called before the provider is used.
*/
init: function PlacesProvider_init() {
PlacesUtils.history.addObserver(this, true);
+ this._placesObserver =
+ new PlacesWeakCallbackWrapper(this.handlePlacesEvents.bind(this));
+ PlacesObservers.addListener(["page-visited"], this._placesObserver);
},
/**
* Gets the current set of links delivered by this provider.
* @param aCallback The function that the array of links is passed to.
*/
getLinks: function PlacesProvider_getLinks(aCallback) {
let options = PlacesUtils.history.getNewQueryOptions();
@@ -651,21 +654,21 @@ var PlacesProvider = {
onEndUpdateBatch() {
this._batchProcessingDepth -= 1;
if (this._batchProcessingDepth == 0 && this._batchCalledFrecencyChanged) {
this.onManyFrecenciesChanged();
this._batchCalledFrecencyChanged = false;
}
},
- onVisits(aVisits) {
+ handlePlacesEvents(aEvents) {
if (!this._batchProcessingDepth) {
- for (let visit of aVisits) {
- if (visit.visitCount == 1 && visit.lastKnownTitle) {
- this.onTitleChanged(visit.uri, visit.lastKnownTitle, visit.guid);
+ for (let event of aEvents) {
+ if (event.visitCount == 1 && event.lastKnownTitle) {
+ this.onTitleChanged(event.url, event.lastKnownTitle, event.pageGuid);
}
}
}
},
onDeleteURI: function PlacesProvider_onDeleteURI(aURI, aGUID, aReason) {
// let observers remove sensetive data associated with deleted visit
this._callObservers("onDeleteURI", {
@@ -706,18 +709,21 @@ var PlacesProvider = {
onManyFrecenciesChanged: function PlacesProvider_onManyFrecenciesChanged() {
this._callObservers("onManyLinksChanged");
},
/**
* Called by the history service.
*/
onTitleChanged: function PlacesProvider_onTitleChanged(aURI, aNewTitle, aGUID) {
+ if (aURI instanceof Ci.nsIURI) {
+ aURI = aURI.spec;
+ }
this._callObservers("onLinkChanged", {
- url: aURI.spec,
+ url: aURI,
title: aNewTitle
});
},
_callObservers: function PlacesProvider__callObservers(aMethodName, aArg) {
for (let obs of this._observers) {
if (obs[aMethodName]) {
try {