Bug 771043. Move MediaQueryList tracking from the prescontext to the document, so they will correctly outlive prescontext changes. r=dbaron
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 03 Oct 2014 14:15:25 -0400
changeset 216517 fd343a8b5cfedc263d0d611fbedc6716071a0983
parent 216516 84d6178e3be2e7d21d68e20c4a0a4b2b4a1fb626
child 216518 613266d6f6ecbf2a2ebd07f88ec4c841fb93324a
push idunknown
push userunknown
push dateunknown
reviewersdbaron
bugs771043
milestone36.0a1
Bug 771043. Move MediaQueryList tracking from the prescontext to the document, so they will correctly outlive prescontext changes. r=dbaron
dom/base/nsDocument.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsIDocument.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/style/MediaQueryList.cpp
layout/style/MediaQueryList.h
layout/style/test/mochitest.ini
layout/style/test/test_bug771043.html
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -185,16 +185,17 @@
 #include "nsIAppsService.h"
 #include "mozilla/dom/AnonymousContent.h"
 #include "mozilla/dom/AnimationTimeline.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/HTMLBodyElement.h"
 #include "mozilla/dom/HTMLInputElement.h"
+#include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/NodeFilterBinding.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/UndoManager.h"
 #include "mozilla/dom/WebComponentsBinding.h"
 #include "nsFrame.h"
 #include "nsDOMCaretPosition.h"
 #include "nsIDOMHTMLTextAreaElement.h"
@@ -1555,16 +1556,18 @@ nsIDocument::nsIDocument()
     // &&-ed in, this is safe.
     mAllowDNSPrefetch(true),
     mIsBeingUsedAsImage(false),
     mHasLinksToUpdate(false),
     mPartID(0),
     mDidFireDOMContentLoaded(true)
 {
   SetInDocument();
+
+  PR_INIT_CLIST(&mDOMMediaQueryLists);  
 }
 
 // NOTE! nsDocument::operator new() zeroes out all members, so don't
 // bother initializing members to 0.
 
 nsDocument::nsDocument(const char* aContentType)
   : nsIDocument()
   , mAnimatingImages(true)
@@ -1600,16 +1603,19 @@ ClearAllBoxObjects(nsIContent* aKey, nsP
   if (aBoxObject) {
     aBoxObject->Clear();
   }
   return PL_DHASH_NEXT;
 }
 
 nsIDocument::~nsIDocument()
 {
+  NS_ABORT_IF_FALSE(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists),
+                    "must not have media query lists left");
+
   if (mNodeInfoManager) {
     mNodeInfoManager->DropDocumentReference();
   }
 }
 
 
 nsDocument::~nsDocument()
 {
@@ -2013,16 +2019,28 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
 
   for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
     nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb);
   }
+
+  // We own only the items in mDOMMediaQueryLists that have listeners;
+  // this reference is managed by their AddListener and RemoveListener
+  // methods.
+  for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
+       l != &tmp->mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
+    MediaQueryList *mql = static_cast<MediaQueryList*>(l);
+    if (mql->HasListeners()) {
+      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
+      cb.NoteXPCOMChild(mql);
+    }
+  }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
   if (tmp->PreservingWrapper()) {
     NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
   }
@@ -2115,16 +2133,27 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     tmp->mCSSLoader->DropDocumentReference();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
   }
 
   for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
     nsHostObjectProtocolHandler::RemoveDataEntry(tmp->mHostObjectURIs[i]);
   }
 
+  // We own only the items in mDOMMediaQueryLists that have listeners;
+  // this reference is managed by their AddListener and RemoveListener
+  // methods.
+  for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
+       l != &tmp->mDOMMediaQueryLists; ) {
+    PRCList *next = PR_NEXT_LINK(l);
+    MediaQueryList *mql = static_cast<MediaQueryList*>(l);
+    mql->RemoveAllListeners();
+    l = next;
+  }
+
   tmp->mInUnlinkOrDeletion = false;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 static bool sPrefsInitialized = false;
 static uint32_t sOnloadDecodeLimit = 0;
 
 nsresult
 nsDocument::Init()
@@ -7134,16 +7163,27 @@ nsDocument::ClearBoxObjectFor(nsIContent
     nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent);
     if (boxObject) {
       boxObject->Clear();
       mBoxObjectTable->Remove(aContent);
     }
   }
 }
 
+already_AddRefed<MediaQueryList>
+nsIDocument::MatchMedia(const nsAString& aMediaQueryList)
+{
+  nsRefPtr<MediaQueryList> result = new MediaQueryList(this, aMediaQueryList);
+
+  // Insert the new item at the end of the linked list.
+  PR_INSERT_BEFORE(result, &mDOMMediaQueryLists);
+
+  return result.forget();
+}
+
 void
 nsDocument::FlushSkinBindings()
 {
   BindingManager()->FlushSkinBindings();
 }
 
 nsresult
 nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5351,36 +5351,21 @@ nsGlobalWindow::MatchMedia(const nsAStri
 {
   // FIXME: This whole forward-to-outer and then get a pres
   // shell/context off the docshell dance is sort of silly; it'd make
   // more sense to forward to the inner, but it's what everyone else
   // (GetSelection, GetScrollXY, etc.) does around here.
   FORWARD_TO_OUTER_OR_THROW(MatchMedia, (aMediaQueryList, aError), aError,
                             nullptr);
 
-  // We need this now to ensure that we have a non-null |presContext|
-  // when we ought to.
-  // This is similar to EnsureSizeUpToDate, but only flushes frames.
-  nsGlobalWindow *parent = static_cast<nsGlobalWindow*>(GetPrivateParent());
-  if (parent) {
-    parent->FlushPendingNotifications(Flush_Frames);
-  }
-
-  if (!mDocShell) {
+  if (!mDoc) {
     return nullptr;
   }
 
-  nsRefPtr<nsPresContext> presContext;
-  mDocShell->GetPresContext(getter_AddRefs(presContext));
-
-  if (!presContext) {
-    return nullptr;
-  }
-
-  return presContext->MatchMedia(aMediaQueryList);
+  return mDoc->MatchMedia(aMediaQueryList);
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList,
                            nsISupports** aResult)
 {
   ErrorResult rv;
   nsRefPtr<MediaQueryList> mediaQueryList = MatchMedia(aMediaQueryList, rv);
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -21,16 +21,17 @@
 #include "nsTHashtable.h"                // for member
 #include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "nsWeakReference.h"
 #include "mozilla/dom/DocumentBinding.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
+#include "prclist.h"
 
 class imgIRequest;
 class nsAString;
 class nsBindingManager;
 class nsIDocShell;
 class nsDocShell;
 class nsDOMNavigationTiming;
 class nsFrameLoader;
@@ -109,16 +110,17 @@ class Event;
 class EventTarget;
 class FontFaceSet;
 class FrameRequestCallback;
 class ImportManager;
 class OverfillCallback;
 class HTMLBodyElement;
 struct LifecycleCallbackArgs;
 class Link;
+class MediaQueryList;
 class GlobalObject;
 class NodeFilter;
 class NodeIterator;
 class ProcessingInstruction;
 class StyleSheetList;
 class SVGDocument;
 class Touch;
 class TouchList;
@@ -1521,16 +1523,27 @@ public:
    * Get the box object for an element. This is not exposed through a
    * scriptable interface except for XUL documents.
    */
   virtual already_AddRefed<mozilla::dom::BoxObject>
     GetBoxObjectFor(mozilla::dom::Element* aElement,
                     mozilla::ErrorResult& aRv) = 0;
 
   /**
+   * Support for window.matchMedia()
+   */
+
+  already_AddRefed<mozilla::dom::MediaQueryList>
+    MatchMedia(const nsAString& aMediaQueryList);
+
+  const PRCList* MediaQueryLists() const {
+    return &mDOMMediaQueryLists;
+  }
+
+  /**
    * Get the compatibility mode for this document
    */
   nsCompatibility GetCompatibilityMode() const {
     return mCompatMode;
   }
   
   /**
    * Check whether we've ever fired a DOMTitleChanged event for this
@@ -2772,16 +2785,19 @@ protected:
   uint32_t mInSyncOperationCount;
 
   nsRefPtr<mozilla::dom::XPathEvaluator> mXPathEvaluator;
 
   nsTArray<nsRefPtr<mozilla::dom::AnonymousContent>> mAnonymousContents;
 
   uint32_t mBlockDOMContentLoaded;
   bool mDidFireDOMContentLoaded:1;
+
+  // Our live MediaQueryLists
+  PRCList mDOMMediaQueryLists;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -243,18 +243,16 @@ nsPresContext::nsPresContext(nsIDocument
 
   mCounterStylesDirty = true;
 
   // if text perf logging enabled, init stats struct
   PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textperf);
   if (log && log->level >= PR_LOG_WARNING) {
     mTextPerf = new gfxTextPerfMetrics();
   }
-
-  PR_INIT_CLIST(&mDOMMediaQueryLists);
 }
 
 void
 nsPresContext::Destroy()
 {
   if (mEventManager) {
     // unclear if these are needed, but can't hurt
     mEventManager->NotifyDestroyPresContext(this);
@@ -320,19 +318,16 @@ nsPresContext::Destroy()
   }
 }
 
 nsPresContext::~nsPresContext()
 {
   NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer");
   SetShell(nullptr);
 
-  NS_ABORT_IF_FALSE(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists),
-                    "must not have media query lists left");
-
   Destroy();
 }
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
    NS_INTERFACE_MAP_ENTRY(nsISupports)
    NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
@@ -350,48 +345,25 @@ nsPresContext::LastRelease()
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
 
-  // We own only the items in mDOMMediaQueryLists that have listeners;
-  // this reference is managed by their AddListener and RemoveListener
-  // methods.
-  for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
-       l != &tmp->mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
-    MediaQueryList *mql = static_cast<MediaQueryList*>(l);
-    if (mql->HasListeners()) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
-      cb.NoteXPCOMChild(mql);
-    }
-  }
-
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLangService); // a service
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrefChangedTimer);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering?
-  // We own only the items in mDOMMediaQueryLists that have listeners;
-  // this reference is managed by their AddListener and RemoveListener
-  // methods.
-  for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
-       l != &tmp->mDOMMediaQueryLists; ) {
-    PRCList *next = PR_NEXT_LINK(l);
-    MediaQueryList *mql = static_cast<MediaQueryList*>(l);
-    mql->RemoveAllListeners();
-    l = next;
-  }
-
   // NS_RELEASE(tmp->mLanguage); // an atom
   // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
   // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
 
   tmp->Destroy();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
@@ -1905,45 +1877,45 @@ nsPresContext::MediaFeatureValuesChanged
 
   if (aRestyleHint || aChangeHint) {
     RebuildAllStyleData(aChangeHint, aRestyleHint);
   }
 
   mPendingViewportChange = false;
 
   if (mDocument->IsBeingUsedAsImage()) {
-    MOZ_ASSERT(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists));
+    MOZ_ASSERT(PR_CLIST_IS_EMPTY(mDocument->MediaQueryLists()));
     return;
   }
 
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   // Media query list listeners should be notified from a queued task
   // (in HTML5 terms), although we also want to notify them on certain
   // flushes.  (We're already running off an event.)
   //
   // Note that we do this after the new style from media queries in
   // style sheets has been computed.
 
-  if (!PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists)) {
+  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.
     MediaQueryList::NotifyList notifyList;
-    for (PRCList *l = PR_LIST_HEAD(&mDOMMediaQueryLists);
-         l != &mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
+    for (PRCList *l = PR_LIST_HEAD(mDocument->MediaQueryLists());
+         l != mDocument->MediaQueryLists(); l = PR_NEXT_LINK(l)) {
       MediaQueryList *mql = static_cast<MediaQueryList*>(l);
       mql->MediumFeaturesChanged(notifyList);
     }
 
     if (!notifyList.IsEmpty()) {
       for (uint32_t i = 0, i_end = notifyList.Length(); i != i_end; ++i) {
         nsAutoMicroTask mt;
         MediaQueryList::HandleChangeData &d = notifyList[i];
@@ -1977,27 +1949,16 @@ nsPresContext::HandleMediaFeatureValuesC
 {
   // Null-check mShell in case the shell has been destroyed (and the
   // event is the only thing holding the pres context alive).
   if (mPendingMediaFeatureValuesChanged && mShell) {
     MediaFeatureValuesChanged(nsRestyleHint(0));
   }
 }
 
-already_AddRefed<MediaQueryList>
-nsPresContext::MatchMedia(const nsAString& aMediaQueryList)
-{
-  nsRefPtr<MediaQueryList> result = new MediaQueryList(this, aMediaQueryList);
-
-  // Insert the new item at the end of the linked list.
-  PR_INSERT_BEFORE(result, &mDOMMediaQueryLists);
-
-  return result.forget();
-}
-
 nsCompatibility
 nsPresContext::CompatibilityMode() const
 {
   return Document()->GetCompatibilityMode();
 }
 
 void
 nsPresContext::SetPaginatedScrolling(bool aPaginated)
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -67,17 +67,16 @@ class nsIWidget;
 class nsDeviceContext;
 
 namespace mozilla {
 class EventStateManager;
 class RestyleManager;
 class CounterStyleManager;
 namespace dom {
 class FontFaceSet;
-class MediaQueryList;
 }
 namespace layers {
 class ContainerLayer;
 }
 }
 
 // supported values for cached bool types
 enum nsPresContext_CachedBoolPrefType {
@@ -285,22 +284,16 @@ public:
   void PostMediaFeatureValuesChangedEvent();
   void HandleMediaFeatureValuesChangedEvent();
   void FlushPendingMediaFeatureValuesChanged() {
     if (mPendingMediaFeatureValuesChanged)
       MediaFeatureValuesChanged(nsRestyleHint(0));
   }
 
   /**
-   * Support for window.matchMedia()
-   */
-  already_AddRefed<mozilla::dom::MediaQueryList>
-    MatchMedia(const nsAString& aMediaQueryList);
-
-  /**
    * Access compatibility mode for this context.  This is the same as
    * our document's compatibility mode.
    */
   nsCompatibility CompatibilityMode() const;
 
   /**
    * Notify the context that the document's compatibility mode has changed
    */
@@ -1234,18 +1227,16 @@ public:
   // Should we disable font size inflation because we're inside of
   // shrink-wrapping calculations on an inflation container?
   bool                  mInflationDisabledForShrinkWrap;
 
 protected:
 
   mozilla::WeakPtr<nsDocShell>             mContainer;
 
-  PRCList               mDOMMediaQueryLists;
-
   // Base minimum font size, independent of the language-specific global preference. Defaults to 0
   int32_t               mBaseMinFontSize;
   float                 mTextZoom;      // Text zoom, defaults to 1.0
   float                 mFullZoom;      // Page zoom, defaults to 1.0
 
   float                 mLastFontInflationScreenWidth;
 
   int32_t               mCurAppUnitsPerDevPixel;
--- a/layout/style/MediaQueryList.cpp
+++ b/layout/style/MediaQueryList.cpp
@@ -1,55 +1,56 @@
+/* -*- 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(nsPresContext *aPresContext,
+MediaQueryList::MediaQueryList(nsIDocument *aDocument,
                                const nsAString &aMediaQueryList)
-  : mPresContext(aPresContext),
+  : mDocument(aDocument),
     mMediaList(new nsMediaList),
     mMatchesValid(false)
 {
   PR_INIT_CLIST(this);
 
   nsCSSParser parser;
   parser.ParseMediaList(aMediaQueryList, nullptr, 0, mMediaList, false);
 }
 
 MediaQueryList::~MediaQueryList()
 {
-  if (mPresContext) {
+  if (mDocument) {
     PR_REMOVE_LINK(this);
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaQueryList)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext)
+  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->mPresContext) {
+  if (tmp->mDocument) {
     PR_REMOVE_LINK(tmp);
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mPresContext)
+    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)
@@ -133,21 +134,43 @@ MediaQueryList::RemoveAllListeners()
     // See NS_ADDREF_THIS() in AddListener.
     NS_RELEASE_THIS();
   }
 }
 
 void
 MediaQueryList::RecomputeMatches()
 {
-  if (!mPresContext) {
+  if (!mDocument) {
     return;
   }
 
-  mMatches = mMediaList->Matches(mPresContext, nullptr);
+  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(NotifyList &aListenersToNotify)
 {
   mMatchesValid = false;
 
@@ -164,20 +187,17 @@ MediaQueryList::MediumFeaturesChanged(No
       }
     }
   }
 }
 
 nsISupports*
 MediaQueryList::GetParentObject() const
 {
-  if (!mPresContext) {
-    return nullptr;
-  }
-  return mPresContext->Document();
+  return mDocument;
 }
 
 JSObject*
 MediaQueryList::WrapObject(JSContext* aCx)
 {
   return MediaQueryListBinding::Wrap(aCx, this);
 }
 
--- a/layout/style/MediaQueryList.h
+++ b/layout/style/MediaQueryList.h
@@ -1,41 +1,43 @@
+/* -*- 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 */
 
 #ifndef mozilla_dom_MediaQueryList_h
 #define mozilla_dom_MediaQueryList_h
 
 #include "nsISupports.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "prclist.h"
 #include "mozilla/Attributes.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/MediaQueryListBinding.h"
 
-class nsPresContext;
+class nsIDocument;
 class nsMediaList;
 
 namespace mozilla {
 namespace dom {
 
 class MediaQueryList MOZ_FINAL : public nsISupports,
                                  public nsWrapperCache,
                                  public PRCList
 {
 public:
   // The caller who constructs is responsible for calling Evaluate
   // before calling any other methods.
-  MediaQueryList(nsPresContext *aPresContext,
+  MediaQueryList(nsIDocument *aDocument,
                  const nsAString &aMediaQueryList);
 private:
   ~MediaQueryList();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaQueryList)
 
@@ -62,31 +64,31 @@ public:
   void GetMedia(nsAString& aMedia);
   bool Matches();
   void AddListener(mozilla::dom::MediaQueryListListener& aListener);
   void RemoveListener(mozilla::dom::MediaQueryListListener& aListener);
 
 private:
   void RecomputeMatches();
 
-  // We only need a pointer to the pres context to support lazy
+  // We only need a pointer to the document to support lazy
   // reevaluation following dynamic changes.  However, this lazy
   // reevaluation is perhaps somewhat important, since some usage
   // patterns may involve the creation of large numbers of
   // MediaQueryList objects which almost immediately become garbage
   // (after a single call to the .matches getter).
   //
   // This pointer does make us a little more dependent on cycle
   // collection.
   //
-  // We have a non-null mPresContext for our entire lifetime except
-  // after cycle collection unlinking.  Having a non-null mPresContext
-  // is equivalent to being in that pres context's mDOMMediaQueryLists
+  // We have a non-null mDocument for our entire lifetime except
+  // after cycle collection unlinking.  Having a non-null mDocument
+  // is equivalent to being in that document's mDOMMediaQueryLists
   // linked list.
-  nsRefPtr<nsPresContext> mPresContext;
+  nsCOMPtr<nsIDocument> mDocument;
 
   nsRefPtr<nsMediaList> mMediaList;
   bool mMatches;
   bool mMatchesValid;
   CallbackList mCallbacks;
 };
 
 } // namespace dom
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -92,16 +92,17 @@ support-files = bug517224.sjs
 [test_bug652486.html]
 [test_bug657143.html]
 [test_bug664955.html]
 [test_bug667520.html]
 [test_bug645998.html]
 support-files = file_bug645998-1.css file_bug645998-2.css
 [test_bug716226.html]
 [test_bug765590.html]
+[test_bug771043.html]
 [test_bug798567.html]
 [test_bug798843_pref.html]
 [test_bug829816.html]
 [test_bug874919.html]
 support-files = file_bug829816.css
 [test_bug887741_at-rules_in_declaration_lists.html]
 [test_bug892929.html]
 [test_cascade.html]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_bug771043.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=771043
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 771043</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 771043 **/
+  var expectedValue;
+  var callCount = 0;
+  var storedHeight;
+  function callback(arg) {
+    ++callCount;
+    is(arg.matches, expectedValue,
+       "Should have the right value on call #" + callCount + " to the callback");
+    SimpleTest.executeSoon(tests.shift());
+  }
+
+  function flushLayout() {
+    storedHeight = document.querySelector("iframe").offsetHeight;
+  }
+
+  function setHeight(height) {
+    var ifr = document.querySelector("iframe");
+    ifr.style.height = height + "px";
+    flushLayout();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+
+  var tests = [
+    () => { expectedValue = true; setHeight(50); },
+    () => { expectedValue = false; setHeight(200); },
+    () => {
+      var ifr = document.querySelector("iframe");
+      ifr.style.display = "none";
+      flushLayout();
+      ifr.style.display = "";
+      expectedValue = true;
+      setHeight(50);
+    },
+    () => { expectedValue = false; setHeight(200); },
+    SimpleTest.finish.bind(SimpleTest)
+  ];
+
+  addLoadEvent(function() {
+    var mql = frames[0].matchMedia("(orientation: landscape)");
+    mql.addListener(callback);
+
+    tests.shift()();
+  });
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=771043">Mozilla Bug 771043</a>
+<!-- Important: the iframe needs to be displayed -->
+<p id="display"><iframe style="width: 100px; height: 200px"</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>