new file mode 100644
--- /dev/null
+++ b/dom/base/PlacesEvent.cpp
@@ -0,0 +1,52 @@
+/* -*- 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 PlacesEvent_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/PlacesEvent.h
@@ -0,0 +1,48 @@
+/* -*- 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
new file mode 100644
--- /dev/null
+++ b/dom/base/PlacesObservers.cpp
@@ -0,0 +1,350 @@
+/* -*- 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
new file mode 100644
--- /dev/null
+++ b/dom/base/PlacesObservers.h
@@ -0,0 +1,62 @@
+/* -*- 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__
new file mode 100644
--- /dev/null
+++ b/dom/base/PlacesVisit.h
@@ -0,0 +1,67 @@
+/* -*- 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 PlacesVisit_Binding::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
new file mode 100644
--- /dev/null
+++ b/dom/base/PlacesWeakCallbackWrapper.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 PlacesWeakCallbackWrapper_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/PlacesWeakCallbackWrapper.h
@@ -0,0 +1,49 @@
+/* -*- 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
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -196,16 +196,20 @@ EXPORTS.mozilla.dom += [
'MessageSender.h',
'MozQueryInterface.h',
'NameSpaceConstants.h',
'Navigator.h',
'NodeInfo.h',
'NodeInfoInlines.h',
'NodeIterator.h',
'ParentProcessMessageManager.h',
+ 'PlacesEvent.h',
+ 'PlacesObservers.h',
+ 'PlacesVisit.h',
+ 'PlacesWeakCallbackWrapper.h',
'Pose.h',
'ProcessGlobal.h',
'ProcessMessageManager.h',
'ResponsiveImageSelector.h',
'SameProcessMessageQueue.h',
'ScreenOrientation.h',
'Selection.h',
'ShadowRoot.h',
@@ -385,16 +389,23 @@ if CONFIG['MOZ_WEBRTC']:
'nsDOMDataChannel.cpp',
]
if CONFIG['FUZZING']:
UNIFIED_SOURCES += [
'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.
'DOMIntersectionObserver.cpp',
# Because of OS X headers.
'nsContentUtils.cpp',
# this file doesn't like windows.h
'nsDOMWindowUtils.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/PlacesEvent.webidl
@@ -0,0 +1,69 @@
+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;
+};
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/PlacesObservers.webidl
@@ -0,0 +1,32 @@
+/* -*- 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);
+};
+
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -41,8 +41,14 @@ WEBIDL_FILES = [
'MozStorageStatementParams.webidl',
'MozStorageStatementRow.webidl',
'PrecompiledScript.webidl',
'PromiseDebugging.webidl',
'StructuredCloneHolder.webidl',
'WebExtensionContentScript.webidl',
'WebExtensionPolicy.webidl',
]
+
+if CONFIG['MOZ_PLACES']:
+ WEBIDL_FILES += [
+ 'PlacesEvent.webidl',
+ 'PlacesObservers.webidl',
+ ]
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -165,131 +165,16 @@ struct VisitData {
bool shouldUpdateFrecency;
// Whether to override the visit type bonus with a redirect bonus when
// calculating frecency on the most recent visit.
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
/**
* Used to store visit filters for RemoveVisits.
*/
struct RemoveVisitsFilter {
RemoveVisitsFilter()
: transitionType(UINT32_MAX)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/INativePlacesEventCallback.h
@@ -0,0 +1,34 @@
+/* -*- 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
--- a/toolkit/components/places/moz.build
+++ b/toolkit/components/places/moz.build
@@ -24,16 +24,17 @@ if CONFIG['MOZ_PLACES']:
'nsIFaviconService.idl',
'nsINavBookmarksService.idl',
'nsITaggingService.idl',
]
EXPORTS.mozilla.places = [
'Database.h',
'History.h',
+ 'INativePlacesEventCallback.h',
'Shutdown.h',
]
UNIFIED_SOURCES += [
'Database.cpp',
'FaviconHelpers.cpp',
'Helpers.cpp',
'History.cpp',
--- a/toolkit/components/places/nsINavHistoryService.idl
+++ b/toolkit/components/places/nsINavHistoryService.idl
@@ -24,77 +24,16 @@ interface nsIAsyncShutdownClient;
interface nsINavHistoryContainerResultNode;
interface nsINavHistoryQueryResultNode;
interface nsINavHistoryQuery;
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
{
/**
* Indentifies the parent result node in the result set. This is null for
* top level nodes.
*/
readonly attribute nsINavHistoryContainerResultNode parent;
@@ -638,28 +577,16 @@ interface nsINavHistoryObserver : nsISup
/**
* Notifies you that we are done doing a bunch of things and you should go
* ahead and update UI, etc.
*/
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
* only one will change at a time. Often, consumers will want to display the
* user title if it is available, and fall back to the page title (the one
* specified in the <title> tag of the page).
*
* Note that there is a difference between an empty title and a NULL title.
* An empty string means that somebody specifically set the title to be