summaryrefslogtreecommitdiffstats
path: root/layout/style/MediaQueryList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/MediaQueryList.cpp')
-rw-r--r--layout/style/MediaQueryList.cpp207
1 files changed, 207 insertions, 0 deletions
diff --git a/layout/style/MediaQueryList.cpp b/layout/style/MediaQueryList.cpp
new file mode 100644
index 000000000..069e049c4
--- /dev/null
+++ b/layout/style/MediaQueryList.cpp
@@ -0,0 +1,207 @@
+/* -*- 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/. */
+
+/* implements DOM interface for querying and observing media queries */
+
+#include "mozilla/dom/MediaQueryList.h"
+#include "nsPresContext.h"
+#include "nsIMediaList.h"
+#include "nsCSSParser.h"
+#include "nsIDocument.h"
+
+namespace mozilla {
+namespace dom {
+
+MediaQueryList::MediaQueryList(nsIDocument *aDocument,
+ const nsAString &aMediaQueryList)
+ : mDocument(aDocument),
+ mMediaList(new nsMediaList),
+ mMatchesValid(false)
+{
+ PR_INIT_CLIST(this);
+
+ nsCSSParser parser;
+ parser.ParseMediaList(aMediaQueryList, nullptr, 0, mMediaList, false);
+}
+
+MediaQueryList::~MediaQueryList()
+{
+ if (mDocument) {
+ PR_REMOVE_LINK(this);
+ }
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaQueryList)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallbacks)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaQueryList)
+ if (tmp->mDocument) {
+ PR_REMOVE_LINK(tmp);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
+ }
+ tmp->RemoveAllListeners();
+ 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_IMPL_CYCLE_COLLECTING_ADDREF(MediaQueryList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaQueryList)
+
+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(MediaQueryListListener& aListener)
+{
+ 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 (!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;
+ }
+ }
+
+ if (!mCallbacks.AppendElement(&aListener, fallible)) {
+ if (!HasListeners()) {
+ // Append failed; undo the AddRef above.
+ NS_RELEASE_THIS();
+ }
+ }
+}
+
+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;
+ }
+ }
+}
+
+void
+MediaQueryList::RemoveAllListeners()
+{
+ bool hadListeners = HasListeners();
+ mCallbacks.Clear();
+ if (hadListeners) {
+ // See NS_ADDREF_THIS() in AddListener.
+ NS_RELEASE_THIS();
+ }
+}
+
+void
+MediaQueryList::RecomputeMatches()
+{
+ if (!mDocument) {
+ return;
+ }
+
+ if (mDocument->GetParentDocument()) {
+ // Flush frames on the parent so our prescontext will get
+ // recreated as needed.
+ mDocument->GetParentDocument()->FlushPendingNotifications(Flush_Frames);
+ // That might have killed our document, so recheck that.
+ if (!mDocument) {
+ return;
+ }
+ }
+
+ nsIPresShell* shell = mDocument->GetShell();
+ if (!shell) {
+ // XXXbz What's the right behavior here? Spec doesn't say.
+ return;
+ }
+
+ nsPresContext* presContext = shell->GetPresContext();
+ if (!presContext) {
+ // XXXbz What's the right behavior here? Spec doesn't say.
+ return;
+ }
+
+ mMatches = mMediaList->Matches(presContext, nullptr);
+ 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
+{
+ return mDocument;
+}
+
+JSObject*
+MediaQueryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return MediaQueryListBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla