gecko-dev/layout/style/MediaQueryList.cpp
Emilio Cobos Álvarez 494a283fa7 Bug 1490012 - Don't flush on the parent document if we already have a pres context. r=xidorn
I think conceptually we should flush layout instead of frames and do it
unconditionally, but everyone agrees on not doing that, and it can be really
slow, so will raise it to the CSSWG and get this spec'd.

See the test-case in bug 1458816, which tracks that.

I don't want to uplift this, but I think it's worth landing.

Differential Revision: https://phabricator.services.mozilla.com/D5562
2018-09-12 15:45:18 +02:00

218 lines
5.2 KiB
C++

/* -*- 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/. */
/* implements DOM interface for querying and observing media queries */
#include "mozilla/dom/MediaQueryList.h"
#include "mozilla/dom/MediaQueryListEvent.h"
#include "mozilla/dom/MediaList.h"
#include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/EventTargetBinding.h"
#include "nsPresContext.h"
#include "nsIDocument.h"
#define ONCHANGE_STRING NS_LITERAL_STRING("change")
namespace mozilla {
namespace dom {
MediaQueryList::MediaQueryList(nsIDocument* aDocument,
const nsAString& aMediaQueryList,
CallerType aCallerType)
: DOMEventTargetHelper(aDocument->GetInnerWindow())
, mDocument(aDocument)
, mMatches(false)
, mMatchesValid(false)
{
mMediaList = MediaList::Create(aMediaQueryList, aCallerType);
KeepAliveIfHasListenersFor(ONCHANGE_STRING);
}
MediaQueryList::~MediaQueryList()
{}
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaQueryList,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList,
DOMEventTargetHelper)
if (tmp->mDocument) {
static_cast<LinkedListElement<MediaQueryList>*>(tmp)->remove();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
}
tmp->Disconnect();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaQueryList)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(MediaQueryList, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(MediaQueryList, DOMEventTargetHelper)
void
MediaQueryList::GetMedia(nsAString &aMedia)
{
mMediaList->GetText(aMedia);
}
bool
MediaQueryList::Matches()
{
if (!mMatchesValid) {
MOZ_ASSERT(!HasListeners(),
"when listeners present, must keep mMatches current");
RecomputeMatches();
}
return mMatches;
}
void
MediaQueryList::AddListener(EventListener* aListener, ErrorResult& aRv)
{
if (!aListener) {
return;
}
AddEventListenerOptionsOrBoolean options;
options.SetAsBoolean() = false;
AddEventListener(ONCHANGE_STRING, aListener, options, false, aRv);
}
void
MediaQueryList::EventListenerAdded(nsAtom* aType)
{
// HasListeners() might still be false if the added thing wasn't a
// listener we care about.
if (!mMatchesValid && HasListeners()) {
RecomputeMatches();
}
DOMEventTargetHelper::EventListenerAdded(aType);
}
void
MediaQueryList::RemoveListener(EventListener* aListener, ErrorResult& aRv)
{
if (!aListener) {
return;
}
EventListenerOptionsOrBoolean options;
options.SetAsBoolean() = false;
RemoveEventListener(ONCHANGE_STRING, aListener, options, aRv);
}
bool
MediaQueryList::HasListeners()
{
return HasListenersFor(ONCHANGE_STRING);
}
void
MediaQueryList::Disconnect()
{
DisconnectFromOwner();
IgnoreKeepAliveIfHasListenersFor(ONCHANGE_STRING);
}
void
MediaQueryList::RecomputeMatches()
{
mMatches = false;
if (!mDocument) {
return;
}
// FIXME(emilio, bug 1490401): We shouldn't need a pres context to evaluate
// media queries.
nsPresContext* presContext = mDocument->GetPresContext();
if (!presContext && mDocument->GetParentDocument()) {
// Flush frames on the parent so our prescontext will get
// created if needed.
mDocument->GetParentDocument()->FlushPendingNotifications(FlushType::Frames);
// That might have killed our document, so recheck that.
if (!mDocument) {
return;
}
presContext = mDocument->GetPresContext();
}
if (!presContext) {
// XXXbz What's the right behavior here? Spec doesn't say.
return;
}
mMatches = mMediaList->Matches(presContext);
mMatchesValid = true;
}
nsISupports*
MediaQueryList::GetParentObject() const
{
return mDocument;
}
JSObject*
MediaQueryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return MediaQueryList_Binding::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);
DispatchEvent(*event);
}
size_t
MediaQueryList::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
// mMediaList is reference counted, but it's created and primarily owned
// by this MediaQueryList object.
n += mMediaList->SizeOfIncludingThis(aMallocSizeOf);
return n;
}
} // namespace dom
} // namespace mozilla