Bug 685779 - Add -moz-full-screen-ancestor pseudo class. r=bz
authorChris Pearce <chris@pearce.org.nz>
Tue, 01 Nov 2011 18:11:09 +1300
changeset 79492 b72b2c0f996f88d37996e6f53fc24c895fafca25
parent 79491 470ed3885e3c65bf6a35ddc7e4f89e16cd6462eb
child 79493 ddad75e027ab1e450db0af4aa83fa3a9bfabb122
push id3029
push usercpearce@mozilla.com
push dateTue, 01 Nov 2011 06:56:28 +0000
treeherdermozilla-inbound@ad6054dd6e08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs685779
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 685779 - Add -moz-full-screen-ancestor pseudo class. r=bz
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsGenericElement.cpp
content/events/public/nsEventStates.h
content/events/src/nsEventStateManager.cpp
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/test/Makefile.in
content/html/content/test/file_fullscreen-api-keys.html
content/html/content/test/file_fullscreen-api.html
content/html/content/test/file_fullscreen-denied.html
content/html/content/test/file_fullscreen-plugins.html
content/html/content/test/test_fullscreen-api.html
layout/style/nsCSSPseudoClassList.h
layout/style/ua.css
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -121,18 +121,18 @@ class Loader;
 
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0xb52356d4, 0xe191, 0x4cf8, \
- { 0xb8, 0x58, 0xc0, 0xf1, 0xe1, 0x98, 0x09, 0xdf } }
+{ 0xc3e40e8e, 0x8b91, 0x424c, \
+  { 0xbe, 0x9c, 0x9c, 0xc1, 0x76, 0xa7, 0xf7, 0x24 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
@@ -732,48 +732,36 @@ public:
    * Add/Remove an element to the document's id and name hashes
    */
   virtual void AddToIdTable(Element* aElement, nsIAtom* aId) = 0;
   virtual void RemoveFromIdTable(Element* aElement, nsIAtom* aId) = 0;
   virtual void AddToNameTable(Element* aElement, nsIAtom* aName) = 0;
   virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) = 0;
 
   /**
-   * Resets the current full-screen element to nsnull.
-   */
-  virtual void ResetFullScreenElement() = 0;
-
-  /**
-   * Returns the element which either is the full-screen element, or
-   * contains the full-screen element if a child of this document contains
-   * the fullscreen element.
+   * Returns the element which either requested DOM full-screen mode, or
+   * contains the element which requested DOM full-screen mode if the
+   * requestee is in a subdocument. Note this element must be *in*
+   * this document.
    */
   virtual Element* GetFullScreenElement() = 0;
 
   /**
    * Requests that the document make aElement the full-screen element,
    * and move into full-screen mode.
    */
   virtual void RequestFullScreen(Element* aElement) = 0;
 
   /**
    * Requests that the document, and all documents in its hierarchy exit
    * from DOM full-screen mode.
    */
   virtual void CancelFullScreen() = 0;
 
   /**
-   * Updates the full-screen status on this document, setting the full-screen
-   * mode to aIsFullScreen. This doesn't affect the window's full-screen mode,
-   * this updates the document's internal state which determines whether the
-   * document reports as being in full-screen mode.
-   */
-  virtual void UpdateFullScreenStatus(bool aIsFullScreen) = 0;
-
-  /**
    * Returns true if this document is in full-screen mode.
    */
   virtual bool IsFullScreenDoc() = 0;
 
   //----------------------------------------------------------------------
 
   // Document notification API's
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -209,16 +209,17 @@
 #include "imgILoader.h"
 #include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
+nsWeakPtr nsDocument::sFullScreenDoc = nsnull;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
 
@@ -6006,17 +6007,17 @@ nsDocument::AdoptNode(nsIDOMNode *aAdopt
   NS_ENSURE_ARG(aAdoptedNode);
 
   *aResult = nsnull;
 
   nsresult rv = nsContentUtils::CheckSameOrigin(this, aAdoptedNode);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode);
-  
+
   // Scope firing mutation events so that we don't carry any state that
   // might be stale
   {
     nsINode* parent = adoptedNode->GetNodeParent();
     if (parent) {
       nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent,
                                            adoptedNode->OwnerDoc());
     }
@@ -8353,227 +8354,263 @@ nsIDocument::SizeOf() const
   for (nsIContent* node = GetFirstChild(); node;
        node = node->GetNextNode(this)) {
     size += node->SizeOf();
   }
 
   return size;
 }
 
-// Returns the root document in a document hierarchy.
-static nsIDocument*
-GetRootDocument(nsIDocument* aDoc)
-{
-  if (!aDoc) {
-    return nsnull;
-  }
-  nsCOMPtr<nsIPresShell> shell = aDoc->GetShell();
-  if (!shell) {
-    return nsnull;
-  }
-  nsPresContext* ctx = shell->GetPresContext();
-  if (!ctx) {
-    return nsnull;
-  }
-  nsRootPresContext* rpc = ctx->GetRootPresContext();
-  if (!rpc) {
-    return nsnull;
-  }
-  return rpc->Document();
-}
-
 class nsDispatchFullScreenChange : public nsRunnable
 {
 public:
-  nsDispatchFullScreenChange(nsIDocument *aDoc)
-    : mDoc(aDoc)
-  {
-    mTarget = aDoc->GetFullScreenElement();
-    if (!mTarget) {
-      mTarget = aDoc;
-    }
-  }
+  nsDispatchFullScreenChange(nsIDocument *aDoc, nsINode* aElement)
+    : mDoc(aDoc),
+      mTarget(aElement ? aElement : aDoc) {}
 
   NS_IMETHOD Run()
   {
     nsContentUtils::DispatchTrustedEvent(mDoc,
                                          mTarget,
                                          NS_LITERAL_STRING("mozfullscreenchange"),
                                          true,
                                          false);
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> mDoc;
   nsCOMPtr<nsISupports> mTarget;
 };
 
-void
-nsDocument::UpdateFullScreenStatus(bool aIsFullScreen)
-{
-  if (mIsFullScreen != aIsFullScreen) {
-    nsCOMPtr<nsIRunnable> event(new nsDispatchFullScreenChange(this));
-    NS_DispatchToCurrentThread(event);
+static void DispatchFullScreenChange(nsIDocument* aDocument, Element* aElement)
+{
+  nsCOMPtr<nsIRunnable> event(
+    new nsDispatchFullScreenChange(aDocument, aElement));
+  NS_DispatchToCurrentThread(event);
+}
+
+bool
+nsDocument::SetFullScreenState(Element* aElement, bool aIsFullScreen)
+{
+  if (mFullScreenElement) {
+    // Reset the ancestor and full-screen styles on the outgoing full-screen
+    // element in the current document.
+    nsEventStateManager::SetFullScreenState(mFullScreenElement, false);
+    mFullScreenElement = nsnull;
+  }
+  if (aElement) {
+    nsEventStateManager::SetFullScreenState(aElement, aIsFullScreen);
+  }
+  mFullScreenElement = aElement;
+
+  if (mIsFullScreen == aIsFullScreen) {
+    return false;
   }
   mIsFullScreen = aIsFullScreen;
-  if (!mIsFullScreen) {
-    // Full-screen is being turned off. Reset the full-screen element, to
-    // save us from having to traverse the document hierarchy again in
-    // MozCancelFullScreen().
-    ResetFullScreenElement();
-  }
-}
-
+  return true;
+}
+
+// Wrapper for the nsIDocument -> nsDocument cast required to call
+// nsDocument::SetFullScreenState().
 static bool
-UpdateFullScreenStatus(nsIDocument* aDocument, void* aData)
-{
-  aDocument->UpdateFullScreenStatus(*static_cast<bool*>(aData));
-  aDocument->EnumerateSubDocuments(UpdateFullScreenStatus, aData);
-  return true;
-}
-
-static void
-UpdateFullScreenStatusInDocTree(nsIDocument* aDoc, bool aIsFullScreen)
-{
-  nsIDocument* root = GetRootDocument(aDoc);
-  if (root) {
-    UpdateFullScreenStatus(root, static_cast<void*>(&aIsFullScreen));
-  }
-}
-
-void
-nsDocument::ResetFullScreenElement()
-{
-  if (mFullScreenElement) {
-    nsEventStateManager::SetFullScreenState(mFullScreenElement, false);
-  }
-  mFullScreenElement = nsnull;
-}
-
-static bool
-ResetFullScreenElement(nsIDocument* aDocument, void* aData)
-{
-  aDocument->ResetFullScreenElement();
-  aDocument->EnumerateSubDocuments(ResetFullScreenElement, aData);
-  return true;
-}
-
-static void
-ResetFullScreenElementInDocTree(nsIDocument* aDoc)
-{
-  nsIDocument* root = GetRootDocument(aDoc);
-  if (root) {
-    ResetFullScreenElement(root, nsnull);
-  }
+SetFullScreenState(nsIDocument* aDoc, Element* aElement, bool aIsFullScreen)
+{
+  return static_cast<nsDocument*>(aDoc)->
+    SetFullScreenState(aElement, aIsFullScreen);
 }
 
 NS_IMETHODIMP
 nsDocument::MozCancelFullScreen()
 {
   if (!nsContentUtils::IsRequestFullScreenAllowed()) {
     return NS_OK;
   }
   CancelFullScreen();
   return NS_OK;
 }
 
+// Runnable to set window full-screen mode. Used as a script runner
+// to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to 
+// run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event
+// (handled in chome code) which is unsafe to run if this is called in
+// nsGenericElement::UnbindFromTree().
+class nsSetWindowFullScreen : public nsRunnable {
+public:
+  nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue)
+    : mDoc(aDoc), mValue(aValue) {}
+
+  NS_IMETHOD Run()
+  {
+    if (mDoc->GetWindow()) {
+      mDoc->GetWindow()->SetFullScreen(mValue);
+    }
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIDocument> mDoc;
+  bool mValue;
+};
+
+static void
+SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
+{
+  nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
+}
+
 void
 nsDocument::CancelFullScreen()
 {
-  if (!nsContentUtils::IsFullScreenApiEnabled() ||
-      !IsFullScreenDoc() ||
-      !GetWindow()) {
+  NS_ASSERTION(!IsFullScreenDoc() || sFullScreenDoc != nsnull,
+               "Should have a full-screen doc when full-screen!");
+
+  if (!IsFullScreenDoc() || !GetWindow() || !sFullScreenDoc) {
     return;
   }
 
-  // Disable full-screen mode in all documents in this hierarchy.
-  UpdateFullScreenStatusInDocTree(this, false);
+  // Reset full-screen state in all full-screen documents.
+  nsCOMPtr<nsIDocument> doc(do_QueryReferent(sFullScreenDoc));
+  while (doc != nsnull) {
+    if (::SetFullScreenState(doc, nsnull, false)) {
+      DispatchFullScreenChange(doc, nsnull);
+    }
+    doc = doc->GetParentDocument();
+  }
+  sFullScreenDoc = nsnull;
 
   // Move the window out of full-screen mode.
-  GetWindow()->SetFullScreen(false);
+  SetWindowFullScreen(this, false);
 
   return;
 }
 
 bool
 nsDocument::IsFullScreenDoc()
 {
-  return nsContentUtils::IsFullScreenApiEnabled() && mIsFullScreen;
+  return mIsFullScreen;
+}
+
+static nsIDocument*
+GetCommonAncestor(nsIDocument* aDoc1, nsIDocument* aDoc2)
+{
+  nsIDocument* doc1 = aDoc1;
+  nsIDocument* doc2 = aDoc2;
+
+  nsAutoTArray<nsIDocument*, 30> parents1, parents2;
+  do {
+    parents1.AppendElement(doc1);
+    doc1 = doc1->GetParentDocument();
+  } while (doc1);
+  do {
+    parents2.AppendElement(doc2);
+    doc2 = doc2->GetParentDocument();
+  } while (doc2);
+
+  PRUint32 pos1 = parents1.Length();
+  PRUint32 pos2 = parents2.Length();
+  nsIDocument* parent = nsnull;
+  PRUint32 len;
+  for (len = NS_MIN(pos1, pos2); len > 0; --len) {
+    nsIDocument* child1 = parents1.ElementAt(--pos1);
+    nsIDocument* child2 = parents2.ElementAt(--pos2);
+    if (child1 != child2) {
+      break;
+    }
+    parent = child1;
+  }
+  return parent;
 }
 
 void
 nsDocument::RequestFullScreen(Element* aElement)
 {
   if (!aElement || !nsContentUtils::IsFullScreenApiEnabled() || !GetWindow()) {
     return;
   }
 
-  // Reset the full-screen elements of every document in this document
-  // hierarchy.
-  ResetFullScreenElementInDocTree(this);
-  
-  if (aElement->IsInDoc()) {
-    // Propagate up the document hierarchy, setting the full-screen element as
-    // the element's container in ancestor documents. Note we don't propagate
-    // down the document hierarchy, the full-screen element (or its container)
-    // is not visible there.
-    mFullScreenElement = aElement;
-    // Set the full-screen state on the element, so the css-pseudo class
-    // applies to the element.
-    nsEventStateManager::SetFullScreenState(mFullScreenElement, true);
-    nsIDocument* child = this;
-    nsIDocument* parent;
-    while (parent = child->GetParentDocument()) {
-      Element* element = parent->FindContentForSubDocument(child);
-      // Containing frames also need the css-pseudo class applied.
-      nsEventStateManager::SetFullScreenState(element, true);
-      static_cast<nsDocument*>(parent)->mFullScreenElement = element;
-      child = parent;
-    }
-  }
-
-  // Set all documents in hierarchy to full-screen mode.
-  UpdateFullScreenStatusInDocTree(this, true);
+  // Turn off full-screen state in all documents which were previously
+  // full-screen but which shouldn't be after this request is granted.
+  // Note commonAncestor will be null when in a separate browser window
+  // to the requesting document.
+  nsIDocument* commonAncestor = nsnull;
+  nsCOMPtr<nsIDocument> fullScreenDoc(do_QueryReferent(sFullScreenDoc));
+  if (fullScreenDoc) {
+    commonAncestor = GetCommonAncestor(fullScreenDoc, this);
+  }
+  nsIDocument* doc = fullScreenDoc;
+  while (doc != commonAncestor) {
+    if (::SetFullScreenState(doc, nsnull, false)) {
+      DispatchFullScreenChange(doc, nsnull);
+    }
+    doc = doc->GetParentDocument();
+  }
+  if (!commonAncestor && fullScreenDoc) {
+    // Other doc is in another browser window. Move it out of full-screen.
+    // Note that nsGlobalWindow::SetFullScreen() proxies to the root window
+    // in its hierarchy, and does not operate on the a per-nsIDOMWindow basis.
+    SetWindowFullScreen(fullScreenDoc, false);
+  }
+
+  // Set the full-screen element. This sets the full-screen style on the
+  // element, and the full-screen-ancestor styles on ancestors of the element
+  // in this document.
+  if (SetFullScreenState(aElement, true)) {
+    DispatchFullScreenChange(this, aElement);
+  }
+
+  // Propagate up the document hierarchy, setting the full-screen element as
+  // the element's container in ancestor documents. This also sets the
+  // appropriate css styles as well. Note we don't propagate down the
+  // document hierarchy, the full-screen element (or its container) is not
+  // visible there.  
+  nsIDocument* child = this;
+  nsIDocument* parent;
+  while ((parent = child->GetParentDocument())) {
+    Element* element = parent->FindContentForSubDocument(child)->AsElement();
+    if (::SetFullScreenState(parent, element, true)) {
+      DispatchFullScreenChange(parent, element);
+    }
+    child = parent;
+  }
 
   // Make the window full-screen. Note we must make the state changes above
   // before making the window full-screen, as then the document reports as
-  // being in full-screen mode when the Chrome "fullscreen" event fires,
-  // enabling browser.js to distinguish between browser and dom full-screen
+  // being in full-screen mode when the chrome "fullscreen" event fires,
+  // enabling chrome to distinguish between browser and dom full-screen
   // modes.
-  GetWindow()->SetFullScreen(true);
+  SetWindowFullScreen(this, true);
+
+  // Remember this is the requesting full-screen document.
+  sFullScreenDoc = do_GetWeakReference(static_cast<nsIDocument*>(this));
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozFullScreenElement(nsIDOMHTMLElement **aFullScreenElement)
 {
   NS_ENSURE_ARG_POINTER(aFullScreenElement);
-  if (!nsContentUtils::IsFullScreenApiEnabled() || !IsFullScreenDoc()) {
-    *aFullScreenElement = nsnull;
-    return NS_OK;
-  }
-  nsCOMPtr<nsIDOMHTMLElement> e(do_QueryInterface(GetFullScreenElement()));
-  NS_IF_ADDREF(*aFullScreenElement = e);
+  *aFullScreenElement = nsnull;
+  if (IsFullScreenDoc()) {
+    // Must have a full-screen element while in full-screen mode.
+    NS_ENSURE_STATE(GetFullScreenElement());
+    CallQueryInterface(GetFullScreenElement(), aFullScreenElement);
+  }
   return NS_OK;
 }
 
 Element*
 nsDocument::GetFullScreenElement()
 {
-  if (!nsContentUtils::IsFullScreenApiEnabled() ||
-      (mFullScreenElement && !mFullScreenElement->IsInDoc())) {
-    return nsnull;
-  }
   return mFullScreenElement;
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozFullScreen(bool *aFullScreen)
 {
   NS_ENSURE_ARG_POINTER(aFullScreen);
-  *aFullScreen = nsContentUtils::IsFullScreenApiEnabled() && IsFullScreenDoc();
+  *aFullScreen = IsFullScreenDoc();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
 {
   NS_ENSURE_ARG_POINTER(aFullScreen);
   *aFullScreen = false;
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -942,23 +942,25 @@ public:
 
   virtual nsresult GetStateObject(nsIVariant** aResult);
 
   virtual nsDOMNavigationTiming* GetNavigationTiming() const;
   virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming);
 
   virtual Element* FindImageMap(const nsAString& aNormalizedMapName);
 
-  virtual void ResetFullScreenElement();
   virtual Element* GetFullScreenElement();
   virtual void RequestFullScreen(Element* aElement);
   virtual void CancelFullScreen();
-  virtual void UpdateFullScreenStatus(bool aIsFullScreen);
   virtual bool IsFullScreenDoc();
 
+  // Returns true if making this change results in a change in the full-screen
+  // state of this document.
+  bool SetFullScreenState(Element* aElement, bool aIsFullScreen);
+ 
   // This method may fire a DOM event; if it does so it will happen
   // synchronously.
   void UpdateVisibilityState();
   // Posts an event to call UpdateVisibilityState
   virtual void PostVisibilityUpdateEvent();
 
 protected:
   friend class nsNodeUtils;
@@ -1068,16 +1070,20 @@ protected:
   // the script global object of the original document.
   nsWeakPtr mScriptObject;
 
   // Weak reference to the scope object (aka the script global object)
   // that, unlike mScriptGlobalObject, is never unset once set. This
   // is a weak reference to avoid leaks due to circular references.
   nsWeakPtr mScopeObject;
 
+  // The document which requested (and was granted) full-screen. All ancestors
+  // of this document will also be full-screen.
+  static nsWeakPtr sFullScreenDoc;
+
   nsRefPtr<nsEventListenerManager> mListenerManager;
   nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
   nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   nsRefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
    *    entries as needed).
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -3051,27 +3051,40 @@ nsGenericElement::BindToTree(nsIDocument
   NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document");
   NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
   NS_POSTCONDITION(aBindingParent == GetBindingParent(),
                    "Bound to wrong binding parent");
 
   return NS_OK;
 }
 
+static bool
+IsFullScreenAncestor(Element* aElement)
+{
+  nsEventStates state = aElement->State();
+  return state.HasAtLeastOneOfStates(NS_EVENT_STATE_FULL_SCREEN_ANCESTOR |
+                                     NS_EVENT_STATE_FULL_SCREEN);
+}
+
 void
 nsGenericElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   NS_PRECONDITION(aDeep || (!GetCurrentDoc() && !GetBindingParent()),
                   "Shallow unbind won't clear document and binding parent on "
                   "kids!");
   // Make sure to unbind this node before doing the kids
   nsIDocument *document =
     HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetCurrentDoc();
 
   if (aNullParent) {
+    if (IsFullScreenAncestor(this)) {
+      // The element being removed is an ancestor of the full-screen element,
+      // exit full-screen state.
+      OwnerDoc()->CancelFullScreen();
+    }
     if (GetParent()) {
       NS_RELEASE(mParent);
     } else {
       mParent = nsnull;
     }
     SetParentIsContent(false);
   }
   ClearInDocument();
--- a/content/events/public/nsEventStates.h
+++ b/content/events/public/nsEventStates.h
@@ -259,24 +259,26 @@ private:
 #define NS_EVENT_STATE_MOZ_SUBMITINVALID NS_DEFINE_EVENT_STATE_MACRO(31)
 // UI friendly version of :invalid pseudo-class.
 #define NS_EVENT_STATE_MOZ_UI_INVALID NS_DEFINE_EVENT_STATE_MACRO(32)
 // UI friendly version of :valid pseudo-class.
 #define NS_EVENT_STATE_MOZ_UI_VALID NS_DEFINE_EVENT_STATE_MACRO(33)
 // Content is the full screen element, or a frame containing the
 // current full-screen element.
 #define NS_EVENT_STATE_FULL_SCREEN   NS_DEFINE_EVENT_STATE_MACRO(34)
+// Content is an ancestor of the DOM full-screen element.
+#define NS_EVENT_STATE_FULL_SCREEN_ANCESTOR   NS_DEFINE_EVENT_STATE_MACRO(35)
 // Handler for click to play plugin
-#define NS_EVENT_STATE_TYPE_CLICK_TO_PLAY NS_DEFINE_EVENT_STATE_MACRO(35)
+#define NS_EVENT_STATE_TYPE_CLICK_TO_PLAY NS_DEFINE_EVENT_STATE_MACRO(36)
 
 /**
  * NOTE: do not go over 63 without updating nsEventStates::InternalType!
  */
 
 #define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS |     \
                             NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER |   \
                             NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
-                            NS_EVENT_STATE_FULL_SCREEN)
+                            NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FULL_SCREEN_ANCESTOR)
 
 #define INTRINSIC_STATES (~ESM_MANAGED_STATES)
 
 #endif // nsEventStates_h__
 
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -4440,22 +4440,32 @@ static nsIContent* FindCommonAncestor(ns
         anc2 = anc2->GetParent();
       }
       return anc1;
     }
   }
   return nsnull;
 }
 
+static Element*
+GetParentElement(Element* aElement)
+{
+  nsIContent* p = aElement->GetParent();
+  return (p && p->IsElement()) ? p->AsElement() : nsnull;
+}
+
 /* static */
 void
-nsEventStateManager::SetFullScreenState(Element* aElement,
-                                        bool aIsFullScreen)
+nsEventStateManager::SetFullScreenState(Element* aElement, bool aIsFullScreen)
 {
   DoStateChange(aElement, NS_EVENT_STATE_FULL_SCREEN, aIsFullScreen);
+  Element* ancestor = aElement;
+  while ((ancestor = GetParentElement(ancestor))) {
+    DoStateChange(ancestor, NS_EVENT_STATE_FULL_SCREEN_ANCESTOR, aIsFullScreen);
+  }
 }
 
 /* static */
 inline void
 nsEventStateManager::DoStateChange(Element* aElement, nsEventStates aState,
                                    bool aAddState)
 {
   if (aAddState) {
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -3398,36 +3398,33 @@ nsGenericHTMLElement::Focus()
 
 nsresult nsGenericHTMLElement::MozRequestFullScreen()
 {
   // Only grant full-screen requests if this is called from inside a trusted
   // event handler (i.e. inside an event handler for a user initiated event).
   // This stops the full-screen from being abused similar to the popups of old,
   // and it also makes it harder for bad guys' script to go full-screen and
   // spoof the browser chrome/window and phish logins etc.
-  if (!nsContentUtils::IsRequestFullScreenAllowed()) {
+  if (!nsContentUtils::IsRequestFullScreenAllowed() ||
+      !IsInDoc()) {
     return NS_OK;
   }
 
   nsIDocument* doc = OwnerDoc();
   nsCOMPtr<nsIDOMDocument> domDocument(do_QueryInterface(doc));
   NS_ENSURE_STATE(domDocument);
   bool fullScreenEnabled;
   domDocument->GetMozFullScreenEnabled(&fullScreenEnabled);
   if (!fullScreenEnabled) {
     return NS_OK;
   }
 
   doc->RequestFullScreen(this);
 #ifdef DEBUG
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(doc->GetWindow());
-  NS_ENSURE_STATE(window);
   bool fullscreen;
-  window->GetFullScreen(&fullscreen);
-  NS_ASSERTION(fullscreen, "Windows should report fullscreen");
   domDocument->GetMozFullScreen(&fullscreen);
   NS_ASSERTION(fullscreen, "Document should report fullscreen");
   NS_ASSERTION(doc->IsFullScreenDoc(), "Should be in full screen state!");
 #endif
   return NS_OK;
 }
 
 nsresult nsGenericHTMLElement::Click()
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -277,16 +277,17 @@ include $(topsrcdir)/config/rules.mk
 		test_checked.html \
 		test_bug677658.html \
 		test_bug677463.html \
 		test_bug682886.html \
 		file_fullscreen-api.html \
 		file_fullscreen-api-keys.html \
 		test_fullscreen-api.html \
 		file_fullscreen-plugins.html \
+		file_fullscreen-denied.html \
 		test_li_attributes_reflection.html \
 		test_ol_attributes_reflection.html \
 		test_bug651956.html \
 		test_bug694503.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
--- a/content/html/content/test/file_fullscreen-api-keys.html
+++ b/content/html/content/test/file_fullscreen-api-keys.html
@@ -4,16 +4,21 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=545812
 
 Test that restricted key pressed drop documents out of DOM full-screen mode.
 
 -->
 <head>
   <title>Test for Bug 545812</title>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <style>
+  body {
+    background-color: black;
+  }
+  </style>
 </head>
 <body onload="document.body.mozRequestFullScreen();">
 
 <script type="application/javascript">
 
 /** Test for Bug 545812 **/
 
 // List of key codes, and whether they're restricted in full-screen mode.
@@ -181,17 +186,18 @@ function keyHandler(event) {
 function checkKeyEffect() {
   is(document.mozFullScreen, gKeyAllowed,
      (gKeyAllowed ? ("Should remain in full-screen mode for allowed key press " + gKeyName)
                   : ("Should drop out of full-screen mode for restricted key press " + gKeyName)));
 
   if (gKeyTestIndex < keyList.length) {
     setTimeout(testNextKey, 0);
   } else {
-    opener.keysTestFinished();
+    document.mozCancelFullScreen();
+    opener.nextTest();
   }
 }
 
 function testTrustedKeyEvents() {
   document.body.focus();
   gKeyReceived = false;
   synthesizeKey(gKeyName, {});
   setTimeout(checkKeyEffect, 0);
--- a/content/html/content/test/file_fullscreen-api.html
+++ b/content/html/content/test/file_fullscreen-api.html
@@ -1,137 +1,169 @@
- <!DOCTYPE HTML>
+<!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=545812
 
 Test DOM full-screen API.
 
 -->
 <head>
   <title>Test for Bug 545812</title>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <style>
-  body:-moz-full-screen, div:-moz-full-screen {
-    background-color: red;
+  body {
+    background-color: black;
   }
   </style>
 </head>
-<body onload="document.body.mozRequestFullScreen();">
+<body onload="fullScreenElement().mozRequestFullScreen();">
 
 <script type="application/javascript">
 
 /** Test for Bug 545812 **/
 
 function ok(condition, msg) {
   opener.ok(condition, msg);
 }
 
 function is(a, b, msg) {
   opener.is(a, b, msg);
 }
 
 /*
 <html>
   <body onload='document.body.mozRequestFullScreen();'>
+  <iframe id='inner-frame'></iframe>
   </body>
 </html>
 */
-var iframeContents = "data:text/html;charset=utf-8,<html>%0D%0A  <body onload%3D'document.body.mozRequestFullScreen()%3B'>%0D%0A  <%2Fbody>%0D%0A<%2Fhtml>";
+var iframeContents = "data:text/html;charset=utf-8,<html><body onload%3D'document.body.mozRequestFullScreen()%3B'><iframe id%3D'inner-frame'><%2Fiframe><%2Fbody><%2Fhtml>";
 
 var iframe = null;
 var outOfDocElement = null;
 var inDocElement = null;
+var container = null;
 var button = null;
 
 var fullScreenChangeCount = 0;
 
 function sendMouseClick(element) {
   synthesizeMouseAtCenter(element, {});
 }
 
 function setRequireTrustedContext(value) {
   opener.SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", value);
 }
 
+function fullScreenElement() {
+  return document.getElementById('full-screen-element');
+}
+
 function fullScreenChange(event) {
   switch (fullScreenChangeCount) {
     case 0: {
       ok(document.mozFullScreen, "Should be in full-screen mode (first time)");
-      is(event.target, document.body, "Event target should be full-screen element");
-      is(document.mozFullScreenElement, document.body,
-         "Full-screen element should be body element.");
-      document.mozCancelFullScreen();
+      is(event.target, fullScreenElement(), "Event target should be full-screen element");
+      is(document.mozFullScreenElement, fullScreenElement(),
+         "Full-screen element should be div element.");
+      ok(document.mozFullScreenElement.mozMatchesSelector(":-moz-full-screen"),
+         "FSE should match :-moz-full-screen");
+      var fse = fullScreenElement();
+      fse.parentNode.removeChild(fse);
+      is(document.mozFullScreenElement, null,
+         "Full-screen element should be null after removing.");
+      ok(!document.mozFullScreen, "Should have left full-screen mode when we remove full-screen element");
+      document.body.appendChild(fse);
+      ok(!document.mozFullScreen, "Should not return to full-screen mode when re-adding former FSE");
+      is(document.mozFullScreenElement, null,
+         "Full-screen element should still be null after re-adding former FSE.");
       break;
     }
     case 1: {
       ok(!document.mozFullScreen, "Should have left full-screen mode (first time)");
-      is(event.target, document.body, "Event target should be full-screen element");
+      is(event.target, document, "Event target should be document when we exit via removing from doc");
       is(document.mozFullScreenElement, null, "Full-screen element should be null.");
       iframe = document.createElement("iframe");
       iframe.mozAllowFullScreen = true;
       document.body.appendChild(iframe);
       iframe.src = iframeContents;
       break;
     }
     case 2: {
       ok(document.mozFullScreen, "Should be back in full-screen mode (second time)");
       is(event.target, iframe,
          "Event target should be full-screen element container");
       is(document.mozFullScreenElement, iframe,
         "Full-screen element should be iframe element.");
-      document.mozCancelFullScreen();
+      var fse = fullScreenElement();
+      fse.mozRequestFullScreen();
+      ok(document.mozFullScreen, "Should still be full-screen mode after re-requesting.");
+      is(document.mozFullScreenElement, fse, "Full-screen element should have switched to requestee.");
+      var _innerFrame = iframe.contentDocument.getElementById("inner-frame");
+      _innerFrame.contentDocument.body.appendChild(fse);
+      ok(!document.mozFullScreen, "Should exit full-screen after transplanting FSE");
+      is(document.mozFullScreenElement, null, "Full-screen element transplanted, should be null.");
+      is(iframe.contentDocument.mozFullScreenElement, null, "Full-screen element in outer frame should be null.");
+      is(_innerFrame.contentDocument.mozFullScreenElement, null, "Full-screen element in inner frame should be null.");
+      ok(!iframe.contentDocument.mozFullScreen, "Outer frame should not acquire full-screen status.");
+      ok(!_innerFrame.contentDocument.mozFullScreen, "Inner frame should not acquire full-screen status.");
+      
+      document.body.appendChild(fse);
       break;
     }
     case 3: {
       ok(!document.mozFullScreen, "Should be back in non-full-screen mode (second time)");
-      is(event.target, iframe,
+      is(event.target, document,
          "Event target should be full-screen element container");
       is(document.mozFullScreenElement, null, "Full-screen element should be null.");
       document.body.removeChild(iframe);
       iframe = null;
       outOfDocElement = document.createElement("div");
       outOfDocElement.mozRequestFullScreen();
+      ok(!document.mozFullScreen, "Requests for full-screen from not-in-doc elements should fail.");
+
+      container = document.createElement("div");
+      inDocElement = document.createElement("div");
+      container.appendChild(inDocElement);
+      fullScreenElement().appendChild(container);
+      
+      inDocElement.mozRequestFullScreen();
+      ok(document.mozFullScreen, "Should grant request to return to full-screen mode (third time)");
       break;
     }
     case 4: {
-      ok(document.mozFullScreen, "Should be back in full-screen mode (third time)");
-      is(event.target, document, "Event target should be document");
-      is(document.mozFullScreenElement, null,
-        "Should not have a full-screen element when element not in document requests full-screen.");
-       
-      // Set another element to be the full-screen element. It should immediately
-      // become the current full-screen element.
-      inDocElement = document.createElement("div");
-      document.body.appendChild(inDocElement);
-      inDocElement.mozRequestFullScreen();
-       
-      ok(document.mozFullScreen, "Should remain in full-screen mode (third and a half time)");
+      ok(document.mozFullScreen, "Should still be in full-screen mode (third time)");
+      is(event.target, inDocElement, "Event target should be inDocElement");
       is(document.mozFullScreenElement, inDocElement,
-        "Full-screen element should be in doc again.");
-
-      // Remove full-screen element from document before exiting full screen.
-      document.body.removeChild(inDocElement);
-      ok(document.mozFullScreen,
-         "Should remain in full-screen mode after removing full-screen element from document");
+        "FSE should be inDocElement.");       
+      
+      var n = container;
+      do {
+        ok(n.mozMatchesSelector(":-moz-full-screen-ancestor"), "Ancestor " + n + " should match :-moz-full-screen-ancestor")
+        n = n.parentNode;
+      } while (n && n.mozMatchesSelector);
+        
+      // Remove full-screen ancestor element from document, verify it stops being reported as current FSE.
+      container.parentNode.removeChild(container);
+      ok(!document.mozFullScreen,
+         "Should exit full-screen mode after removing full-screen element ancestor from document");
       is(document.mozFullScreenElement, null,
         "Should not have a full-screen element again.");
-
-      document.mozCancelFullScreen();
       break;
     }
     case 5: {
       ok(!document.mozFullScreen, "Should be back in non-full-screen mode (third time)");
       setRequireTrustedContext(true);
-      document.body.mozRequestFullScreen();
+      fullScreenElement().mozRequestFullScreen();
       ok(!document.mozFullScreen, "Should still be in normal mode, because calling context isn't trusted.");
 
       button = document.createElement("button");
-      button.onclick = function(){document.body.mozRequestFullScreen();}
-      document.body.appendChild(button);
+      button.onclick = function(){fullScreenElement().mozRequestFullScreen();}
+      fullScreenElement().appendChild(button);
       sendMouseClick(button);
       break;
     }
     case 6: {
       ok(document.mozFullScreen, "Moved to full-screen after mouse click");
       document.mozCancelFullScreen();
       ok(document.mozFullScreen, "Should still be in full-screen mode, because calling context isn't trusted.");
       setRequireTrustedContext(false);
@@ -139,39 +171,40 @@ function fullScreenChange(event) {
       ok(!document.mozFullScreen, "Should have left full-screen mode.");
       break;
     }
     case 7: {
       ok(!document.mozFullScreen, "Should have left full-screen mode (last time).");
 
       SpecialPowers.setBoolPref("full-screen-api.enabled", false);
       is(document.mozFullScreenEnabled, false, "document.mozFullScreenEnabled should be false if full-screen-api.enabled is false");
-      document.body.mozRequestFullScreen();
+      fullScreenElement().mozRequestFullScreen();
       ok(!document.mozFullScreen, "Should still be in normal mode, because pref is not enabled.");
 
       SpecialPowers.setBoolPref("full-screen-api.enabled", true);
       is(document.mozFullScreenEnabled, true, "document.mozFullScreenEnabled should be true if full-screen-api.enabled is true");
 
       iframe = document.createElement("iframe");
-      document.body.appendChild(iframe);
+      fullScreenElement().appendChild(iframe);
       iframe.src = iframeContents;
       ok(!document.mozFullScreen, "Should still be in normal mode, because iframe did not have mozallowfullscreen attribute.");
-      document.body.removeChild(iframe);
+      fullScreenElement().removeChild(iframe);
       iframe = null;
 
       // Set timeout for calling finish(), so that any pending "mozfullscreenchange" events
       // would have a chance to fire.
-      setTimeout(function(){opener.apiTestFinished();}, 0);
+      setTimeout(function(){opener.nextTest();}, 0);
       break;
     }
     default: {
       ok(false, "Should not receive any more fullscreenchange events!");
     }
   }
   fullScreenChangeCount++;
 }
 
 document.addEventListener("mozfullscreenchange", fullScreenChange, false);
 
 </script>
 </pre>
+<div id="full-screen-element"></div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_fullscreen-denied.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=545812
+
+Test DOM full-screen API.
+
+-->
+<head>
+  <title>Test for Bug 545812</title>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <style>
+  body {
+    background-color: black;
+  }
+  </style>
+</head>
+<body onload="run();">
+
+<script type="application/javascript">
+
+/** Test for Bug 545812 **/
+
+function ok(condition, msg) {
+  opener.ok(condition, msg);
+}
+
+function is(a, b, msg) {
+  opener.is(a, b, msg);
+}
+
+/*
+<html>
+  <body onload='document.body.mozRequestFullScreen();'>
+  </body>
+</html>
+*/
+var requestFullScreenContents = "data:text/html;charset=utf-8,<html>%0D%0A  <body onload%3D'document.body.mozRequestFullScreen()%3B'>%0D%0A  <%2Fbody>%0D%0A<%2Fhtml>";
+
+
+var gotFullScreenChange = false;
+
+function run() {
+  document.addEventListener("mozfullscreenchange",
+    function() {
+      ok(false, "Should never receive a mozfullscreenchange event in the main window.");
+      gotFullScreenChange = true;
+    },
+    false);
+
+  // Request full-screen from a non trusted context (this script isn't a user
+  // generated event!).
+  SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", true);
+  document.body.mozRequestFullScreen();
+  ok(!document.mozFullScreen, "Should not grant request in non-truested context");
+
+  // Test requesting full-screen mode in a long-running user-generated event handler.
+  // The request in the key handler should not be granted.
+  window.addEventListener("keypress", keyHandler, false);
+  synthesizeKey("VK_A", {});
+}
+
+function keyHandler(event) {
+  window.removeEventListener("keypress", keyHandler, false);
+  
+  // Busy loop until 2s has passed. We should then be past the 1 second threshold, and so
+  // our request for full-screen mode should be rejected.
+  var end = (new Date()).getTime() + 2000;
+  while ((new Date()).getTime() < end) {
+    ; // Wait...
+  }
+  document.body.mozRequestFullScreen();
+  ok(!document.mozFullScreen, "Should not grant request in long-running event handler.");
+  
+  // Disable the requirement for trusted contexts only, so the tests are easier
+  // to write.
+  SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);  
+  
+  // Create an iframe without a mozallowfullscreen sttribute, whose contents requests
+  // full-screen. The request should be denied.
+  var iframe = document.createElement("iframe");
+  iframe.src = requestFullScreenContents;
+  document.body.appendChild(iframe);
+ 
+  setTimeout(
+    function() {
+      ok(!gotFullScreenChange, "Should not ever grant a fullscreen request in this doc.");
+      opener.nextTest();
+    }, 0);
+}
+
+</script>
+</pre>
+<div id="full-screen-element"></div>
+</body>
+</html>
--- a/content/html/content/test/file_fullscreen-plugins.html
+++ b/content/html/content/test/file_fullscreen-plugins.html
@@ -114,17 +114,17 @@ function macFullScreenChange(event) {
           ok(document.mozFullScreen, "Adding windowed plugin to document should not cause full-screen to exit on MacOS.");
           document.mozCancelFullScreen();
         }, 0);
           
       break;
     }
     case 1: {
       ok(!document.mozFullScreen, "Should have left full-screen mode after calling document.mozCancelFullScreen().");
-      opener.pluginTestFinished();
+      opener.nextTest();
       break;
     }
     default: {
       ok(false, "Should not receive any more fullscreenchange events!");
     }
   }
   fullScreenChangeCount++;
 }
@@ -159,17 +159,17 @@ function fullScreenChange(event) {
     }
     case 4: {
       ok(document.mozFullScreen, "Should have (again) reentered full-screen mode");
       document.body.appendChild(windowedPlugin);
       break;
     }
     case 5: {
       ok(!document.mozFullScreen, "Should have left full-screen mode after adding windowed plugin created before going full-screen to document");
-      opener.pluginTestFinished();
+      opener.nextTest();
       break;
     }
     default: {
       ok(false, "Should not receive any more fullscreenchange events!");
     }
   }
   fullScreenChangeCount++;
 }
--- a/content/html/content/test/test_fullscreen-api.html
+++ b/content/html/content/test/test_fullscreen-api.html
@@ -1,106 +1,68 @@
  <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test for Bug 545812</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <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" src="/tests/SimpleTest/EventUtils.js"></script>
+  <style>
+  body {
+    background-color: black;
+  }
+  </style>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=545812">Mozilla Bug 545812</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test for Bug 545812 **/
-var testWindow = null;
+/** Tests for Bug 545812 **/
 
-/*
-<html>
-  <body onload='document.body.mozRequestFullScreen();'>
-  </body>
-</html>
-*/
-var requestFullScreenContents = "data:text/html;charset=utf-8,<html>%0D%0A  <body onload%3D'document.body.mozRequestFullScreen()%3B'>%0D%0A  <%2Fbody>%0D%0A<%2Fhtml>";
+// Ensure the full-screen api is enabled, and will be disabled on test exit.
+var prevEnabled = SpecialPowers.getBoolPref("full-screen-api.enabled");
+SpecialPowers.setBoolPref("full-screen-api.enabled", true);
 
-var prevTrusted = false;
-var prevEnabled = false;
+// Disable the requirement for trusted contexts only, so the tests are easier
+// to write.
+var prevTrusted = SpecialPowers.getBoolPref("full-screen-api.allow-trusted-requests-only");
+SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);
 
-function run() {
-  document.addEventListener("mozfullscreenchange",
-    function(){ok(false, "Should never receive a mozfullscreenchange event in the main window.");},
-    false);
+// Run the tests which go full-screen in new windows, as mochitests normally
+// run in an iframe, which by default will not have the mozallowfullscreen
+// attribute set, so full-screen won't work.
+var gTestWindows = [
+  "file_fullscreen-denied.html",
+  "file_fullscreen-api.html",
+  "file_fullscreen-api-keys.html",
+  "file_fullscreen-plugins.html"
+];
+
+var testWindow = null;
+var gTestIndex = 0;
 
-  // Ensure the full-screen api is enabled, and will be disabled on test exit.
-  prevEnabled = SpecialPowers.getBoolPref("full-screen-api.enabled");
-  SpecialPowers.setBoolPref("full-screen-api.enabled", true);
-
-  // Test requesting full-screen mode in a long-running user-generated event handler.
-  // The request in the key handler should not be granted.
-  window.addEventListener("keypress", keyHandler, false);
-  synthesizeKey("VK_A", {});
+function nextTest() {
+  if (testWindow) {
+    testWindow.close();
+  }
+  if (gTestIndex < gTestWindows.length) {
+    testWindow = window.open(gTestWindows[gTestIndex], "", "width=500,height=500");
+    gTestIndex++;
+  } else {
+    SpecialPowers.setBoolPref("full-screen-api.enabled", prevEnabled);
+    SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", prevTrusted);	
+    SimpleTest.finish();
+  }
 }
 
-function keyHandler(event) {
-  window.removeEventListener("keypress", keyHandler, false);
-  
-  // Busy loop until 2s has passed. We should then be past the 1 second threshold, and so
-  // our request for full-screen mode should be rejected.
-  var end = (new Date()).getTime() + 2000;
-  while ((new Date()).getTime() < end) {
-    ; // Wait...
-  }
-  document.body.mozRequestFullScreen();
-
-  prevTrusted = SpecialPowers.getBoolPref("full-screen-api.allow-trusted-requests-only");
-
-  // Request full-screen from a non trusted context (this script isn't a user
-  // generated event!). We should not receive a "mozfullscreenchange" event.
-  SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", true);
-  document.body.mozRequestFullScreen();
-
-  // Disable the requirement for trusted contexts only, so the tests are easier
-  // to write.
-  SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);
-
-  // Load an iframe whose contents requests full-screen. This request should
-  // fail, and we should never receive a "mozfullscreenchange" event, because the
-  // iframe doesn't have a mozallowfullscreen attribute.
-  var iframe = document.createElement("iframe");
-  iframe.src = requestFullScreenContents;
-  document.body.appendChild(iframe);
- 
-  // Run the tests which go full-screen in a new window, as mochitests normally
-  // run in an iframe, which by default will not have the mozallowfullscreen
-  // attribute set, so full-screen won't work.
-  testWindow = window.open("file_fullscreen-api.html", "", "width=500,height=500");
-}
-
-function apiTestFinished() {
-  testWindow.close();
-  testWindow = window.open("file_fullscreen-api-keys.html", "", "width=500,height=500");
-}
-
-function keysTestFinished() {
-  testWindow.close();
-  testWindow = window.open("file_fullscreen-plugins.html", "", "width=500,height=500");
-}
-
-function pluginTestFinished() {
-  testWindow.close();
-  SpecialPowers.setBoolPref("full-screen-api.enabled", prevEnabled);
-  SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", prevTrusted);	
-  SimpleTest.finish();
-}
-
-addLoadEvent(run);
+addLoadEvent(nextTest);
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
--- a/layout/style/nsCSSPseudoClassList.h
+++ b/layout/style/nsCSSPseudoClassList.h
@@ -139,16 +139,20 @@ CSS_STATE_PSEUDO_CLASS(mozDragOver, ":-m
 CSS_STATE_PSEUDO_CLASS(target, ":target", NS_EVENT_STATE_URLTARGET)
 CSS_STATE_PSEUDO_CLASS(indeterminate, ":indeterminate",
                        NS_EVENT_STATE_INDETERMINATE)
 
 // Matches the element which is being displayed full-screen, and
 // any containing frames.
 CSS_STATE_PSEUDO_CLASS(mozFullScreen, ":-moz-full-screen", NS_EVENT_STATE_FULL_SCREEN)
 
+// Matches any element which is an ancestor of the DOM full-screen element,
+// or an ancestor of a containing frame of the full-screen element.
+CSS_STATE_PSEUDO_CLASS(mozFullScreenAncestor, ":-moz-full-screen-ancestor", NS_EVENT_STATE_FULL_SCREEN_ANCESTOR)
+
 // Matches if the element is focused and should show a focus ring
 CSS_STATE_PSEUDO_CLASS(mozFocusRing, ":-moz-focusring", NS_EVENT_STATE_FOCUSRING)
 
 // Image, object, etc state pseudo-classes
 CSS_STATE_PSEUDO_CLASS(mozBroken, ":-moz-broken", NS_EVENT_STATE_BROKEN)
 CSS_STATE_PSEUDO_CLASS(mozUserDisabled, ":-moz-user-disabled",
                        NS_EVENT_STATE_USERDISABLED)
 CSS_STATE_PSEUDO_CLASS(mozSuppressed, ":-moz-suppressed",
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -236,27 +236,50 @@
 
   * {
     cursor: default !important;
   }
 
 }
 
 *|*:-moz-full-screen {
-  position:fixed;
-  top:0;
-  left:0;
-  right:0;
-  bottom:0;
-  z-index:2147483647;
-  background:black;
+  position: fixed !important;
+  top: 0 !important;
+  left: 0 !important;
+  right: 0 !important;
+  bottom: 0 !important;
+  z-index: 2147483647 !important;
+  background: black;
   width: 100% !important;
   height: 100% !important;
 }
 
+/* If there is a full-screen element that is not the root then
+   we should hide the viewport scrollbar. */
+*|*:root:-moz-full-screen-ancestor {
+  overflow: hidden !important;
+}
+
+*|*:-moz-full-screen-ancestor {
+  /* Ancestors of a full-screen element should not induce stacking contexts
+     that would prevent the full-screen element from being on top. */
+  z-index:auto !important;
+  /* Ancestors of a full-screen element should not be partially transparent,
+     since that would apply to the full-screen element and make the page visible
+     behind it. It would also create a pseudo-stacking-context that would let content
+     draw on top of the full-screen element. */
+  opacity:1 !important;
+  /* Ancestors of a full-screen element should not apply SVG masking, clipping, or
+     filtering, since that would affect the full-screen element and create a pseudo-
+     stacking context. */
+  mask:none !important;
+  clip:none !important;
+  filter:none !important;
+}
+
 /* XML parse error reporting */
 
 parsererror|parsererror {
   display: block;
   font-family: sans-serif;
   font-weight: bold;
   white-space: pre;
   margin: 1em;