Merge mozilla-central to mozilla-inbound. CLOSED TREE

This commit is contained in:
Dorel Luca 2018-06-27 14:09:04 +03:00
commit 48178c7559
76 changed files with 687 additions and 1323 deletions

View File

@ -94,17 +94,17 @@ const getHistoryObserver = () => {
onDeleteURI(uri, guid, reason) {
this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
}
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,
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,
};
this.emit("visited", visit);
this.emit("visited", data);
}
}
onBeginUpdateBatch() {}
@ -122,9 +122,6 @@ const getHistoryObserver = () => {
this.emit("visitRemoved", {allHistory: false, urls: [uri.spec]});
}
}();
PlacesUtils.observers.addListener(
["page-visited"],
_observer.handlePlacesEvents.bind(_observer));
PlacesUtils.history.addObserver(_observer);
}
return _observer;

View File

@ -631,6 +631,9 @@ add_task(async function checkUndoVisitsState() {
let observer = {
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits(visits) {
wrongMethodDeferred.reject(new Error("Unexpected call to onVisits " + visits.length));
},
onTitleChanged(uri) {
wrongMethodDeferred.reject(new Error("Unexpected call to onTitleChanged " + uri.spec));
},

View File

@ -60,6 +60,7 @@ class HistoryObserver extends Observer {
onEndUpdateBatch() {}
onVisits() {}
onTitleChanged() {}

View File

@ -816,6 +816,7 @@ var AeroPeek = {
/* nsINavHistoryObserver implementation */
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits() {},
onTitleChanged() {},
onFrecencyChanged() {},
onManyFrecenciesChanged() {},

View File

@ -55,6 +55,7 @@ function test() {
var historyObserver = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {},
onVisits: function() {},
onTitleChanged: function(aURI, aPageTitle) {},
onDeleteURI: function(aURI) {},
onClearHistory: function() {},

View File

@ -15,6 +15,7 @@ add_task(async function() {
var historyObserver = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {},
onVisits: function() {},
onTitleChanged: function(aURI, aPageTitle) {
aURI = aURI.spec;
switch (aURI) {

View File

@ -48,6 +48,7 @@ function test() {
onBeginUpdateBatch: function() { },
onEndUpdateBatch: function() { },
onVisits: function() { },
onTitleChanged: function() { },
onDeleteURI: function() { },
onClearHistory: function() { },

View File

@ -1,52 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/dom/PlacesEvent.h"
#include "mozilla/HoldDropJSObjects.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(PlacesEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PlacesEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PlacesEvent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(PlacesEvent)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PlacesEvent, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PlacesEvent, Release)
already_AddRefed<PlacesEvent>
PlacesEvent::Constructor(const GlobalObject& aGlobal,
PlacesEventType aType,
ErrorResult& rv)
{
RefPtr<PlacesEvent> event = new PlacesEvent(aType);
return event.forget();
}
nsISupports*
PlacesEvent::GetParentObject() const
{
return nullptr;
}
JSObject*
PlacesEvent::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return PlacesEventBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla

View File

@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_PlacesEvent_h
#define mozilla_dom_PlacesEvent_h
#include "mozilla/dom/PlacesEventBinding.h"
#include "mozilla/ErrorResult.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class PlacesEvent : public nsWrapperCache
{
public:
explicit PlacesEvent(PlacesEventType aType)
: mType(aType)
{}
static already_AddRefed<PlacesEvent>
Constructor(const GlobalObject& aGlobal,
PlacesEventType aType,
ErrorResult& aRv);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PlacesEvent)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PlacesEvent)
nsISupports* GetParentObject() const;
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
PlacesEventType Type() const { return mType; }
virtual const PlacesVisit* AsPlacesVisit() const { return nullptr; }
protected:
virtual ~PlacesEvent() = default;
PlacesEventType mType;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PlacesEvent_h

View File

@ -1,350 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "PlacesObservers.h"
#include "PlacesWeakCallbackWrapper.h"
#include "nsIWeakReferenceUtils.h"
#include "mozilla/ClearOnShutdown.h"
namespace mozilla {
namespace dom {
template <class T>
struct Flagged
{
Flagged(uint32_t aFlags, T&& aValue)
: flags(aFlags)
, value(std::forward<T>(aValue))
{}
Flagged(Flagged&& aOther)
: Flagged(std::move(aOther.flags), std::move(aOther.value))
{}
Flagged(const Flagged& aOther) = default;
~Flagged() = default;
uint32_t flags;
T value;
};
template <class T>
using FlaggedArray = nsTArray<Flagged<T>>;
template <class T>
struct ListenerCollection
{
static StaticAutoPtr<FlaggedArray<T>> gListeners;
static StaticAutoPtr<FlaggedArray<T>> gListenersToRemove;
static FlaggedArray<T>* GetListeners() {
MOZ_ASSERT(NS_IsMainThread());
if (!gListeners) {
gListeners = new FlaggedArray<T>();
ClearOnShutdown(&gListeners);
}
return gListeners;
}
static FlaggedArray<T>* GetListenersToRemove() {
MOZ_ASSERT(NS_IsMainThread());
if (!gListenersToRemove) {
gListenersToRemove = new FlaggedArray<T>();
ClearOnShutdown(&gListenersToRemove);
}
return gListenersToRemove;
}
};
template <class T>
StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListeners;
template <class T>
StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListenersToRemove;
typedef ListenerCollection<RefPtr<PlacesEventCallback>> JSListeners;
typedef ListenerCollection<WeakPtr<PlacesWeakCallbackWrapper>> WeakJSListeners;
typedef ListenerCollection<WeakPtr<places::INativePlacesEventCallback>> WeakNativeListeners;
static bool gCallingListeners = false;
uint32_t
GetEventTypeFlag(PlacesEventType aEventType)
{
if (aEventType == PlacesEventType::None) {
return 0;
}
return 1 << ((uint32_t)aEventType - 1);
}
uint32_t
GetFlagsForEventTypes(const nsTArray<PlacesEventType>& aEventTypes)
{
uint32_t flags = 0;
for (PlacesEventType eventType : aEventTypes) {
flags |= GetEventTypeFlag(eventType);
}
return flags;
}
uint32_t
GetFlagsForEvents(const nsTArray<OwningNonNull<PlacesEvent>>& aEvents)
{
uint32_t flags = 0;
for (const PlacesEvent& event : aEvents) {
flags |= GetEventTypeFlag(event.Type());
}
return flags;
}
template <class TWrapped, class TUnwrapped>
void CallListeners(uint32_t aEventFlags,
FlaggedArray<TWrapped>& aListeners,
const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
const std::function<TUnwrapped(TWrapped&)>& aUnwrapListener,
const std::function<void(TUnwrapped&, const Sequence<OwningNonNull<PlacesEvent>>&)>& aCallListener)
{
for (uint32_t i = 0; i < aListeners.Length(); i++) {
Flagged<TWrapped>& l = aListeners[i];
TUnwrapped unwrapped = aUnwrapListener(l.value);
if (!unwrapped) {
aListeners.RemoveElementAt(i);
i--;
continue;
}
if ((l.flags & aEventFlags) == aEventFlags) {
aCallListener(unwrapped, aEvents);
} else if (l.flags & aEventFlags) {
Sequence<OwningNonNull<PlacesEvent>> filtered;
for (const OwningNonNull<PlacesEvent>& event : aEvents) {
if (l.flags & GetEventTypeFlag(event->Type())) {
bool success = !!filtered.AppendElement(event, fallible);
MOZ_RELEASE_ASSERT(success);
}
}
aCallListener(unwrapped, filtered);
}
}
}
void
PlacesObservers::AddListener(GlobalObject& aGlobal,
const nsTArray<PlacesEventType>& aEventTypes,
PlacesEventCallback& aCallback,
ErrorResult& rv)
{
uint32_t flags = GetFlagsForEventTypes(aEventTypes);
FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
JSListeners::GetListeners();
Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
listeners->AppendElement(pair);
}
void
PlacesObservers::AddListener(GlobalObject& aGlobal,
const nsTArray<PlacesEventType>& aEventTypes,
PlacesWeakCallbackWrapper& aCallback,
ErrorResult& rv)
{
uint32_t flags = GetFlagsForEventTypes(aEventTypes);
FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
WeakJSListeners::GetListeners();
WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
MOZ_ASSERT(weakCb.get());
Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags, std::move(weakCb));
listeners->AppendElement(flagged);
}
void
PlacesObservers::AddListener(const nsTArray<PlacesEventType>& aEventTypes,
places::INativePlacesEventCallback* aCallback)
{
uint32_t flags = GetFlagsForEventTypes(aEventTypes);
FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
WeakNativeListeners::GetListeners();
Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
listeners->AppendElement(pair);
}
void
PlacesObservers::RemoveListener(GlobalObject& aGlobal,
const nsTArray<PlacesEventType>& aEventTypes,
PlacesEventCallback& aCallback,
ErrorResult& rv)
{
uint32_t flags = GetFlagsForEventTypes(aEventTypes);
if (gCallingListeners) {
FlaggedArray<RefPtr<PlacesEventCallback>>* listeners =
JSListeners::GetListenersToRemove();
Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback);
listeners->AppendElement(pair);
} else {
RemoveListener(flags, aCallback);
}
}
void
PlacesObservers::RemoveListener(GlobalObject& aGlobal,
const nsTArray<PlacesEventType>& aEventTypes,
PlacesWeakCallbackWrapper& aCallback,
ErrorResult& rv)
{
uint32_t flags = GetFlagsForEventTypes(aEventTypes);
if (gCallingListeners) {
FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners =
WeakJSListeners::GetListenersToRemove();
WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback);
MOZ_ASSERT(weakCb.get());
Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags, std::move(weakCb));
listeners->AppendElement(flagged);
} else {
RemoveListener(flags, aCallback);
}
}
void
PlacesObservers::RemoveListener(const nsTArray<PlacesEventType>& aEventTypes,
places::INativePlacesEventCallback* aCallback)
{
uint32_t flags = GetFlagsForEventTypes(aEventTypes);
if (gCallingListeners) {
FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners =
WeakNativeListeners::GetListenersToRemove();
Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback);
listeners->AppendElement(pair);
} else {
RemoveListener(flags, aCallback);
}
}
void
PlacesObservers::RemoveListener(uint32_t aFlags,
PlacesEventCallback& aCallback)
{
FlaggedArray<RefPtr<PlacesEventCallback>>& listeners =
*JSListeners::GetListeners();
for (uint32_t i = 0; i < listeners.Length(); i++) {
Flagged<RefPtr<PlacesEventCallback>>& l = listeners[i];
if (!(*l.value == aCallback)) {
continue;
}
if (l.flags == aFlags) {
listeners.RemoveElementAt(i);
i--;
} else {
l.flags &= ~aFlags;
}
}
}
void
PlacesObservers::RemoveListener(uint32_t aFlags,
PlacesWeakCallbackWrapper& aCallback)
{
FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>& listeners =
*WeakJSListeners::GetListeners();
for (uint32_t i = 0; i < listeners.Length(); i++) {
Flagged<WeakPtr<PlacesWeakCallbackWrapper>>& l = listeners[i];
RefPtr<PlacesWeakCallbackWrapper> unwrapped = l.value.get();
if (unwrapped != &aCallback) {
continue;
}
if (l.flags == aFlags) {
listeners.RemoveElementAt(i);
i--;
} else {
l.flags &= ~aFlags;
}
}
}
void
PlacesObservers::RemoveListener(uint32_t aFlags,
places::INativePlacesEventCallback* aCallback)
{
FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>& listeners =
*WeakNativeListeners::GetListeners();
for (uint32_t i = 0; i < listeners.Length(); i++) {
Flagged<WeakPtr<places::INativePlacesEventCallback>>& l = listeners[i];
RefPtr<places::INativePlacesEventCallback> unwrapped = l.value.get();
if (unwrapped != aCallback) {
continue;
}
if (l.flags == aFlags) {
listeners.RemoveElementAt(i);
i--;
} else {
l.flags &= ~aFlags;
}
}
}
void
PlacesObservers::NotifyListeners(GlobalObject& aGlobal,
const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
ErrorResult& rv)
{
NotifyListeners(aEvents);
}
void
PlacesObservers::NotifyListeners(const Sequence<OwningNonNull<PlacesEvent>>& aEvents)
{
MOZ_RELEASE_ASSERT(!gCallingListeners);
gCallingListeners = true;
uint32_t flags = GetFlagsForEvents(aEvents);
CallListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>>(
flags, *JSListeners::GetListeners(), aEvents,
[](auto& cb) { return cb; },
[&](auto& cb, const auto& events) {
cb->Call(aEvents);
});
CallListeners<WeakPtr<places::INativePlacesEventCallback>,
RefPtr<places::INativePlacesEventCallback>>(
flags, *WeakNativeListeners::GetListeners(), aEvents,
[](auto& cb) { return cb.get(); },
[&](auto& cb, const Sequence<OwningNonNull<PlacesEvent>>& events) {
cb->HandlePlacesEvent(events);
});
CallListeners<WeakPtr<PlacesWeakCallbackWrapper>,
RefPtr<PlacesWeakCallbackWrapper>>(
flags, *WeakJSListeners::GetListeners(), aEvents,
[](auto& cb) { return cb.get(); },
[&](auto& cb, const auto& events) {
cb->mCallback->Call(aEvents);
});
auto& listenersToRemove = *JSListeners::GetListenersToRemove();
if (listenersToRemove.Length() > 0) {
for (auto& listener : listenersToRemove) {
RemoveListener(listener.flags, *listener.value);
}
}
auto& weakListenersToRemove = *WeakJSListeners::GetListenersToRemove();
if (weakListenersToRemove.Length() > 0) {
for (auto& listener : weakListenersToRemove) {
RemoveListener(listener.flags, *listener.value.get());
}
}
auto& nativeListenersToRemove = *WeakNativeListeners::GetListenersToRemove();
if (nativeListenersToRemove.Length() > 0) {
for (auto& listener : nativeListenersToRemove) {
RemoveListener(listener.flags, listener.value.get());
}
}
gCallingListeners = false;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,62 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_PlacesObservers__
#define mozilla_dom_PlacesObservers__
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/PlacesObserversBinding.h"
#include "mozilla/dom/PlacesEvent.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Pair.h"
#include "mozilla/places/INativePlacesEventCallback.h"
#include "nsIWeakReferenceUtils.h"
namespace mozilla {
namespace dom {
class PlacesObservers
{
public:
static void AddListener(GlobalObject& aGlobal,
const nsTArray<PlacesEventType>& aEventTypes,
PlacesEventCallback& aCallback,
ErrorResult& rv);
static void AddListener(GlobalObject& aGlobal,
const nsTArray<PlacesEventType>& aEventTypes,
PlacesWeakCallbackWrapper& aCallback,
ErrorResult& rv);
static void AddListener(const nsTArray<PlacesEventType>& aEventTypes,
places::INativePlacesEventCallback* aCallback);
static void RemoveListener(GlobalObject& aGlobal,
const nsTArray<PlacesEventType>& aEventTypes,
PlacesEventCallback& aCallback,
ErrorResult& rv);
static void RemoveListener(GlobalObject& aGlobal,
const nsTArray<PlacesEventType>& aEventTypes,
PlacesWeakCallbackWrapper& aCallback,
ErrorResult& rv);
static void RemoveListener(const nsTArray<PlacesEventType>& aEventTypes,
places::INativePlacesEventCallback* aCallback);
static void NotifyListeners(GlobalObject& aGlobal,
const Sequence<OwningNonNull<PlacesEvent>>& aEvents,
ErrorResult& rv);
static void NotifyListeners(const Sequence<OwningNonNull<PlacesEvent>>& aEvents);
private:
static void RemoveListener(uint32_t aFlags, PlacesEventCallback& aCallback);
static void RemoveListener(uint32_t aFlags, PlacesWeakCallbackWrapper& aCallback);
static void RemoveListener(uint32_t aFlags, places::INativePlacesEventCallback* aCallback);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PlacesObservers__

View File

@ -1,67 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_PlacesVisit_h
#define mozilla_dom_PlacesVisit_h
#include "mozilla/dom/PlacesEvent.h"
namespace mozilla {
namespace dom {
class PlacesVisit final : public PlacesEvent
{
public:
explicit PlacesVisit() : PlacesEvent(PlacesEventType::Page_visited) {}
static already_AddRefed<PlacesVisit>
Constructor(const GlobalObject& aGlobal,
ErrorResult& aRv) {
RefPtr<PlacesVisit> event = new PlacesVisit();
return event.forget();
}
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
{
return PlacesVisitBinding::Wrap(aCx, this, aGivenProto);
}
const PlacesVisit* AsPlacesVisit() const override { return this; }
void GetUrl(nsString& aUrl) { aUrl = mUrl; }
uint64_t VisitId() { return mVisitId; }
uint64_t VisitTime() { return mVisitTime; }
uint64_t ReferringVisitId() { return mReferringVisitId; }
uint64_t TransitionType() { return mTransitionType; }
void GetPageGuid(nsTString<char>& aPageGuid) { aPageGuid = mPageGuid; }
bool Hidden() { return mHidden; }
uint32_t VisitCount() { return mVisitCount; }
uint32_t TypedCount() { return mTypedCount; }
void GetLastKnownTitle(nsString& aLastKnownTitle) { aLastKnownTitle = mLastKnownTitle; }
// It's convenient for these to be directly available in C++, so just expose
// them. These are generally passed around with const qualifiers anyway, so
// it shouldn't be a problem.
nsString mUrl;
uint64_t mVisitId;
uint64_t mVisitTime;
uint64_t mReferringVisitId;
uint32_t mTransitionType;
nsCString mPageGuid;
bool mHidden;
uint32_t mVisitCount;
uint32_t mTypedCount;
nsString mLastKnownTitle;
private:
~PlacesVisit() = default;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PlacesVisit_h

View File

@ -1,55 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/dom/PlacesWeakCallbackWrapper.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/dom/ProcessGlobal.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PlacesWeakCallbackWrapper, mParent, mCallback)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PlacesWeakCallbackWrapper, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PlacesWeakCallbackWrapper, Release)
PlacesWeakCallbackWrapper::PlacesWeakCallbackWrapper(nsISupports* aParent,
PlacesEventCallback& aCallback)
: mParent(do_GetWeakReference(aParent))
, mCallback(&aCallback)
{
}
already_AddRefed<PlacesWeakCallbackWrapper>
PlacesWeakCallbackWrapper::Constructor(const GlobalObject& aGlobal,
PlacesEventCallback& aCallback,
ErrorResult& rv)
{
nsCOMPtr<nsISupports> parent = aGlobal.GetAsSupports();
RefPtr<PlacesWeakCallbackWrapper> wrapper =
new PlacesWeakCallbackWrapper(parent, aCallback);
return wrapper.forget();
}
PlacesWeakCallbackWrapper::~PlacesWeakCallbackWrapper()
{
}
nsISupports*
PlacesWeakCallbackWrapper::GetParentObject() const
{
nsCOMPtr<nsISupports> parent = do_QueryReferent(mParent);
return parent;
}
JSObject*
PlacesWeakCallbackWrapper::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return PlacesWeakCallbackWrapperBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla

View File

@ -1,49 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_PlacesWeakCallbackWrapper_h
#define mozilla_dom_PlacesWeakCallbackWrapper_h
#include "mozilla/dom/PlacesObserversBinding.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Pair.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class PlacesWeakCallbackWrapper final : public nsWrapperCache
, public SupportsWeakPtr<PlacesWeakCallbackWrapper>
{
public:
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PlacesWeakCallbackWrapper)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PlacesWeakCallbackWrapper)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PlacesWeakCallbackWrapper)
explicit PlacesWeakCallbackWrapper(nsISupports* aParent,
PlacesEventCallback& aCallback);
static already_AddRefed<PlacesWeakCallbackWrapper>
Constructor(const GlobalObject& aGlobal,
PlacesEventCallback& aCallback,
ErrorResult& rv);
nsISupports* GetParentObject() const;
JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
protected:
friend class PlacesObservers;
~PlacesWeakCallbackWrapper();
nsWeakPtr mParent;
RefPtr<PlacesEventCallback> mCallback;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PlacesWeakCallbackWrapper_h

View File

@ -9,12 +9,6 @@
#include "nsISelectionListener.h"
#include "mozilla/Attributes.h"
#include "nsCycleCollectionParticipant.h"
#include "nsTArray.h"
#include "nsCOMPtr.h"
class nsRange;
class nsINode;
namespace mozilla {
namespace dom {

View File

@ -6,6 +6,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/ShadowRootBinding.h"
#include "mozilla/dom/DocumentFragment.h"
#include "ChildIterator.h"
#include "nsContentUtils.h"

View File

@ -10,12 +10,9 @@
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/DocumentOrShadowRoot.h"
#include "mozilla/dom/NameSpaceConstants.h"
#include "mozilla/dom/ShadowRootBinding.h"
#include "mozilla/ServoBindings.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIdentifierMapEntry.h"
#include "nsStubMutationObserver.h"
#include "nsTHashtable.h"
class nsAtom;

View File

@ -7,7 +7,6 @@
#ifndef mozilla_dom_StyleSheetList_h
#define mozilla_dom_StyleSheetList_h
#include "nsContentUtils.h"
#include "mozilla/dom/DocumentOrShadowRoot.h"
#include "nsStubMutationObserver.h"
#include "nsWrapperCache.h"

View File

@ -8,7 +8,6 @@
#include "nsIAppStartup.h"
#include "nsToolkitCompsCID.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
namespace mozilla {

View File

@ -201,10 +201,6 @@ EXPORTS.mozilla.dom += [
'NodeInfoInlines.h',
'NodeIterator.h',
'ParentProcessMessageManager.h',
'PlacesEvent.h',
'PlacesObservers.h',
'PlacesVisit.h',
'PlacesWeakCallbackWrapper.h',
'Pose.h',
'ProcessGlobal.h',
'ProcessMessageManager.h',
@ -394,13 +390,6 @@ if CONFIG['FUZZING']:
'FuzzingFunctions.cpp',
]
if CONFIG['MOZ_PLACES']:
UNIFIED_SOURCES += [
'PlacesEvent.cpp',
'PlacesObservers.cpp',
'PlacesWeakCallbackWrapper.cpp',
]
# these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
SOURCES += [
# Several conflicts with other bindings.

View File

@ -28,7 +28,6 @@
#include "nsIWebNavigation.h"
#include "nsIDocShell.h"
#include "nsIContent.h"
#include "nsIContentInlines.h"
#include "nsIImageLoadingContent.h"
#include "nsITextControlElement.h"
#include "nsUnicharUtils.h"

View File

@ -24,8 +24,6 @@
using namespace mozilla;
using namespace mozilla::dom;
using mozilla::dom::DocGroup;
using mozilla::dom::HTMLSlotElement;
AutoTArray<RefPtr<nsDOMMutationObserver>, 4>*
nsDOMMutationObserver::sScheduledMutationObservers = nullptr;

View File

@ -15,7 +15,6 @@
using mozilla::MallocSizeOf;
using mozilla::Maybe;
using mozilla::DOMEventTargetHelper;
using mozilla::dom::BlobURLProtocolHandler;
using mozilla::dom::ClientInfo;
using mozilla::dom::ServiceWorker;
using mozilla::dom::ServiceWorkerDescriptor;

View File

@ -90,7 +90,6 @@
#include "nsUnicharUtils.h"
#include "nsXBLBinding.h"
#include "nsXBLPrototypeBinding.h"
#include "nsWindowSizes.h"
#include "mozilla/Preferences.h"
#include "xpcpublic.h"
#include "HTMLLegendElement.h"

View File

@ -5,7 +5,6 @@
#include <nsIMutableArray.h>
#include <nsArrayUtils.h>
#include <nsPerformanceMetrics.h>
#include "nsComponentManagerUtils.h"
/* ------------------------------------------------------
*

View File

@ -25,7 +25,6 @@
#endif
using namespace mozilla;
using namespace mozilla::dom;
StaticRefPtr<nsWindowMemoryReporter> sWindowReporter;

View File

@ -12,7 +12,6 @@
#include "nsXHTMLContentSerializer.h"
#include "mozilla/dom/Element.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsElementTable.h"
@ -31,6 +30,7 @@
#include "nsStubMutationObserver.h"
#include "nsAttrName.h"
#include "nsComputedDOMStyle.h"
#include "mozilla/dom/Element.h"
using namespace mozilla;

View File

@ -1,69 +0,0 @@
enum PlacesEventType {
"none",
/**
* data: PlacesVisit. Fired whenever a page is visited.
*/
"page-visited",
};
[ChromeOnly, Exposed=(Window,System)]
interface PlacesEvent {
readonly attribute PlacesEventType type;
};
[ChromeOnly, Exposed=(Window,System)]
interface PlacesVisit : PlacesEvent {
/**
* URL of the visit.
*/
readonly attribute DOMString url;
/**
* Id of the visit.
*/
readonly attribute unsigned long long visitId;
/**
* Time of the visit, in milliseconds since epoch.
*/
readonly attribute unsigned long long visitTime;
/**
* The id of the visit the user came from, defaults to 0 for no referrer.
*/
readonly attribute unsigned long long referringVisitId;
/**
* One of nsINavHistory.TRANSITION_*
*/
readonly attribute unsigned long transitionType;
/**
* The unique id associated with the page.
*/
readonly attribute ByteString pageGuid;
/**
* Whether the visited page is marked as hidden.
*/
readonly attribute boolean hidden;
/**
* Number of visits (including this one) for this URL.
*/
readonly attribute unsigned long visitCount;
/**
* Whether the URL has been typed or not.
* TODO (Bug 1271801): This will become a count, rather than a boolean.
* For future compatibility, always compare it with "> 0".
*/
readonly attribute unsigned long typedCount;
/**
* The last known title of the page. Might not be from the current visit,
* and might be null if it is not known.
*/
readonly attribute DOMString? lastKnownTitle;
};

View File

@ -1,32 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; 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/.
*/
callback PlacesEventCallback = void (sequence<PlacesEvent> events);
[ChromeOnly, Exposed=(Window,System),
Constructor(PlacesEventCallback callback)]
interface PlacesWeakCallbackWrapper {
};
// Global singleton which should handle all events for places.
[ChromeOnly, Exposed=(Window,System)]
namespace PlacesObservers {
[Throws]
void addListener(sequence<PlacesEventType> eventTypes,
PlacesEventCallback listener);
[Throws]
void addListener(sequence<PlacesEventType> eventTypes,
PlacesWeakCallbackWrapper listener);
[Throws]
void removeListener(sequence<PlacesEventType> eventTypes,
PlacesEventCallback listener);
[Throws]
void removeListener(sequence<PlacesEventType> eventTypes,
PlacesWeakCallbackWrapper listener);
[Throws]
void notifyListeners(sequence<PlacesEvent> events);
};

View File

@ -46,9 +46,3 @@ WEBIDL_FILES = [
'WebExtensionContentScript.webidl',
'WebExtensionPolicy.webidl',
]
if CONFIG['MOZ_PLACES']:
WEBIDL_FILES += [
'PlacesEvent.webidl',
'PlacesObservers.webidl',
]

View File

@ -59,7 +59,7 @@ public:
void
GetPropertyChangeClosure(DeclarationBlockMutationClosure* aClosure,
mozilla::MutationClosureData* aClosureData) final
MutationClosureData* aClosureData) final
{
aClosure->function = MutationClosureFunction;
aClosure->data = aClosureData;
@ -71,7 +71,7 @@ protected:
~nsDOMCSSAttributeDeclaration();
virtual nsresult SetCSSDeclaration(mozilla::DeclarationBlock* aDecl,
mozilla::MutationClosureData* aClosureData) override;
MutationClosureData* aClosureData) override;
virtual nsIDocument* DocToUpdate() override;
RefPtr<Element> mElement;

View File

@ -483,17 +483,11 @@ HistoryTracker.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([
@ -524,20 +518,19 @@ HistoryTracker.prototype = {
);
},
handlePlacesEvents(aEvents) {
this.asyncObserver.enqueueCall(() => this._handlePlacesEvents(aEvents));
onVisits(aVisits) {
this.asyncObserver.enqueueCall(() => this._onVisits(aVisits));
},
async _handlePlacesEvents(aEvents) {
async _onVisits(aVisits) {
if (this.ignoreAll) {
this._log.trace("ignoreAll: ignoring visits [" +
aEvents.map(v => v.guid).join(",") + "]");
aVisits.map(v => v.guid).join(",") + "]");
return;
}
for (let event of aEvents) {
this._log.trace("'page-visited': " + event.url);
if (this.engine.shouldSyncURL(event.url) &&
await this.addChangedID(event.pageGuid)) {
for (let {uri, guid} of aVisits) {
this._log.trace("onVisits: " + uri.spec);
if (this.engine.shouldSyncURL(uri.spec) && (await this.addChangedID(guid))) {
this.score += SCORE_INCREMENT_SMALL;
}
}

View File

@ -549,18 +549,15 @@ async function serverForFoo(engine, callback) {
async function promiseVisit(expectedType, expectedURI) {
return new Promise(resolve => {
function done(type, uri) {
if (uri == expectedURI.spec && type == expectedType) {
if (uri.equals(expectedURI) && type == expectedType) {
PlacesUtils.history.removeObserver(observer);
PlacesObservers.removeListener(["page-visited"],
observer.handlePlacesEvents);
resolve();
}
}
let observer = {
handlePlacesEvents(events) {
Assert.equal(events.length, 1);
Assert.equal(events[0].type, "page-visited");
done("added", events[0].url);
onVisits(visits) {
Assert.equal(visits.length, 1);
done("added", visits[0].uri);
},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
@ -568,15 +565,13 @@ async function promiseVisit(expectedType, expectedURI) {
onFrecencyChanged() {},
onManyFrecenciesChanged() {},
onDeleteURI(uri) {
done("removed", uri.spec);
done("removed", uri);
},
onClearHistory() {},
onPageChanged() {},
onDeleteVisits() {},
};
PlacesUtils.history.addObserver(observer, false);
PlacesObservers.addListener(["page-visited"],
observer.handlePlacesEvents);
});
}

View File

@ -13,11 +13,26 @@ const TIMESTAMP3 = (Date.now() - 123894) * 1000;
function promiseOnVisitObserved() {
return new Promise(res => {
let listener = new PlacesWeakCallbackWrapper((events) => {
PlacesObservers.removeListener(["page-visited"], listener);
res();
});
PlacesObservers.addListener(["page-visited"], listener);
PlacesUtils.history.addObserver({
onBeginUpdateBatch: function onBeginUpdateBatch() {},
onEndUpdateBatch: function onEndUpdateBatch() {},
onPageChanged: function onPageChanged() {},
onTitleChanged: function onTitleChanged() {
},
onVisits: function onVisits() {
PlacesUtils.history.removeObserver(this);
res();
},
onDeleteVisits: function onDeleteVisits() {},
onPageExpired: function onPageExpired() {},
onDeleteURI: function onDeleteURI() {},
onClearHistory: function onClearHistory() {},
QueryInterface: ChromeUtils.generateQI([
Ci.nsINavHistoryObserver,
Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS,
Ci.nsISupportsWeakReference
])
}, true);
});
}

View File

@ -1022,6 +1022,7 @@ this.DownloadHistoryObserver.prototype = {
onTitleChanged() {},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits() {},
onPageChanged() {},
onDeleteVisits() {},
};

View File

@ -2400,7 +2400,7 @@ add_task(async function test_history() {
// The history notifications should be received before the download completes.
let [time, transitionType] = await promiseVisit;
Assert.equal(time, download.startTime.getTime());
Assert.equal(time, download.startTime.getTime() * 1000);
Assert.equal(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD);
// Restart and complete the download after clearing history.
@ -2434,7 +2434,7 @@ add_task(async function test_history_tryToKeepPartialData() {
// The time set by nsIHelperAppService may be different than the start time in
// the download object, thus we only check that it is a meaningful time. Note
// that we subtract one second from the earliest time to account for rounding.
Assert.ok(time >= beforeStartTimeMs - 1000);
Assert.ok(time >= beforeStartTimeMs * 1000 - 1000000);
// Complete the download before finishing the test.
continueResponses();

View File

@ -146,16 +146,32 @@ function promiseTimeout(aTime) {
*/
function promiseWaitForVisit(aUrl) {
return new Promise(resolve => {
function listener(aEvents) {
Assert.equal(aEvents.length, 1);
let event = aEvents[0];
Assert.equal(event.type, "page-visited");
if (event.url == aUrl) {
PlacesObservers.removeListener(["page-visited"], listener);
resolve([event.visitTime, event.transitionType]);
}
}
PlacesObservers.addListener(["page-visited"], listener);
let uri = NetUtil.newURI(aUrl);
PlacesUtils.history.addObserver({
QueryInterface: ChromeUtils.generateQI([Ci.nsINavHistoryObserver]),
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits(aVisits) {
Assert.equal(aVisits.length, 1);
let {
uri: visitUri,
time,
transitionType,
} = aVisits[0];
if (visitUri.equals(uri)) {
PlacesUtils.history.removeObserver(this);
resolve([time, transitionType]);
}
},
onTitleChanged() {},
onDeleteURI() {},
onClearHistory() {},
onPageChanged() {},
onDeleteVisits() {},
});
});
}

View File

@ -38,10 +38,6 @@
#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
@ -173,6 +169,121 @@ struct VisitData {
bool useFrecencyRedirectBonus;
};
////////////////////////////////////////////////////////////////////////////////
//// nsVisitData
class nsVisitData : public nsIVisitData
{
public:
explicit nsVisitData(nsIURI* aURI,
int64_t aVisitId,
PRTime aTime,
int64_t aReferrerVisitId,
int32_t aTransitionType,
const nsACString& aGuid,
bool aHidden,
uint32_t aVisitCount,
uint32_t aTyped,
const nsAString& aLastKnownTitle)
: mURI(aURI)
, mVisitId(aVisitId)
, mTime(aTime)
, mReferrerVisitId(aReferrerVisitId)
, mTransitionType(aTransitionType)
, mGuid(aGuid)
, mHidden(aHidden)
, mVisitCount(aVisitCount)
, mTyped(aTyped)
, mLastKnownTitle(aLastKnownTitle)
{
MOZ_ASSERT(NS_IsMainThread(),
"nsVisitData should only be constructed on the main thread.");
}
NS_DECL_ISUPPORTS
NS_IMETHOD GetUri(nsIURI** aUri) override
{
NS_ENSURE_ARG_POINTER(aUri);
*aUri = mURI;
NS_IF_ADDREF(*aUri);
return NS_OK;
}
NS_IMETHOD GetVisitId(int64_t* aVisitId) override
{
*aVisitId = mVisitId;
return NS_OK;
}
NS_IMETHOD GetTime(PRTime* aTime) override
{
*aTime = mTime;
return NS_OK;
}
NS_IMETHOD GetReferrerId(int64_t* aReferrerVisitId) override
{
*aReferrerVisitId = mReferrerVisitId;
return NS_OK;
}
NS_IMETHOD GetTransitionType(uint32_t* aTransitionType) override
{
*aTransitionType = mTransitionType;
return NS_OK;
}
NS_IMETHOD GetGuid(nsACString& aGuid) override
{
aGuid.Assign(mGuid);
return NS_OK;
}
NS_IMETHOD GetHidden(bool* aHidden) override
{
*aHidden = mHidden;
return NS_OK;
}
NS_IMETHOD GetVisitCount(uint32_t* aVisitCount) override
{
*aVisitCount = mVisitCount;
return NS_OK;
}
NS_IMETHOD GetTyped(uint32_t* aTyped) override
{
*aTyped = mTyped;
return NS_OK;
}
NS_IMETHOD GetLastKnownTitle(nsAString& aLastKnownTitle) override
{
aLastKnownTitle.Assign(mLastKnownTitle);
return NS_OK;
}
private:
virtual ~nsVisitData() {
MOZ_ASSERT(NS_IsMainThread(),
"nsVisitData should only be destructed on the main thread.");
};
nsCOMPtr<nsIURI> mURI;
int64_t mVisitId;
PRTime mTime;
int64_t mReferrerVisitId;
uint32_t mTransitionType;
nsCString mGuid;
bool mHidden;
uint32_t mVisitCount;
uint32_t mTyped;
nsString mLastKnownTitle;
};
NS_IMPL_ISUPPORTS(nsVisitData, nsIVisitData)
////////////////////////////////////////////////////////////////////////////////
//// RemoveVisitsFilter
@ -670,28 +781,21 @@ public:
aNavHistory->NotifyTitleChange(aURI, aPlace.title, aPlace.guid);
}
aNavHistory->UpdateDaysOfHistory(aPlace.visitTime);
return NS_OK;
}
void AddPlaceForNotify(const VisitData& aPlace,
nsIURI* aURI,
Sequence<OwningNonNull<PlacesEvent>>& aEvents) {
nsCOMArray<nsIVisitData>& aPlaces) {
if (aPlace.transitionType != nsINavHistoryService::TRANSITION_EMBED) {
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);
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());
}
}
@ -714,7 +818,7 @@ public:
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
Sequence<OwningNonNull<PlacesEvent>> events;
nsCOMArray<nsIVisitData> places;
nsCOMArray<nsIURI> uris;
if (mPlaces.Length() > 0) {
for (uint32_t i = 0; i < mPlaces.Length(); ++i) {
@ -723,7 +827,7 @@ public:
if (!uri) {
return NS_ERROR_UNEXPECTED;
}
AddPlaceForNotify(mPlaces[i], uri, events);
AddPlaceForNotify(mPlaces[i], uri, places);
uris.AppendElement(uri.forget());
}
} else {
@ -732,12 +836,11 @@ public:
if (!uri) {
return NS_ERROR_UNEXPECTED;
}
AddPlaceForNotify(mPlace, uri, events);
AddPlaceForNotify(mPlace, uri, places);
uris.AppendElement(uri.forget());
}
if (events.Length() > 0) {
PlacesObservers::NotifyListeners(events);
if (places.Length() > 0) {
navHistory->NotifyOnVisits(places.Elements(), places.Length());
}
PRTime now = PR_Now();

View File

@ -1,34 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef mozilla_image_INativePlacesEventCallback_h
#define mozilla_image_INativePlacesEventCallback_h
#include "mozilla/dom/PlacesObserversBinding.h"
#include "mozilla/WeakPtr.h"
#include "nsISupports.h"
#include "nsTArray.h"
namespace mozilla {
namespace places {
class INativePlacesEventCallback : public SupportsWeakPtr<INativePlacesEventCallback>
{
public:
typedef dom::Sequence<OwningNonNull<dom::PlacesEvent>> PlacesEventSequence;
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(INativePlacesEventCallback)
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
virtual void HandlePlacesEvent(const PlacesEventSequence& aEvents) = 0;
protected:
virtual ~INativePlacesEventCallback() { }
};
} // namespace places
} // namespace mozilla
#endif // mozilla_image_INativePlacesEventCallback_h

View File

@ -338,7 +338,6 @@ var PlacesUtils = {
TOPIC_BOOKMARKS_RESTORE_FAILED: "bookmarks-restore-failed",
ACTION_SCHEME: "moz-action:",
observers: PlacesObservers,
/**
* GUIDs associated with virtual queries that are used for displaying the

View File

@ -29,7 +29,6 @@ if CONFIG['MOZ_PLACES']:
EXPORTS.mozilla.places = [
'Database.h',
'History.h',
'INativePlacesEventCallback.h',
'Shutdown.h',
]

View File

@ -63,8 +63,6 @@ interface mozIAsyncLivemarks : nsISupports
*/
void reloadLivemarks([optional]in boolean aForceUpdate);
void handlePlacesEvents(in jsval aEvents);
jsval invalidateCachedLivemarks();
};

View File

@ -29,6 +29,67 @@ interface nsINavHistoryQueryOptions;
interface nsINavHistoryResult;
interface nsINavHistoryBatchCallback;
/**
* This interface exists specifically for passing visit information
* in bulk to onVisits below.
*/
[scriptable, uuid(9d8df1ff-142f-4ca7-9f45-3c62a508c7e2)]
interface nsIVisitData : nsISupports
{
/**
* URI of the visit that was just created.
*/
readonly attribute nsIURI uri;
/**
* Id of the visit that was just created.
*/
readonly attribute long long visitId;
/**
* Time of the visit.
*/
readonly attribute PRTime time;
/**
* The id of the visit the user came from, defaults to 0 for no referrer.
*/
readonly attribute long long referrerId;
/**
* One of nsINavHistory.TRANSITION_*
*/
readonly attribute unsigned long transitionType;
/**
* The unique id associated with the page.
*/
readonly attribute ACString guid;
/**
* Whether the visited page is marked as hidden.
*/
readonly attribute boolean hidden;
/**
* Number of visits (included this one) for this URI.
*/
readonly attribute unsigned long visitCount;
/**
* Whether the URI has been typed or not.
* TODO (Bug 1271801): This will become a count, rather than a boolean.
* For future compatibility, always compare it with "> 0".
*/
readonly attribute unsigned long typed;
/**
* The last known title of the page. Might not be from the current visit,
* and might be null if it is not known.
*/
readonly attribute AString lastKnownTitle;
};
[scriptable, uuid(91d104bb-17ef-404b-9f9a-d9ed8de6824c)]
interface nsINavHistoryResultNode : nsISupports
{
@ -581,6 +642,18 @@ interface nsINavHistoryObserver : nsISupports
*/
void onEndUpdateBatch();
/**
* Called everytime a URI is visited, or once for a batch of visits if visits were
* added in bulk.
*
* @note TRANSITION_EMBED visits (corresponding to images in a page, for
* example) are not displayed in history results. Most observers can
* ignore TRANSITION_EMBED visit notifications (which will comprise the
* majority of visit notifications) to save work.
*/
void onVisits([array, size_is(aVisitsCount)] in nsIVisitData aVisits,
in unsigned long aVisitsCount);
/**
* Called whenever either the "real" title or the custom title of the page
* changed. BOTH TITLES ARE ALWAYS INCLUDED in this notification, even though

View File

@ -12,12 +12,8 @@ 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(livemarks, true);
let listener = new PlacesWeakCallbackWrapper(
livemarks.handlePlacesEvents.bind(livemarks));
PlacesObservers.addListener(["page-visited"], listener);
PlacesUtils.history.addObserver(PlacesUtils.livemarks, true);
return PlacesUtils.history;
});
@ -340,21 +336,6 @@ LivemarkService.prototype = {
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() {},
@ -431,6 +412,16 @@ LivemarkService.prototype = {
});
},
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}"),
@ -701,18 +692,18 @@ Livemark.prototype = {
/**
* Updates the visited status of nodes observing this livemark.
*
* @param href
* @param aURI
* If provided will update nodes having the given uri,
* otherwise any node.
* @param visitedStatus
* @param aVisitedStatus
* Whether the nodes should be set as visited.
*/
updateURIVisitedStatus(href, visitedStatus) {
updateURIVisitedStatus(aURI, aVisitedStatus) {
let wasVisited = false;
for (let child of this.children) {
if (!href || child.uri.spec == href) {
if (!aURI || child.uri.equals(aURI)) {
wasVisited = child.visited;
child.visited = visitedStatus;
child.visited = aVisitedStatus;
}
}
@ -720,7 +711,7 @@ Livemark.prototype = {
if (this._nodes.has(container)) {
let nodes = this._nodes.get(container);
for (let node of nodes) {
if (!href || node.uri == href) {
if (!aURI || node.uri == aURI.spec) {
Services.tm.dispatchToMainThread(() => {
observer.nodeHistoryDetailsChanged(node, node.time, wasVisited);
});
@ -827,7 +818,6 @@ LivemarkLoadListener.prototype = {
this._livemark.children = livemarkChildren;
} catch (ex) {
Cu.reportError(ex);
this.abort(ex);
} finally {
this._processor.listener = null;

View File

@ -17,8 +17,6 @@
#include "nsQueryObject.h"
#include "mozilla/Preferences.h"
#include "mozilla/storage.h"
#include "mozilla/dom/PlacesObservers.h"
#include "mozilla/dom/PlacesVisit.h"
#include "GeckoProfiler.h"
@ -214,9 +212,6 @@ nsNavBookmarks::Init()
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.
@ -2058,28 +2053,30 @@ nsNavBookmarks::OnEndUpdateBatch()
}
void
nsNavBookmarks::HandlePlacesEvent(const PlacesEventSequence& aEvents)
NS_IMETHODIMP
nsNavBookmarks::OnVisits(nsIVisitData** aVisits, uint32_t aVisitsCount)
{
for (const auto& event : aEvents) {
if (NS_WARN_IF(event->Type() != PlacesEventType::Page_visited)) {
continue;
}
NS_ENSURE_ARG(aVisits);
NS_ENSURE_ARG(aVisitsCount);
const dom::PlacesVisit* visit = event->AsPlacesVisit();
if (NS_WARN_IF(!visit)) {
continue;
}
for (uint32_t i = 0; i < aVisitsCount; ++i) {
nsIVisitData* place = aVisits[i];
nsCOMPtr<nsIURI> uri;
MOZ_ALWAYS_SUCCEEDS(place->GetUri(getter_AddRefs(uri)));
// If the page is bookmarked, notify observers for each associated bookmark.
ItemVisitData visitData;
visitData.visitId = visit->mVisitId;
visitData.bookmark.url = NS_ConvertUTF16toUTF8(visit->mUrl);
visitData.time = visit->mVisitTime * 1000;
visitData.transitionType = visit->mTransitionType;
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));
RefPtr< AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData> > notifier =
new AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData>(this, &nsNavBookmarks::NotifyItemVisited, visitData);
notifier->Init();
}
return NS_OK;
}

View File

@ -78,7 +78,6 @@ class nsNavBookmarks final : public nsINavBookmarksService
, public nsINavHistoryObserver
, public nsIObserver
, public nsSupportsWeakReference
, public mozilla::places::INativePlacesEventCallback
{
public:
NS_DECL_ISUPPORTS
@ -208,16 +207,6 @@ public:
*/
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;

View File

@ -521,15 +521,25 @@ nsNavHistory::LoadPrefs()
}
void
nsNavHistory::UpdateDaysOfHistory(PRTime visitTime)
nsNavHistory::NotifyOnVisits(nsIVisitData** aVisits, uint32_t aVisitsCount)
{
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;
}
if (visitTime > mLastCachedEndOfDay || visitTime < mLastCachedStartOfDay) {
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;
}
}
NOTIFY_OBSERVERS(mCanNotify, mObservers, nsINavHistoryObserver,
OnVisits(aVisits, aVisitsCount));
}
void

View File

@ -435,10 +435,9 @@ public:
}
/**
* Updates and invalidates the mDaysOfHistory cache. Should be
* called whenever a visit is added.
* Fires onVisits event to nsINavHistoryService observers
*/
void UpdateDaysOfHistory(PRTime visitTime);
void NotifyOnVisits(nsIVisitData** aVisits, uint32_t aVisitsCount);
/**
* Fires onTitleChanged event to nsINavHistoryService observers

View File

@ -19,8 +19,6 @@
#include "nsUnicharUtils.h"
#include "prtime.h"
#include "nsQueryObject.h"
#include "mozilla/dom/PlacesObservers.h"
#include "mozilla/dom/PlacesVisit.h"
#include "nsCycleCollectionParticipant.h"
@ -3998,9 +3996,6 @@ nsNavHistoryResult::StopObserving()
nsNavHistory* history = nsNavHistory::GetHistoryService();
if (history) {
history->RemoveObserver(this);
AutoTArray<PlacesEventType, 1> events;
events.AppendElement(PlacesEventType::Page_visited);
PlacesObservers::RemoveListener(events, this);
mIsHistoryObserver = false;
}
}
@ -4013,9 +4008,6 @@ nsNavHistoryResult::AddHistoryObserver(nsNavHistoryQueryResultNode* aNode)
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
@ -4599,27 +4591,32 @@ nsNavHistoryResult::OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,
}
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;
}
NS_IMETHODIMP
nsNavHistoryResult::OnVisits(nsIVisitData** aVisits,
uint32_t aVisitsCount) {
for (uint32_t i = 0; i < aVisitsCount; ++i) {
nsIVisitData* place = aVisits[i];
nsCOMPtr<nsIURI> uri;
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);
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);
}
return NS_OK;
}

View File

@ -12,7 +12,6 @@
#ifndef nsNavHistoryResult_h_
#define nsNavHistoryResult_h_
#include "INativePlacesEventCallback.h"
#include "nsTArray.h"
#include "nsInterfaceHashtable.h"
#include "nsDataHashtable.h"
@ -99,8 +98,7 @@ private:
class nsNavHistoryResult final : public nsSupportsWeakReference,
public nsINavHistoryResult,
public nsINavBookmarkObserver,
public nsINavHistoryObserver,
public mozilla::places::INativePlacesEventCallback
public nsINavHistoryObserver
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULT_IID)
@ -109,6 +107,8 @@ public:
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);
@ -174,8 +174,6 @@ public:
ContainerObserverList mRefreshParticipants;
void requestRefresh(nsNavHistoryContainerResultNode* aContainer);
void HandlePlacesEvent(const PlacesEventSequence& aEvents) override;
void OnMobilePrefChanged();
static void OnMobilePrefChangedCallback(const char* prefName, void* closure);

View File

@ -536,6 +536,7 @@ nsPlacesExpiration.prototype = {
this.status = STATUS.CLEAN;
},
onVisits() {},
onTitleChanged() {},
onDeleteURI() {},
onPageChanged() {},

View File

@ -318,18 +318,6 @@ var PlacesTestUtils = Object.freeze({
},
waitForNotification(notification, conditionFn = () => true, type = "bookmarks") {
if (type == "places") {
return new Promise(resolve => {
function listener(events) {
if (conditionFn(events)) {
PlacesObservers.removeListener([notification], listener);
resolve();
}
}
PlacesObservers.addListener([notification], listener);
});
}
let iface = type == "bookmarks" ? Ci.nsINavBookmarkObserver
: Ci.nsINavHistoryObserver;
return new Promise(resolve => {

View File

@ -15,33 +15,43 @@ add_task(async function() {
];
// Create and add history observer.
let count = 0;
let expectedURI = null;
function onVisitsListener(aEvents) {
for (let event of aEvents) {
info("Received onVisits: " + event.url);
if (event.url == expectedURI) {
count++;
let historyObserver = {
count: 0,
expectedURI: null,
onVisits(aVisits) {
for (let {uri} of aVisits) {
info("Received onVisits: " + uri.spec);
if (uri.equals(this.expectedURI)) {
this.count++;
}
}
}
}
},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onTitleChanged() {},
onDeleteURI() {},
onClearHistory() {},
onPageChanged() {},
onDeleteVisits() {},
QueryInterface: ChromeUtils.generateQI([Ci.nsINavHistoryObserver])
};
async function promiseLoadedThreeTimes(uri) {
count = 0;
expectedURI = uri;
historyObserver.count = 0;
historyObserver.expectedURI = Services.io.newURI(uri);
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
PlacesObservers.addListener(["page-visited"], onVisitsListener);
PlacesUtils.history.addObserver(historyObserver);
gBrowser.loadURI(uri);
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
PlacesObservers.removeListener(["page-visited"], onVisitsListener);
PlacesUtils.history.removeObserver(historyObserver);
BrowserTestUtils.removeTab(tab);
}
for (let uri of URIS) {
await promiseLoadedThreeTimes(uri);
is(count, 1,
"'page-visited' has been received right number of times for " + uri);
is(historyObserver.count, 1,
"onVisit has been received right number of times for " + uri);
}
});

View File

@ -23,6 +23,7 @@ add_task(async function() {
onBeginUpdateBatch() { },
onEndUpdateBatch() { },
onVisits() { },
onDeleteURI() { },
onClearHistory() { },
onPageChanged() { },

View File

@ -11,18 +11,19 @@ add_task(async function() {
const FINAL_URI = NetUtil.newURI(BASE_URL + "final.html");
let promiseVisits = new Promise(resolve => {
let observer = {
PlacesUtils.history.addObserver({
__proto__: NavHistoryObserver.prototype,
_notified: [],
onVisit(uri, id, time, referrerId, transition) {
info("Received onVisit: " + uri);
info("Received onVisit: " + uri.spec);
this._notified.push(uri);
if (uri != FINAL_URI.spec) {
if (!uri.equals(FINAL_URI)) {
return;
}
is(this._notified.length, 4);
PlacesObservers.removeListener(["page-visited"], this.handleEvents);
PlacesUtils.history.removeObserver(this);
(async function() {
// Get all pages visited from the original typed one
@ -44,21 +45,18 @@ add_task(async function() {
resolve();
})();
},
handleEvents(events) {
is(events.length, 1, "Right number of visits notified");
is(events[0].type, "page-visited");
onVisits(visits) {
is(visits.length, 1, "Right number of visits notified");
let {
url,
uri,
visitId,
visitTime,
referringVisitId,
time,
referrerId,
transitionType,
} = events[0];
this.onVisit(url, visitId, visitTime, referringVisitId, transitionType);
} = visits[0];
this.onVisit(uri, visitId, time, referrerId, transitionType);
}
};
observer.handleEvents = observer.handleEvents.bind(observer);
PlacesObservers.addListener(["page-visited"], observer.handleEvents);
});
});
PlacesUtils.history.markPageAsTyped(TEST_URI);

View File

@ -32,15 +32,15 @@ async function check_uri(uri, frecency, hidden) {
async function waitVisitedNotifications() {
let redirectNotified = false;
await PlacesTestUtils.waitForNotification("page-visited", visits => {
await PlacesTestUtils.waitForNotification("onVisits", visits => {
is(visits.length, 1, "Was notified for the right number of visits.");
let {url} = visits[0];
info("Received 'page-visited': " + url);
if (url == REDIRECT_URI.spec) {
let {uri} = visits[0];
info("Received onVisits: " + uri.spec);
if (uri.equals(REDIRECT_URI)) {
redirectNotified = true;
}
return url == TARGET_URI.spec;
}, "places");
return uri.equals(TARGET_URI);
}, "history");
return redirectNotified;
}

View File

@ -10,24 +10,42 @@ add_task(async function() {
// Create and add history observer.
let visitedPromise = new Promise(resolve => {
function listener(aEvents) {
is(aEvents.length, 1, "Right number of visits notified");
is(aEvents[0].type, "page-visited");
let uri = NetUtil.newURI(aEvents[0].url);
PlacesObservers.removeListener(["page-visited"], listener);
info("Received 'page-visited': " + uri.spec);
fieldForUrl(uri, "frecency", function(aFrecency) {
is(aFrecency, 0, "Frecency should be 0");
fieldForUrl(uri, "hidden", function(aHidden) {
is(aHidden, 0, "Page should not be hidden");
fieldForUrl(uri, "typed", function(aTyped) {
is(aTyped, 0, "page should not be marked as typed");
resolve();
let historyObserver = {
onVisit(aURI, aVisitID, aTime, aReferringID, aTransitionType) {
PlacesUtils.history.removeObserver(historyObserver);
info("Received onVisit: " + aURI.spec);
fieldForUrl(aURI, "frecency", function(aFrecency) {
is(aFrecency, 0, "Frecency should be 0");
fieldForUrl(aURI, "hidden", function(aHidden) {
is(aHidden, 0, "Page should not be hidden");
fieldForUrl(aURI, "typed", function(aTyped) {
is(aTyped, 0, "page should not be marked as typed");
resolve();
});
});
});
});
}
PlacesObservers.addListener(["page-visited"], listener);
},
onVisits(aVisits) {
is(aVisits.length, 1, "Right number of visits notified");
let {
uri,
visitId,
time,
referrerId,
transitionType,
} = aVisits[0];
this.onVisit(uri, visitId, time, referrerId, transitionType);
},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onTitleChanged() {},
onDeleteURI() {},
onClearHistory() {},
onPageChanged() {},
onDeleteVisits() {},
QueryInterface: ChromeUtils.generateQI([Ci.nsINavHistoryObserver])
};
PlacesUtils.history.addObserver(historyObserver);
});
let newTabPromise = BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);

View File

@ -3,20 +3,19 @@ const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/conte
add_task(async function checkTitleNotificationForNavigation() {
const EXPECTED_URL = Services.io.newURI(TEST_PATH + "empty_page.html");
let promiseTitleChanged = new Promise(resolve => {
function onVisits(aEvents) {
Assert.equal(aEvents.length, 1, "Right number of visits notified");
Assert.equal(aEvents[0].type, "page-visited");
let {
url,
lastKnownTitle,
} = aEvents[0];
info("'page-visited': " + url);
if (url == EXPECTED_URL.spec) {
Assert.equal(lastKnownTitle, null, "Should not have a title");
}
PlacesObservers.removeListener(["page-visited"], onVisits);
}
let obs = {
onVisits(aVisits) {
Assert.equal(aVisits.length, 1, "Right number of visits notified");
let {
uri,
lastKnownTitle,
} = aVisits[0];
info("onVisits: " + uri.spec);
if (uri.equals(EXPECTED_URL)) {
Assert.equal(lastKnownTitle, null, "Should not have a title");
}
},
onTitleChanged(aURI, aTitle, aGuid) {
if (aURI.equals(EXPECTED_URL)) {
is(aTitle, "I am an empty page", "Should have correct title in titlechanged notification");
@ -26,7 +25,6 @@ add_task(async function checkTitleNotificationForNavigation() {
},
};
PlacesUtils.history.addObserver(obs);
PlacesObservers.addListener(["page-visited"], onVisits);
});
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, EXPECTED_URL.spec);
await promiseTitleChanged;

View File

@ -39,15 +39,15 @@ add_task(async function redirect_check_new_typed_visit() {
redirectTargetFrecency += TYPED_VISIT_BONUS;
let redirectNotified = false;
let visitedPromise = PlacesTestUtils.waitForNotification("page-visited", visits => {
let visitedPromise = PlacesTestUtils.waitForNotification("onVisits", visits => {
is(visits.length, 1, "Was notified for the right number of visits.");
let {url} = visits[0];
info("Received 'page-visited': " + url);
if (url == REDIRECT_URI.spec) {
let {uri} = visits[0];
info("Received onVisits for: " + uri.spec);
if (uri.equals(REDIRECT_URI)) {
redirectNotified = true;
}
return url == TARGET_URI.spec;
}, "places");
return uri.equals(TARGET_URI);
}, "history");
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, REDIRECT_URI.spec);
info("Waiting for onVisits");
@ -68,15 +68,15 @@ add_task(async function redirect_check_second_typed_visit() {
redirectTargetFrecency += TYPED_VISIT_BONUS;
let redirectNotified = false;
let visitedPromise = PlacesTestUtils.waitForNotification("page-visited", visits => {
let visitedPromise = PlacesTestUtils.waitForNotification("onVisits", visits => {
is(visits.length, 1, "Was notified for the right number of visits.");
let {url} = visits[0];
info("Received 'page-visited': " + url);
if (url == REDIRECT_URI.spec) {
let {uri} = visits[0];
info("Received onVisits: " + uri.spec);
if (uri.equals(REDIRECT_URI)) {
redirectNotified = true;
}
return url == TARGET_URI.spec;
}, "places");
return uri.equals(TARGET_URI);
}, "history");
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, REDIRECT_URI.spec);
info("Waiting for onVisits");
@ -95,15 +95,15 @@ add_task(async function redirect_check_subsequent_link_visit() {
redirectTargetFrecency += LINK_VISIT_BONUS;
let redirectNotified = false;
let visitedPromise = PlacesTestUtils.waitForNotification("page-visited", visits => {
let visitedPromise = PlacesTestUtils.waitForNotification("onVisits", visits => {
is(visits.length, 1, "Was notified for the right number of visits.");
let {url} = visits[0];
info("Received 'page-visited': " + url);
if (url == REDIRECT_URI.spec) {
let {uri} = visits[0];
info("Received onVisits: " + uri.spec);
if (uri.equals(REDIRECT_URI)) {
redirectNotified = true;
}
return url == TARGET_URI.spec;
}, "places");
return uri.equals(TARGET_URI);
}, "history");
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, REDIRECT_URI.spec);
info("Waiting for onVisits");

View File

@ -25,6 +25,7 @@ add_task(async function() {
data: [],
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits() {},
onTitleChanged(aURI, aPageTitle, aGUID) {
this.data.push({ uri: aURI, title: aPageTitle, guid: aGUID });

View File

@ -20,14 +20,16 @@ add_task(async function test() {
PlacesUtils.history.markPageAsTyped(NetUtil.newURI(TEST_URL));
let promiseVisit = new Promise(resolve => {
function onVisits(events) {
PlacesObservers.removeListener(["page-visited"], onVisits);
is(events.length, 1, "Right number of visits");
is(events[0].type, "page-visited");
is(events[0].url, TEST_URL, "Check visited url");
resolve();
}
PlacesObservers.addListener(["page-visited"], onVisits);
let historyObserver = {
__proto__: NavHistoryObserver.prototype,
onVisits(visits) {
PlacesUtils.history.removeObserver(historyObserver);
is(visits.length, 1, "Right number of visits");
is(visits[0].uri.spec, TEST_URL, "Check visited url");
resolve();
}
};
PlacesUtils.history.addObserver(historyObserver);
});
gBrowser.selectedBrowser.loadURI(TEST_URL);
await promiseVisit;

View File

@ -77,6 +77,7 @@ function NavHistoryObserver() {}
NavHistoryObserver.prototype = {
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits() {},
onTitleChanged() {},
onDeleteURI() {},
onClearHistory() {},

View File

@ -77,6 +77,7 @@ add_task(async function test_notifications_onDeleteURI() {
onBeginUpdateBatch: function PEX_onBeginUpdateBatch() {},
onEndUpdateBatch: function PEX_onEndUpdateBatch() {},
onClearHistory() {},
onVisits() {},
onTitleChanged() {},
onDeleteURI(aURI, aGUID, aReason) {
currentTest.receivedNotifications++;

View File

@ -102,6 +102,7 @@ add_task(async function test_notifications_onDeleteVisits() {
onBeginUpdateBatch: function PEX_onBeginUpdateBatch() {},
onEndUpdateBatch: function PEX_onEndUpdateBatch() {},
onClearHistory() {},
onVisits() {},
onTitleChanged() {},
onDeleteURI(aURI, aGUID, aReason) {
// Check this uri was not bookmarked.

View File

@ -734,6 +734,7 @@ function NavHistoryObserver() {}
NavHistoryObserver.prototype = {
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits() {},
onTitleChanged() {},
onDeleteURI() {},
onClearHistory() {},

View File

@ -80,47 +80,48 @@ TitleChangedObserver.prototype = {
/**
* Listens for a visit notification, and calls aCallback when it gets it.
*
* @param aURI
* The URI of the page we expect a notification for.
* @param aCallback
* The method to call when we have gotten the proper notification about
* being visited.
*/
class VisitObserver {
constructor(aURI,
aGUID,
aCallback) {
this.uri = aURI;
this.guid = aGUID;
this.callback = aCallback;
this.handlePlacesEvent = this.handlePlacesEvent.bind(this);
PlacesObservers.addListener(["page-visited"], this.handlePlacesEvent);
}
handlePlacesEvent(aEvents) {
info("'page-visited'!!!");
Assert.equal(aEvents.length, 1, "Right number of visits notified");
Assert.equal(aEvents[0].type, "page-visited");
function VisitObserver(aURI,
aGUID,
aCallback) {
this.uri = aURI;
this.guid = aGUID;
this.callback = aCallback;
}
VisitObserver.prototype = {
__proto__: NavHistoryObserver.prototype,
onVisits(aVisits) {
info("onVisits()!!!");
Assert.equal(aVisits.length, 1, "Right number of visits notified");
let {
url,
uri,
visitId,
visitTime,
referringVisitId,
time,
referrerId,
transitionType,
pageGuid,
guid,
hidden,
visitCount,
typedCount,
typed,
lastKnownTitle,
} = aEvents[0];
} = aVisits[0];
let args = [
visitId, visitTime, referringVisitId, transitionType, pageGuid,
hidden, visitCount, typedCount, lastKnownTitle,
visitId, time, referrerId, transitionType, guid,
hidden, visitCount, typed, lastKnownTitle,
];
info("'page-visited' (" + url + args.join(", ") + ")");
if (this.uri.spec != url || this.guid != pageGuid) {
info("onVisit(" + uri.spec + args.join(", ") + ")");
if (!this.uri.equals(uri) || this.guid != guid) {
return;
}
this.callback(visitTime * 1000, transitionType, lastKnownTitle);
PlacesObservers.removeListener(["page-visited"], this.handlePlacesEvent);
}
}
this.callback(time, transitionType, lastKnownTitle);
},
};
/**
* Tests that a title was set properly in the database.
@ -983,15 +984,15 @@ add_task(async function test_title_change_notifies() {
});
let visitPromise = new Promise(resolve => {
function onVisits(events) {
Assert.equal(events.length, 1, "Should only get notified for one visit.");
Assert.equal(events[0].type, "page-visited");
let {url} = events[0];
Assert.equal(url, place.uri.spec, "Should get notified for visiting the new URI.");
PlacesObservers.removeListener(["page-visited"], onVisits);
resolve();
}
PlacesObservers.addListener(["page-visited"], onVisits);
PlacesUtils.history.addObserver({
onVisits(visits) {
Assert.equal(visits.length, 1, "Should only get notified for one visit.");
let {uri} = visits[0];
Assert.equal(uri.spec, place.uri.spec, "Should get notified for visiting the new URI.");
PlacesUtils.history.removeObserver(this);
resolve();
}
});
});
asyncHistory.updatePlaces(place);
await visitPromise;
@ -1027,15 +1028,17 @@ add_task(async function test_visit_notifies() {
resolve();
}
};
new VisitObserver(place.uri, place.guid,
let visitObserver = new VisitObserver(place.uri, place.guid,
function(aVisitDate,
aTransitionType) {
let visit = place.visits[0];
Assert.equal(visit.visitDate, aVisitDate);
Assert.equal(visit.transitionType, aTransitionType);
PlacesUtils.history.removeObserver(visitObserver);
finisher();
});
PlacesUtils.history.addObserver(visitObserver);
let observer = function(aSubject, aTopic, aData) {
info("observe(" + aSubject + ", " + aTopic + ", " + aData + ")");
Assert.ok(aSubject instanceof Ci.nsIURI);
@ -1266,14 +1269,16 @@ add_task(async function test_title_on_initial_visit() {
guid: "mnopqrstuvwx",
};
let visitPromise = new Promise(resolve => {
new VisitObserver(place.uri, place.guid,
function(aVisitDate,
aTransitionType,
aLastKnownTitle) {
let visitObserver = new VisitObserver(place.uri, place.guid,
function(aVisitDate,
aTransitionType,
aLastKnownTitle) {
Assert.equal(place.title, aLastKnownTitle);
PlacesUtils.history.removeObserver(visitObserver);
resolve();
});
PlacesUtils.history.addObserver(visitObserver);
});
await promiseUpdatePlaces(place);
await visitPromise;
@ -1288,14 +1293,16 @@ add_task(async function test_title_on_initial_visit() {
guid: "fghijklmnopq",
};
visitPromise = new Promise(resolve => {
new VisitObserver(place.uri, place.guid,
function(aVisitDate,
aTransitionType,
aLastKnownTitle) {
let visitObserver = new VisitObserver(place.uri, place.guid,
function(aVisitDate,
aTransitionType,
aLastKnownTitle) {
Assert.equal(place.title, aLastKnownTitle);
PlacesUtils.history.removeObserver(visitObserver);
resolve();
});
PlacesUtils.history.addObserver(visitObserver);
});
await promiseUpdatePlaces(place);
await visitPromise;
@ -1309,14 +1316,16 @@ add_task(async function test_title_on_initial_visit() {
guid: "fghijklmnopq",
};
visitPromise = new Promise(resolve => {
new VisitObserver(place.uri, place.guid,
function(aVisitDate,
aTransitionType,
aLastKnownTitle) {
let visitObserver = new VisitObserver(place.uri, place.guid,
function(aVisitDate,
aTransitionType,
aLastKnownTitle) {
Assert.equal(null, aLastKnownTitle);
PlacesUtils.history.removeObserver(visitObserver);
resolve();
});
PlacesUtils.history.addObserver(visitObserver);
});
await promiseUpdatePlaces(place);
await visitPromise;

View File

@ -44,6 +44,9 @@ add_task(async function test_remove_single() {
observer = {
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits(aVisits) {
reject(new Error("Unexpected call to onVisits " + aVisits.length));
},
onTitleChanged(aUri) {
reject(new Error("Unexpected call to onTitleChanged " + aUri.spec));
},

View File

@ -292,6 +292,9 @@ function getObserverPromise(bookmarkedUri) {
observer = {
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits() {
reject(new Error("Unexpected call to onVisits"));
},
onTitleChanged(aUri) {
reject(new Error("Unexpected call to onTitleChanged"));
},

View File

@ -134,6 +134,9 @@ add_task(async function test_removeVisitsByFilter() {
deferred: PromiseUtils.defer(),
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits(aVisits) {
this.deferred.reject(new Error("Unexpected call to onVisits " + aVisits.length));
},
onTitleChanged(uri) {
this.deferred.reject(new Error("Unexpected call to onTitleChanged " + uri.spec));
},

View File

@ -11,11 +11,11 @@ var visit_count = 0;
async function task_add_visit(aURI, aVisitType) {
// Wait for a visits notification and get the visitId.
let visitId;
let visitsPromise = PlacesTestUtils.waitForNotification("page-visited", visits => {
let visitsPromise = PlacesTestUtils.waitForNotification("onVisits", visits => {
visitId = visits[0].visitId;
let {url} = visits[0];
return url == aURI.spec;
}, "places");
let {uri} = visits[0];
return uri.equals(aURI);
}, "history");
// Add visits.
await PlacesTestUtils.addVisits([{

View File

@ -20,28 +20,29 @@ const PRIVATE_URI = NetUtil.newURI("http://www.example.net/");
* Callback function to be called with the same arguments of onVisit.
*/
function waitForOnVisit(aCallback) {
function listener(aEvents) {
Assert.equal(aEvents.length, 1, "Right number of visits notified");
Assert.equal(aEvents[0].type, "page-visited");
let {
url,
visitId,
visitTime,
referringVisitId,
transitionType,
pageGuid,
hidden,
visitCount,
typedCount,
lastKnownTitle,
} = aEvents[0];
PlacesObservers.removeListener(["page-visited"], listener);
let uriArg = NetUtil.newURI(url);
aCallback(uriArg, visitId, visitTime, 0, referringVisitId,
transitionType, pageGuid, hidden, visitCount,
typedCount, lastKnownTitle);
}
PlacesObservers.addListener(["page-visited"], listener);
let historyObserver = {
__proto__: NavHistoryObserver.prototype,
onVisits: function HO_onVisit(aVisits) {
Assert.equal(aVisits.length, 1, "Right number of visits notified");
let {
uri,
visitId,
time,
referrerId,
transitionType,
guid,
hidden,
visitCount,
typed,
lastKnownTitle,
} = aVisits[0];
PlacesUtils.history.removeObserver(this);
aCallback(uri, visitId, time, 0, referrerId,
transitionType, guid, hidden, visitCount,
typed, lastKnownTitle);
}
};
PlacesUtils.history.addObserver(historyObserver);
}
/**
@ -156,11 +157,11 @@ add_task(async function test_dh_addBookmarkRemoveDownload() {
add_task(async function test_dh_addDownload_referrer() {
// Wait for visits notification and get the visit id.
let visitId;
let referrerPromise = PlacesTestUtils.waitForNotification("page-visited", visits => {
let referrerPromise = PlacesTestUtils.waitForNotification("onVisits", visits => {
visitId = visits[0].visitId;
let {url} = visits[0];
return url == REFERRER_URI.spec;
}, "places");
let {uri} = visits[0];
return uri.equals(REFERRER_URI);
}, "history");
await PlacesTestUtils.addVisits([{
uri: REFERRER_URI,
@ -174,17 +175,16 @@ add_task(async function test_dh_addDownload_referrer() {
// Wait for visits notification and get the referrer Id.
let referrerId;
let downloadPromise = PlacesTestUtils.waitForNotification("page-visited", visits => {
referrerId = visits[0].referringVisitId;
let {url} = visits[0];
return url == DOWNLOAD_URI.spec;
}, "places");
let downloadPromise = PlacesTestUtils.waitForNotification("onVisits", visits => {
referrerId = visits[0].referrerId;
let {uri} = visits[0];
return uri.equals(DOWNLOAD_URI);
}, "history");
gDownloadHistory.addDownload(DOWNLOAD_URI, REFERRER_URI, Date.now() * 1000);
await downloadPromise;
// Verify results for download uri.
// ensure that we receive the 'page-visited' notification before we call addDownload.
Assert.ok(!!PlacesTestUtils.isPageInDB(DOWNLOAD_URI));
Assert.equal(visitId, referrerId);
@ -260,6 +260,7 @@ add_test(function test_dh_details() {
let historyObserver = {
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits() {},
onTitleChanged: function HO_onTitleChanged(aURI, aPageTitle) {
if (aURI.equals(SOURCE_URI)) {
titleSet = true;

View File

@ -10,6 +10,7 @@ function NavHistoryObserver() {
NavHistoryObserver.prototype = {
onBeginUpdateBatch() { },
onEndUpdateBatch() { },
onVisits() { },
onTitleChanged() { },
onDeleteURI() { },
onClearHistory() { },
@ -35,23 +36,6 @@ function onNotify(callback) {
});
}
/**
* Registers a one-time places observer for 'page-visited',
* which resolves a promise on being called.
*/
function promiseVisitAdded(callback) {
return new Promise(resolve => {
function listener(events) {
PlacesObservers.removeListener(["page-visited"], listener);
Assert.equal(events.length, 1, "Right number of visits notified");
Assert.equal(events[0].type, "page-visited");
callback(events[0]);
resolve();
}
PlacesObservers.addListener(["page-visited"], listener);
});
}
/**
* Asynchronous task that adds a visit to the history database.
*/
@ -66,18 +50,19 @@ async function task_add_visit(uri, timestamp, transition) {
return [uri, timestamp];
}
add_task(async function test_visitAdded() {
let promiseNotify = promiseVisitAdded(function(visit) {
add_task(async function test_onVisits() {
let promiseNotify = onNotify(function onVisits(aVisits) {
Assert.equal(aVisits.length, 1, "Right number of visits notified");
let visit = aVisits[0];
Assert.ok(visit.uri.equals(testuri));
Assert.ok(visit.visitId > 0);
Assert.equal(visit.url, testuri.spec);
Assert.equal(visit.visitTime, testtime / 1000);
Assert.equal(visit.referringVisitId, 0);
Assert.equal(visit.time, testtime);
Assert.equal(visit.referrerId, 0);
Assert.equal(visit.transitionType, TRANSITION_TYPED);
let uri = NetUtil.newURI(visit.url);
do_check_guid_for_uri(uri, visit.pageGuid);
do_check_guid_for_uri(visit.uri, visit.guid);
Assert.ok(!visit.hidden);
Assert.equal(visit.visitCount, 1);
Assert.equal(visit.typedCount, 1);
Assert.equal(visit.typed, 1);
});
let testuri = NetUtil.newURI("http://firefox.com/");
let testtime = Date.now() * 1000;
@ -85,18 +70,19 @@ add_task(async function test_visitAdded() {
await promiseNotify;
});
add_task(async function test_visitAdded() {
let promiseNotify = promiseVisitAdded(function(visit) {
add_task(async function test_onVisits() {
let promiseNotify = onNotify(function onVisits(aVisits) {
Assert.equal(aVisits.length, 1, "Right number of visits notified");
let visit = aVisits[0];
Assert.ok(visit.uri.equals(testuri));
Assert.ok(visit.visitId > 0);
Assert.equal(visit.url, testuri.spec);
Assert.equal(visit.visitTime, testtime / 1000);
Assert.equal(visit.referringVisitId, 0);
Assert.equal(visit.time, testtime);
Assert.equal(visit.referrerId, 0);
Assert.equal(visit.transitionType, TRANSITION_FRAMED_LINK);
let uri = NetUtil.newURI(visit.url);
do_check_guid_for_uri(uri, visit.pageGuid);
do_check_guid_for_uri(visit.uri, visit.guid);
Assert.ok(visit.hidden);
Assert.equal(visit.visitCount, 1);
Assert.equal(visit.typedCount, 0);
Assert.equal(visit.typed, 0);
});
let testuri = NetUtil.newURI("http://hidden.firefox.com/");
let testtime = Date.now() * 1000;
@ -107,43 +93,44 @@ add_task(async function test_visitAdded() {
add_task(async function test_multiple_onVisit() {
let testuri = NetUtil.newURI("http://self.firefox.com/");
let promiseNotifications = new Promise(resolve => {
function listener(aEvents) {
Assert.equal(aEvents.length, 3, "Right number of visits notified");
for (let i = 0; i < aEvents.length; i++) {
Assert.equal(aEvents[i].type, "page-visited");
let visit = aEvents[i];
Assert.equal(testuri.spec, visit.url);
Assert.ok(visit.visitId > 0);
Assert.ok(visit.visitTime > 0);
Assert.ok(!visit.hidden);
let uri = NetUtil.newURI(visit.url);
do_check_guid_for_uri(uri, visit.pageGuid);
switch (i) {
case 0:
Assert.equal(visit.referringVisitId, 0);
Assert.equal(visit.transitionType, TRANSITION_LINK);
Assert.equal(visit.visitCount, 1);
Assert.equal(visit.typedCount, 0);
break;
case 1:
Assert.ok(visit.referringVisitId > 0);
Assert.equal(visit.transitionType, TRANSITION_LINK);
Assert.equal(visit.visitCount, 2);
Assert.equal(visit.typedCount, 0);
break;
case 2:
Assert.equal(visit.referringVisitId, 0);
Assert.equal(visit.transitionType, TRANSITION_TYPED);
Assert.equal(visit.visitCount, 3);
Assert.equal(visit.typedCount, 1);
let observer = {
__proto__: NavHistoryObserver.prototype,
onVisits(aVisits) {
Assert.equal(aVisits.length, 3, "Right number of visits notified");
for (let i = 0; i < aVisits.length; i++) {
let visit = aVisits[i];
Assert.ok(testuri.equals(visit.uri));
Assert.ok(visit.visitId > 0);
Assert.ok(visit.time > 0);
Assert.ok(!visit.hidden);
do_check_guid_for_uri(visit.uri, visit.guid);
switch (i) {
case 0:
Assert.equal(visit.referrerId, 0);
Assert.equal(visit.transitionType, TRANSITION_LINK);
Assert.equal(visit.visitCount, 1);
Assert.equal(visit.typed, 0);
break;
case 1:
Assert.ok(visit.referrerId > 0);
Assert.equal(visit.transitionType, TRANSITION_LINK);
Assert.equal(visit.visitCount, 2);
Assert.equal(visit.typed, 0);
break;
case 2:
Assert.equal(visit.referrerId, 0);
Assert.equal(visit.transitionType, TRANSITION_TYPED);
Assert.equal(visit.visitCount, 3);
Assert.equal(visit.typed, 1);
PlacesObservers.removeListener(["page-visited"], listener);
resolve();
break;
PlacesUtils.history.removeObserver(observer, false);
resolve();
break;
}
}
}
}
PlacesObservers.addListener(["page-visited"], listener);
},
};
PlacesUtils.history.addObserver(observer);
});
await PlacesTestUtils.addVisits([
{ uri: testuri, transition: TRANSITION_LINK },

View File

@ -12,24 +12,43 @@ var gVisits = [{url: "http://www.mozilla.com/",
transition: TRANSITION_LINK}];
add_task(async function test_execute() {
let observer;
let completionPromise = new Promise(resolveCompletionPromise => {
let visitCount = 0;
function listener(aEvents) {
Assert.equal(aEvents.length, 1, "Right number of visits notified");
Assert.equal(aEvents[0].type, "page-visited");
let event = aEvents[0];
Assert.equal(event.url, gVisits[visitCount].url);
Assert.equal(event.transitionType, gVisits[visitCount].transition);
visitCount++;
observer = {
__proto__: NavHistoryObserver.prototype,
_visitCount: 0,
onVisit(aURI, aVisitID, aTime, aSessionID, aReferringID,
aTransitionType, aAdded) {
Assert.equal(aURI.spec, gVisits[this._visitCount].url);
Assert.equal(aTransitionType, gVisits[this._visitCount].transition);
this._visitCount++;
if (visitCount == gVisits.length) {
resolveCompletionPromise();
PlacesObservers.removeListener(["page-visited"], listener);
}
}
PlacesObservers.addListener(["page-visited"], listener);
if (this._visitCount == gVisits.length) {
resolveCompletionPromise();
}
},
onVisits(aVisits) {
Assert.equal(aVisits.length, 1, "Right number of visits notified");
let {
uri,
visitId,
time,
referrerId,
transitionType,
guid,
hidden,
visitCount,
typed,
lastKnownTitle,
} = aVisits[0];
this.onVisit(uri, visitId, time, 0, referrerId,
transitionType, guid, hidden, visitCount,
typed, lastKnownTitle);
},
};
});
PlacesUtils.history.addObserver(observer);
for (var visit of gVisits) {
if (visit.transition == TRANSITION_TYPED)
@ -48,4 +67,5 @@ add_task(async function test_execute() {
await completionPromise;
PlacesUtils.history.removeObserver(observer);
});

View File

@ -827,6 +827,7 @@ var PageThumbsHistoryObserver = {
onTitleChanged() {},
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisits() {},
onPageChanged() {},
onDeleteVisits() {},

View File

@ -549,9 +549,6 @@ var PlacesProvider = {
*/
init: function PlacesProvider_init() {
PlacesUtils.history.addObserver(this, true);
this._placesObserver =
new PlacesWeakCallbackWrapper(this.handlePlacesEvents.bind(this));
PlacesObservers.addListener(["page-visited"], this._placesObserver);
},
/**
@ -659,11 +656,11 @@ var PlacesProvider = {
}
},
handlePlacesEvents(aEvents) {
onVisits(aVisits) {
if (!this._batchProcessingDepth) {
for (let event of aEvents) {
if (event.visitCount == 1 && event.lastKnownTitle) {
this.onTitleChanged(event.url, event.lastKnownTitle, event.pageGuid);
for (let visit of aVisits) {
if (visit.visitCount == 1 && visit.lastKnownTitle) {
this.onTitleChanged(visit.uri, visit.lastKnownTitle, visit.guid);
}
}
}
@ -714,11 +711,8 @@ var PlacesProvider = {
* 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,
url: aURI.spec,
title: aNewTitle
});
},

View File

@ -41,9 +41,6 @@ module.exports = {
"MatchPatternSet": false,
"MenuBoxObject": false,
// Specific to Firefox (Chrome code only).
"PlacesObservers": false,
"PlacesWeakCallbackWrapper": false,
// Specific to Firefox (Chrome code only).
"SharedArrayBuffer": false,
"SimpleGestureEvent": false,
// Note: StopIteration will likely be removed as part of removing legacy