Update MediaQueryList to the current draft spec.
parent
905c84ec47
commit
930db7cb29
|
@ -1785,7 +1785,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
|||
l != &tmp->mDOMMediaQueryLists; ) {
|
||||
PRCList *next = PR_NEXT_LINK(l);
|
||||
MediaQueryList *mql = static_cast<MediaQueryList*>(l);
|
||||
mql->RemoveAllListeners();
|
||||
mql->Disconnect();
|
||||
l = next;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,17 +4,21 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* http://dev.w3.org/csswg/cssom-view/#the-mediaquerylist-interface
|
||||
* https://drafts.csswg.org/cssom-view/#mediaquerylist
|
||||
*
|
||||
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
interface MediaQueryList {
|
||||
interface MediaQueryList : EventTarget {
|
||||
readonly attribute DOMString media;
|
||||
readonly attribute boolean matches;
|
||||
void addListener(MediaQueryListListener listener);
|
||||
void removeListener(MediaQueryListListener listener);
|
||||
};
|
||||
|
||||
callback MediaQueryListListener = void (MediaQueryList list);
|
||||
[Throws]
|
||||
void addListener(EventListener? listener);
|
||||
|
||||
[Throws]
|
||||
void removeListener(EventListener? listener);
|
||||
|
||||
attribute EventHandler onchange;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- 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/.
|
||||
*
|
||||
* https://drafts.csswg.org/cssom-view/#mediaquerylistevent
|
||||
*/
|
||||
|
||||
[Constructor(DOMString type, optional MediaQueryListEventInit eventInitDict)]
|
||||
interface MediaQueryListEvent : Event {
|
||||
readonly attribute DOMString media;
|
||||
readonly attribute boolean matches;
|
||||
};
|
||||
|
||||
dictionary MediaQueryListEventInit : EventInit {
|
||||
DOMString media = "";
|
||||
boolean matches = false;
|
||||
};
|
|
@ -683,6 +683,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
|
|||
'HashChangeEvent.webidl',
|
||||
'HiddenPluginEvent.webidl',
|
||||
'ImageCaptureErrorEvent.webidl',
|
||||
'MediaQueryListEvent.webidl',
|
||||
'MediaStreamEvent.webidl',
|
||||
'MediaStreamTrackEvent.webidl',
|
||||
'MozSettingsEvent.webidl',
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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_CYCLE_COLLECTION_INHERITED(MediaQueryList)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
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_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(MediaQueryListListener& aListener)
|
||||
MediaQueryList::RemoveListener(EventListener* aListener, ErrorResult& aRv)
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (!aListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
EventListenerOptionsOrBoolean options;
|
||||
options.SetAsBoolean() = false;
|
||||
|
||||
RemoveEventListener(ONCHANGE_STRING, aListener, options, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
MediaQueryList::RemoveAllListeners()
|
||||
MediaQueryList::RemoveEventListener(const nsAString& aType,
|
||||
EventListener* aCallback,
|
||||
const EventListenerOptionsOrBoolean& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
bool hadListeners = HasListeners();
|
||||
mCallbacks.Clear();
|
||||
if (hadListeners) {
|
||||
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::UpdateMustKeepAlive()
|
||||
{
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue