summaryrefslogtreecommitdiff
path: root/layout
diff options
context:
space:
mode:
Diffstat (limited to 'layout')
-rw-r--r--layout/base/nsPresContext.cpp26
-rw-r--r--layout/style/MediaQueryList.cpp215
-rw-r--r--layout/style/MediaQueryList.h48
3 files changed, 179 insertions, 110 deletions
diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
index befb5deb2e..1d90b967a0 100644
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1931,33 +1931,13 @@ nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
if (!PR_CLIST_IS_EMPTY(mDocument->MediaQueryLists())) {
// We build a list of all the notifications we're going to send
- // before we send any of them. (The spec says the notifications
- // should be a queued task, so any removals that happen during the
- // notifications shouldn't affect what gets notified.) Furthermore,
- // we hold strong pointers to everything we're going to make
- // notification calls to, since each notification involves calling
- // arbitrary script that might otherwise destroy these objects, or,
- // for that matter, |this|.
- //
- // Note that we intentionally send the notifications to media query
- // list in the order they were created and, for each list, to the
- // listeners in the order added.
- nsTArray<MediaQueryList::HandleChangeData> notifyList;
+ // before we send any of them.
for (PRCList *l = PR_LIST_HEAD(mDocument->MediaQueryLists());
l != mDocument->MediaQueryLists(); l = PR_NEXT_LINK(l)) {
+ nsAutoMicroTask mt;
MediaQueryList *mql = static_cast<MediaQueryList*>(l);
- mql->MediumFeaturesChanged(notifyList);
+ mql->MaybeNotify();
}
-
- if (!notifyList.IsEmpty()) {
- for (uint32_t i = 0, i_end = notifyList.Length(); i != i_end; ++i) {
- nsAutoMicroTask mt;
- MediaQueryList::HandleChangeData &d = notifyList[i];
- d.callback->Call(*d.mql);
- }
- }
-
- // NOTE: When |notifyList| goes out of scope, our destructor could run.
}
}
diff --git a/layout/style/MediaQueryList.cpp b/layout/style/MediaQueryList.cpp
index db3781b768..5838645be3 100644
--- a/layout/style/MediaQueryList.cpp
+++ b/layout/style/MediaQueryList.cpp
@@ -1,5 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
@@ -7,19 +6,26 @@
/* implements DOM interface for querying and observing media queries */
#include "mozilla/dom/MediaQueryList.h"
+#include "mozilla/dom/MediaQueryListEvent.h"
+#include "mozilla/dom/EventTarget.h"
+#include "mozilla/dom/EventTargetBinding.h"
#include "nsPresContext.h"
#include "nsIMediaList.h"
#include "nsCSSParser.h"
#include "nsIDocument.h"
+// Fixed event target type
+#define ONCHANGE_STRING NS_LITERAL_STRING("change")
+
namespace mozilla {
namespace dom {
MediaQueryList::MediaQueryList(nsIDocument *aDocument,
const nsAString &aMediaQueryList)
- : mDocument(aDocument),
- mMediaList(new nsMediaList),
- mMatchesValid(false)
+ : mDocument(aDocument)
+ , mMediaList(new nsMediaList)
+ , mMatchesValid(false)
+ , mIsKeptAlive(false)
{
PR_INIT_CLIST(this);
@@ -36,30 +42,24 @@ MediaQueryList::~MediaQueryList()
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaQueryList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaQueryList, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallbacks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaQueryList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList, DOMEventTargetHelper)
if (tmp->mDocument) {
PR_REMOVE_LINK(tmp);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
}
- tmp->RemoveAllListeners();
+ tmp->Disconnect();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(MediaQueryList)
-
-NS_INTERFACE_MAP_BEGIN(MediaQueryList)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsISupports)
- NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(MediaQueryList)
-NS_INTERFACE_MAP_END
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaQueryList)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaQueryList)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaQueryList)
+NS_IMPL_ADDREF_INHERITED(MediaQueryList, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(MediaQueryList, DOMEventTargetHelper)
void
MediaQueryList::GetMedia(nsAString &aMedia)
@@ -80,57 +80,124 @@ MediaQueryList::Matches()
}
void
-MediaQueryList::AddListener(MediaQueryListListener& aListener)
+MediaQueryList::AddListener(EventListener* aListener, ErrorResult& aRv)
{
- if (!HasListeners()) {
- // When we have listeners, the pres context owns a reference to
- // this. This is a cyclic reference that can only be broken by
- // cycle collection.
- NS_ADDREF_THIS();
+ if (!aListener) {
+ return;
}
+ AddEventListenerOptionsOrBoolean options;
+ options.SetAsBoolean() = false;
+
+ AddEventListener(ONCHANGE_STRING, aListener, options, Nullable<bool>(), aRv);
+}
+
+void
+MediaQueryList::AddEventListener(const nsAString& aType,
+ EventListener* aCallback,
+ const AddEventListenerOptionsOrBoolean& aOptions,
+ const dom::Nullable<bool>& aWantsUntrusted,
+ ErrorResult& aRv)
+{
if (!mMatchesValid) {
MOZ_ASSERT(!HasListeners(),
"when listeners present, must keep mMatches current");
RecomputeMatches();
}
- for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
- if (aListener == *mCallbacks[i]) {
- // Already registered
- return;
- }
+ DOMEventTargetHelper::AddEventListener(aType, aCallback, aOptions,
+ aWantsUntrusted, aRv);
+
+ if (aRv.Failed()) {
+ return;
}
- if (!mCallbacks.AppendElement(&aListener, fallible)) {
- if (!HasListeners()) {
- // Append failed; undo the AddRef above.
- NS_RELEASE_THIS();
- }
+ UpdateMustKeepAlive();
+}
+
+void
+MediaQueryList::RemoveListener(EventListener* aListener, ErrorResult& aRv)
+{
+ if (!aListener) {
+ return;
}
+
+ EventListenerOptionsOrBoolean options;
+ options.SetAsBoolean() = false;
+
+ RemoveEventListener(ONCHANGE_STRING, aListener, options, aRv);
}
void
-MediaQueryList::RemoveListener(MediaQueryListListener& aListener)
-{
- for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
- if (aListener == *mCallbacks[i]) {
- mCallbacks.RemoveElementAt(i);
- if (!HasListeners()) {
- // See NS_ADDREF_THIS() in AddListener.
- NS_RELEASE_THIS();
- }
- break;
- }
+MediaQueryList::RemoveEventListener(const nsAString& aType,
+ EventListener* aCallback,
+ const EventListenerOptionsOrBoolean& aOptions,
+ ErrorResult& aRv)
+{
+ DOMEventTargetHelper::RemoveEventListener(aType, aCallback, aOptions, aRv);
+
+ if (aRv.Failed()) {
+ return;
+ }
+
+ UpdateMustKeepAlive();
+}
+
+EventHandlerNonNull*
+MediaQueryList::GetOnchange()
+{
+ if (NS_IsMainThread()) {
+ return GetEventHandler(nsGkAtoms::onchange, EmptyString());
+ }
+ return GetEventHandler(nullptr, ONCHANGE_STRING);
+}
+
+void
+MediaQueryList::SetOnchange(EventHandlerNonNull* aCallback)
+{
+ if (NS_IsMainThread()) {
+ SetEventHandler(nsGkAtoms::onchange, EmptyString(), aCallback);
+ } else {
+ SetEventHandler(nullptr, ONCHANGE_STRING, aCallback);
}
+
+ UpdateMustKeepAlive();
}
void
-MediaQueryList::RemoveAllListeners()
+MediaQueryList::UpdateMustKeepAlive()
{
- bool hadListeners = HasListeners();
- mCallbacks.Clear();
- if (hadListeners) {
+ bool toKeepAlive = HasListeners();
+ if (toKeepAlive == mIsKeptAlive) {
+ return;
+ }
+
+ // When we have listeners, the pres context owns a reference to
+ // this. This is a cyclic reference that can only be broken by
+ // cycle collection.
+
+ mIsKeptAlive = toKeepAlive;
+
+ if (toKeepAlive) {
+ NS_ADDREF_THIS();
+ } else {
+ NS_RELEASE_THIS();
+ }
+}
+
+bool
+MediaQueryList::HasListeners()
+{
+ return HasListenersFor(ONCHANGE_STRING);
+}
+
+void
+MediaQueryList::Disconnect()
+{
+ DisconnectFromOwner();
+
+ if (mIsKeptAlive) {
+ mIsKeptAlive = false;
// See NS_ADDREF_THIS() in AddListener.
NS_RELEASE_THIS();
}
@@ -169,27 +236,6 @@ MediaQueryList::RecomputeMatches()
mMatchesValid = true;
}
-void
-MediaQueryList::MediumFeaturesChanged(
- nsTArray<HandleChangeData>& aListenersToNotify)
-{
- mMatchesValid = false;
-
- if (HasListeners()) {
- bool oldMatches = mMatches;
- RecomputeMatches();
- if (mMatches != oldMatches) {
- for (uint32_t i = 0, i_end = mCallbacks.Length(); i != i_end; ++i) {
- HandleChangeData *d = aListenersToNotify.AppendElement(fallible);
- if (d) {
- d->mql = this;
- d->callback = mCallbacks[i];
- }
- }
- }
- }
-}
-
nsISupports*
MediaQueryList::GetParentObject() const
{
@@ -202,5 +248,36 @@ MediaQueryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return MediaQueryListBinding::Wrap(aCx, this, aGivenProto);
}
+void
+MediaQueryList::MaybeNotify()
+{
+ mMatchesValid = false;
+
+ if (!HasListeners()) {
+ return;
+ }
+
+ bool oldMatches = mMatches;
+ RecomputeMatches();
+
+ // No need to notify the change.
+ if (mMatches == oldMatches) {
+ return;
+ }
+
+ MediaQueryListEventInit init;
+ init.mBubbles = false;
+ init.mCancelable = false;
+ init.mMatches = mMatches;
+ mMediaList->GetText(init.mMedia);
+
+ RefPtr<MediaQueryListEvent> event =
+ MediaQueryListEvent::Constructor(this, ONCHANGE_STRING, init);
+ event->SetTrusted(true);
+
+ bool dummy;
+ DispatchEvent(event, &dummy);
+}
+
} // namespace dom
} // namespace mozilla
diff --git a/layout/style/MediaQueryList.h b/layout/style/MediaQueryList.h
index 5ba568528d..d2acb34c13 100644
--- a/layout/style/MediaQueryList.h
+++ b/layout/style/MediaQueryList.h
@@ -16,6 +16,7 @@
#include "prclist.h"
#include "mozilla/Attributes.h"
#include "nsWrapperCache.h"
+#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/MediaQueryListBinding.h"
class nsIDocument;
@@ -24,8 +25,7 @@ class nsMediaList;
namespace mozilla {
namespace dom {
-class MediaQueryList final : public nsISupports,
- public nsWrapperCache,
+class MediaQueryList final : public DOMEventTargetHelper,
public PRCList
{
public:
@@ -37,33 +37,45 @@ private:
~MediaQueryList();
public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaQueryList)
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaQueryList, DOMEventTargetHelper)
nsISupports* GetParentObject() const;
- struct HandleChangeData {
- RefPtr<MediaQueryList> mql;
- RefPtr<mozilla::dom::MediaQueryListListener> callback;
- };
-
- // Appends listeners that need notification to aListenersToNotify
- void MediumFeaturesChanged(nsTArray<HandleChangeData>& aListenersToNotify);
-
- bool HasListeners() const { return !mCallbacks.IsEmpty(); }
-
- void RemoveAllListeners();
+ void MaybeNotify();
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// WebIDL methods
void GetMedia(nsAString& aMedia);
bool Matches();
- void AddListener(mozilla::dom::MediaQueryListListener& aListener);
- void RemoveListener(mozilla::dom::MediaQueryListListener& aListener);
+ void AddListener(EventListener* aListener, ErrorResult& aRv);
+ void RemoveListener(EventListener* aListener, ErrorResult& aRv);
+
+ EventHandlerNonNull* GetOnchange();
+ void SetOnchange(EventHandlerNonNull* aCallback);
+
+ using nsIDOMEventTarget::AddEventListener;
+ using nsIDOMEventTarget::RemoveEventListener;
+
+ virtual void AddEventListener(const nsAString& aType,
+ EventListener* aCallback,
+ const AddEventListenerOptionsOrBoolean& aOptions,
+ const Nullable<bool>& aWantsUntrusted,
+ ErrorResult& aRv) override;
+ virtual void RemoveEventListener(const nsAString& aType,
+ EventListener* aCallback,
+ const EventListenerOptionsOrBoolean& aOptions,
+ ErrorResult& aRv) override;
+
+ bool HasListeners();
+
+ void Disconnect();
private:
void RecomputeMatches();
+
+ void UpdateMustKeepAlive();
// We only need a pointer to the document to support lazy
// reevaluation following dynamic changes. However, this lazy
@@ -84,7 +96,7 @@ private:
RefPtr<nsMediaList> mMediaList;
bool mMatches;
bool mMatchesValid;
- nsTArray<RefPtr<mozilla::dom::MediaQueryListListener>> mCallbacks;
+ bool mIsKeptAlive;
};
} // namespace dom