Bug 508482 - Window activation status should be a pseudoclass (:-moz-window-inactive) instead of an attribute. r=dbaron, r+sr=jst
☠☠ backed out by 11d4bebe3514 ☠ ☠
authorMarkus Stange <mstange@themasta.com>
Wed, 17 Mar 2010 18:10:57 +0100
changeset 39526 e17c076aceea1afeb0d105c1a3b701d698a6c134
parent 39525 1e5178693a55168012c2654a4c910324bb376d07
child 39527 d906e4dd1e4957d46aaad39dfbd6f64990ca52dd
child 39537 11d4bebe3514ef47021734d052468df2ed7315ff
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron, r
bugs508482
milestone1.9.3a4pre
Bug 508482 - Window activation status should be a pseudoclass (:-moz-window-inactive) instead of an attribute. r=dbaron, r+sr=jst
accessible/src/base/nsDocAccessible.cpp
content/base/public/nsIDocument.h
content/base/public/nsIDocumentObserver.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/xul/document/src/nsXULDocument.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
dom/tests/mochitest/chrome/Makefile.in
dom/tests/mochitest/chrome/test_activation.xul
dom/tests/mochitest/chrome/window_activation.xul
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.cpp
layout/style/nsCSSPseudoClassList.h
layout/style/nsCSSRuleProcessor.cpp
layout/style/nsCSSRuleProcessor.h
layout/style/nsHTMLCSSStyleSheet.cpp
layout/style/nsHTMLCSSStyleSheet.h
layout/style/nsHTMLStyleSheet.cpp
layout/style/nsHTMLStyleSheet.h
layout/style/nsIStyleRuleProcessor.h
layout/style/nsRuleProcessorData.h
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
layout/style/nsTransitionManager.cpp
layout/style/nsTransitionManager.h
layout/style/test/test_selectors.html
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1330,16 +1330,21 @@ void nsDocAccessible::ContentStatesChang
   if (0 == (aStateMask & NS_EVENT_STATE_CHECKED)) {
     return;
   }
 
   nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
   nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
 }
 
+void nsDocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
+                                            PRInt32 aStateMask)
+{
+}
+
 void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument,
                                               nsIContent* aContent,
                                               CharacterDataChangeInfo* aInfo)
 {
   FireTextChangeEventForText(aContent, aInfo, PR_FALSE);
 }
 
 void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -110,22 +110,29 @@ class Loader;
 } // namespace css
 
 namespace dom {
 class Link;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID      \
-{ 0x36f0a42c, 0x089b, 0x4909, \
-  { 0xb3, 0xee, 0xc5, 0xa4, 0x00, 0x90, 0x30, 0x02 } }
+{ 0x94fb5716, 0xff00, 0x4b97, \
+ { 0x90, 0x01, 0x91, 0x65, 0x1a, 0x5f, 0xbe, 0x64 } }
 
 // 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              (1 << 0)
+// Window activation status
+#define NS_DOCUMENT_STATE_WINDOW_INACTIVE         (1 << 1)
+
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
 class nsIDocument : public nsINode
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOCUMENT_IID)
@@ -672,16 +679,21 @@ public:
   virtual ReadyState GetReadyStateEnum() = 0;
 
   // notify that one or two content nodes changed state
   // either may be nsnull, but not both
   virtual void ContentStatesChanged(nsIContent* aContent1,
                                     nsIContent* aContent2,
                                     PRInt32 aStateMask) = 0;
 
+  // Notify that a document state has changed.
+  // This should only be called by callers whose state is also reflected in the
+  // implementation of nsDocument::GetDocumentState.
+  virtual void DocumentStatesChanged(PRInt32 aStateMask) = 0;
+
   // Observation hooks for style data to propagate notifications
   // to document observers
   virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet,
                                 nsIStyleRule* aOldStyleRule,
                                 nsIStyleRule* aNewStyleRule) = 0;
   virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet,
                               nsIStyleRule* aStyleRule) = 0;
   virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
@@ -1278,16 +1290,23 @@ public:
    * Returns Doc_Theme_None if there is no lightweight theme specified,
    * Doc_Theme_Dark for a dark theme, Doc_Theme_Bright for a light theme, and
    * Doc_Theme_Neutral for any other theme. This is used to determine the state
    * of the pseudoclasses :-moz-lwtheme and :-moz-lwtheme-text.
    */
   virtual int GetDocumentLWTheme() { return Doc_Theme_None; }
 
   /**
+   * Returns the document state.
+   * Document state bits have the form NS_DOCUMENT_STATE_* and are declared in
+   * nsIDocument.h.
+   */
+  virtual PRInt32 GetDocumentState() = 0;
+
+  /**
    * Gets the document's cached pointer to the first <base> element in this
    * document which has an href attribute.  If the document doesn't contain any
    * <base> elements with an href, returns null.
    */
   virtual nsIContent* GetFirstBaseNodeWithHref() = 0;
 
   /**
    * Sets the document's cached pointer to the first <base> element with an
--- a/content/base/public/nsIDocumentObserver.h
+++ b/content/base/public/nsIDocumentObserver.h
@@ -114,16 +114,25 @@ public:
    * @param aContent2 optional second piece of content that changed
    */
   virtual void ContentStatesChanged(nsIDocument* aDocument,
                                     nsIContent* aContent1,
                                     nsIContent* aContent2,
                                     PRInt32 aStateMask) = 0;
 
   /**
+   * Notification that the state of the document has changed.
+   *
+   * @param aDocument The document being observed
+   * @param aStateMask the state that changed
+   */
+  virtual void DocumentStatesChanged(nsIDocument* aDocument,
+                                     PRInt32 aStateMask) = 0;
+
+  /**
    * A StyleSheet has just been added to the document.  This method is
    * called automatically when a StyleSheet gets added to the
    * document, even if the stylesheet is not applicable. The
    * notification is passed on to all of the document observers.   
    *
    * @param aDocument The document being observed
    * @param aStyleSheet the StyleSheet that has been added
    * @param aDocumentSheet True if sheet is in document's style sheet list,
@@ -229,16 +238,18 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumen
     virtual void BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);\
     virtual void EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);\
     virtual void BeginLoad(nsIDocument* aDocument);                          \
     virtual void EndLoad(nsIDocument* aDocument);                            \
     virtual void ContentStatesChanged(nsIDocument* aDocument,                \
                                       nsIContent* aContent1,                 \
                                       nsIContent* aContent2,                 \
                                       PRInt32 aStateMask);                   \
+    virtual void DocumentStatesChanged(nsIDocument* aDocument,               \
+                                       PRInt32 aStateMask);                  \
     virtual void StyleSheetAdded(nsIDocument* aDocument,                     \
                                  nsIStyleSheet* aStyleSheet,                 \
                                  PRBool aDocumentSheet);                     \
     virtual void StyleSheetRemoved(nsIDocument* aDocument,                   \
                                    nsIStyleSheet* aStyleSheet,               \
                                    PRBool aDocumentSheet);                   \
     virtual void StyleSheetApplicableStateChanged(nsIDocument* aDocument,    \
                                                   nsIStyleSheet* aStyleSheet,\
@@ -279,16 +290,22 @@ void                                    
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(_class)                    \
 void                                                                      \
 _class::ContentStatesChanged(nsIDocument* aDocument,                      \
                              nsIContent* aContent1,                       \
                              nsIContent* aContent2,                       \
                              PRInt32 aStateMask)                          \
 {                                                                         \
+}                                                                         \
+                                                                          \
+void                                                                      \
+_class::DocumentStatesChanged(nsIDocument* aDocument,                     \
+                              PRInt32 aStateMask)                         \
+{                                                                         \
 }
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_CONTENT(_class)                       \
 NS_IMPL_NSIMUTATIONOBSERVER_CONTENT(_class)
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(_class)                    \
 void                                                                      \
 _class::StyleSheetAdded(nsIDocument* aDocument,                           \
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -4051,16 +4051,26 @@ void
 nsDocument::ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2,
                                  PRInt32 aStateMask)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStatesChanged,
                                (this, aContent1, aContent2, aStateMask));
 }
 
 void
+nsDocument::DocumentStatesChanged(PRInt32 aStateMask)
+{
+  // Invalidate our cached state.
+  mGotDocumentState &= ~aStateMask;
+  mDocumentState &= ~aStateMask;
+
+  NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
+}
+
+void
 nsDocument::StyleRuleChanged(nsIStyleSheet* aStyleSheet,
                              nsIStyleRule* aOldStyleRule,
                              nsIStyleRule* aNewStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
                                (this, aStyleSheet,
                                 aOldStyleRule, aNewStyleRule));
 }
@@ -7610,16 +7620,36 @@ nsDocument::MaybePreLoadImage(nsIURI* ur
   // Pin image-reference to avoid evicting it from the img-cache before
   // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
   // unlink
   if (NS_SUCCEEDED(rv)) {
     mPreloadingImages.AppendObject(request);
   }
 }
 
+PRInt32
+nsDocument::GetDocumentState()
+{
+  if (!(mGotDocumentState & NS_DOCUMENT_STATE_RTL_LOCALE)) {
+    if (IsDocumentRightToLeft()) {
+      mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
+    }
+    mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
+  }
+  if (!(mGotDocumentState & NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
+    nsIPresShell* shell = GetPrimaryShell();
+    if (shell && shell->GetPresContext() &&
+        shell->GetPresContext()->IsTopLevelWindowInactive()) {
+      mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
+    }
+    mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
+  }
+  return mDocumentState;
+}
+
 namespace {
 
 /**
  * Stub for LoadSheet(), since all we want is to get the sheet into
  * the CSSLoader's style cache
  */
 class StubCSSLoaderObserver : public nsICSSLoaderObserver {
 public:
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -688,16 +688,17 @@ public:
   virtual void EndLoad();
 
   virtual void SetReadyStateInternal(ReadyState rs);
   virtual ReadyState GetReadyStateEnum();
 
   virtual void ContentStatesChanged(nsIContent* aContent1,
                                     nsIContent* aContent2,
                                     PRInt32 aStateMask);
+  virtual void DocumentStatesChanged(PRInt32 aStateMask);
 
   virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet,
                                 nsIStyleRule* aOldStyleRule,
                                 nsIStyleRule* aNewStyleRule);
   virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet,
                               nsIStyleRule* aStyleRule);
   virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
                                 nsIStyleRule* aStyleRule);
@@ -934,16 +935,18 @@ public:
 
   virtual void PreloadStyle(nsIURI* uri, const nsAString& charset);
 
   virtual nsresult LoadChromeSheetSync(nsIURI* uri, PRBool isAgentSheet,
                                        nsICSSStyleSheet** sheet);
 
   virtual nsISupports* GetCurrentContentSink();
 
+  virtual PRInt32 GetDocumentState();
+
   virtual void RegisterFileDataUri(nsACString& aUri);
 
 protected:
   friend class nsNodeUtils;
   void RegisterNamedItems(nsIContent *aContent);
   void UnregisterNamedItems(nsIContent *aContent);
   void UpdateNameTableEntry(nsIContent *aContent);
   void UpdateIdTableEntry(nsIContent *aContent);
@@ -1129,16 +1132,19 @@ protected:
   PRUint32 mUpdateNestLevel;
 
   // The application cache that this document is associated with, if
   // any.  This can change during the lifetime of the document.
   nsCOMPtr<nsIApplicationCache> mApplicationCache;
 
   nsCOMPtr<nsIContent> mFirstBaseNodeWithHref;
 
+  PRInt32 mDocumentState;
+  PRInt32 mGotDocumentState;
+
 private:
   friend class nsUnblockOnloadEvent;
 
   void PostUnblockOnloadEvent();
   void DoUnblockOnload();
 
   nsresult InitCSP();
 
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -121,17 +121,16 @@
 #include "nsIObserverService.h"
 #include "nsNodeUtils.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIXULWindow.h"
 #include "nsXULPopupManager.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsURILoader.h"
-#include "nsCSSFrameConstructor.h"
 
 //----------------------------------------------------------------------
 //
 // CIDs
 //
 
 static NS_DEFINE_CID(kParserCID,                 NS_PARSER_CID);
 
@@ -4660,27 +4659,21 @@ nsXULDocument::IsDocumentRightToLeft()
     }
 
     return (mDocDirection == Direction_RightToLeft);
 }
 
 int
 nsXULDocument::DirectionChanged(const char* aPrefName, void* aData)
 {
-  // reset the direction and reflow the document. This will happen if
-  // the direction isn't actually being used, but that doesn't really
-  // matter too much
+  // Reset the direction and restyle the document if necessary.
   nsXULDocument* doc = (nsXULDocument *)aData;
-  if (doc)
+  if (doc) {
       doc->ResetDocumentDirection();
-
-  nsIPresShell *shell = doc->GetPrimaryShell();
-  if (shell) {
-      shell->FrameConstructor()->
-          PostRestyleEvent(doc->GetRootContent(), eReStyle_Self, NS_STYLE_HINT_NONE);
+      doc->DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
   }
 
   return 0;
 }
 
 int
 nsXULDocument::GetDocumentLWTheme()
 {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6828,62 +6828,63 @@ nsGlobalWindow::GetLocation(nsIDOMLocati
   NS_IF_ADDREF(*aLocation = mLocation);
 
   return NS_OK;
 }
 
 void
 nsGlobalWindow::ActivateOrDeactivate(PRBool aActivate)
 {
-  // Set / unset the "active" attribute on the documentElement
-  // of the top level window
+  // Set / unset mIsActive on the top level window, which is used for the
+  // :-moz-window-inactive pseudoclass.
   nsCOMPtr<nsIWidget> mainWidget = GetMainWidget();
-  if (mainWidget) {
-    // Get the top level widget (if the main widget is a sheet, this will
-    // be the sheet's top (non-sheet) parent).
-    nsCOMPtr<nsIWidget> topLevelWidget = mainWidget->GetSheetWindowParent();
-    if (!topLevelWidget)
-      topLevelWidget = mainWidget;
-
-    // Get the top level widget's nsGlobalWindow
-    nsCOMPtr<nsIDOMWindowInternal> topLevelWindow;
-    if (topLevelWidget == mainWidget) {
-      topLevelWindow = static_cast<nsIDOMWindowInternal *>(this);
-    } else {
-      // This is a workaround for the following problem:
-      // When a window with an open sheet loses focus, only the sheet window
-      // receives the NS_DEACTIVATE event. However, it's not the sheet that
-      // should lose the "active" attribute, but the containing top level window.
-      void* clientData;
-      topLevelWidget->GetClientData(clientData); // clientData is nsXULWindow
-      nsISupports* data = static_cast<nsISupports*>(clientData);
-      nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(data));
-      topLevelWindow = do_GetInterface(req);
-    }
-
-    if (topLevelWindow) {
-      // Only set the attribute if the document is a XUL document and the
-      // window is a chrome window
-      nsCOMPtr<nsIDOMDocument> domDoc;
-      topLevelWindow->GetDocument(getter_AddRefs(domDoc));
-      nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
-      nsCOMPtr<nsIDOMXULDocument> xulDoc(do_QueryInterface(doc));
-      nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(topLevelWindow);
-      if (xulDoc && chromeWin) {
-        nsCOMPtr<nsIContent> rootElem = doc->GetRootContent();
-        if (rootElem) {
-          if (aActivate)
-            rootElem->SetAttr(kNameSpaceID_None, nsGkAtoms::active,
-                              NS_LITERAL_STRING("true"), PR_TRUE);
-          else
-            rootElem->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, PR_TRUE);
-        }
-      }
-    }
-  }
+  if (!mainWidget)
+    return;
+
+  // Get the top level widget (if the main widget is a sheet, this will
+  // be the sheet's top (non-sheet) parent).
+  nsCOMPtr<nsIWidget> topLevelWidget = mainWidget->GetSheetWindowParent();
+  if (!topLevelWidget) {
+    topLevelWidget = mainWidget;
+  }
+
+  // Get the top level widget's nsGlobalWindow
+  nsCOMPtr<nsIDOMWindowInternal> topLevelWindow;
+  if (topLevelWidget == mainWidget) {
+    topLevelWindow = static_cast<nsIDOMWindowInternal*>(this);
+  } else {
+    // This is a workaround for the following problem:
+    // When a window with an open sheet loses focus, only the sheet window
+    // receives the NS_DEACTIVATE event. However, it's not the sheet that
+    // should lose the active styling, but the containing top level window.
+    void* clientData;
+    topLevelWidget->GetClientData(clientData); // clientData is nsXULWindow
+    nsISupports* data = static_cast<nsISupports*>(clientData);
+    nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(data));
+    topLevelWindow = do_GetInterface(req);
+  }
+  if (topLevelWindow) {
+    nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(topLevelWindow));
+    piWin->SetActive(aActivate);
+  }
+}
+
+static PRBool
+NotifyDocumentTree(nsIDocument* aDocument, void* aData)
+{
+  aDocument->EnumerateSubDocuments(NotifyDocumentTree, nsnull);
+  aDocument->DocumentStatesChanged(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
+  return PR_TRUE;
+}
+
+void
+nsGlobalWindow::SetActive(PRBool aActive)
+{
+  nsPIDOMWindow::SetActive(aActive);
+  NotifyDocumentTree(mDoc, nsnull);
 }
 
 void
 nsGlobalWindow::SetChromeEventHandler(nsPIDOMEventTarget* aChromeEventHandler)
 {
   SetChromeEventHandlerInternal(aChromeEventHandler);
   if (IsOuterWindow()) {
     // update the chrome event handler on all our inner windows
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -283,16 +283,17 @@ public:
   NS_DECL_NSIDOM3EVENTTARGET
 
   // nsIDOMNSEventTarget
   NS_DECL_NSIDOMNSEVENTTARGET
 
   // nsPIDOMWindow
   virtual NS_HIDDEN_(nsPIDOMWindow*) GetPrivateRoot();
   virtual NS_HIDDEN_(void) ActivateOrDeactivate(PRBool aActivate);
+  virtual NS_HIDDEN_(void) SetActive(PRBool aActive);
   virtual NS_HIDDEN_(void) SetChromeEventHandler(nsPIDOMEventTarget* aChromeEventHandler);
 
   virtual NS_HIDDEN_(void) SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal);
   virtual NS_HIDDEN_(nsIPrincipal*) GetOpenerScriptPrincipal();
 
   virtual NS_HIDDEN_(PopupControlState) PushPopupControlState(PopupControlState state, PRBool aForce) const;
   virtual NS_HIDDEN_(void) PopPopupControlState(PopupControlState state) const;
   virtual NS_HIDDEN_(PopupControlState) GetPopupControlState() const;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -73,31 +73,41 @@ class nsIScriptTimeoutHandler;
 class nsPresContext;
 struct nsTimeout;
 class nsScriptObjectHolder;
 class nsXBLPrototypeHandler;
 class nsIArray;
 class nsPIWindowRoot;
 
 #define NS_PIDOMWINDOW_IID \
-{ 0x81cdf500, 0x2183, 0x4af6, \
-  { 0xa4, 0x56, 0x35, 0x1f, 0x4a, 0x0d, 0x1a, 0x0b } }
+{ 0xC5CB154D, 0x17C0, 0x49E9, \
+  { 0x9C, 0x83, 0xFF, 0xC7, 0x2C, 0x93, 0xAF, 0x24 } }
 
 class nsPIDOMWindow : public nsIDOMWindowInternal
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOW_IID)
 
   virtual nsPIDOMWindow* GetPrivateRoot() = 0;
 
   virtual void ActivateOrDeactivate(PRBool aActivate) = 0;
 
   // this is called GetTopWindowRoot to avoid conflicts with nsIDOMWindow2::GetWindowRoot
   virtual already_AddRefed<nsPIWindowRoot> GetTopWindowRoot() = 0;
 
+  virtual void SetActive(PRBool aActive)
+  {
+    mIsActive = aActive;
+  }
+
+  PRBool IsActive()
+  {
+    return mIsActive;
+  }
+
   nsPIDOMEventTarget* GetChromeEventHandler() const
   {
     return mChromeEventHandler;
   }
 
   virtual void SetChromeEventHandler(nsPIDOMEventTarget* aChromeEventHandler) = 0;
 
   PRBool HasMutationListeners(PRUint32 aMutationEventType) const
@@ -485,18 +495,18 @@ protected:
   // be null if and only if the created window itself is an outer
   // window. In all other cases aOuterWindow should be the outer
   // window for the inner window that is being created.
   nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
     : mFrameElement(nsnull), mDocShell(nsnull), mModalStateDepth(0),
       mRunningTimeout(nsnull), mMutationBits(0), mIsDocumentLoaded(PR_FALSE),
       mIsHandlingResizeEvent(PR_FALSE), mIsInnerWindow(aOuterWindow != nsnull),
       mMayHavePaintEventListener(PR_FALSE),
-      mIsModalContentWindow(PR_FALSE), mInnerWindow(nsnull),
-      mOuterWindow(aOuterWindow)
+      mIsModalContentWindow(PR_FALSE), mIsActive(PR_FALSE),
+      mInnerWindow(nsnull), mOuterWindow(aOuterWindow)
   {
   }
 
   void SetChromeEventHandlerInternal(nsPIDOMEventTarget* aChromeEventHandler) {
     mChromeEventHandler = aChromeEventHandler;
   }
 
   // These two variables are special in that they're set to the same
@@ -520,16 +530,19 @@ protected:
   PRPackedBool           mIsHandlingResizeEvent;
   PRPackedBool           mIsInnerWindow;
   PRPackedBool           mMayHavePaintEventListener;
 
   // This variable is used on both inner and outer windows (and they
   // should match).
   PRPackedBool           mIsModalContentWindow;
 
+  // Tracks activation state that's used for :-moz-window-inactive 
+  PRPackedBool           mIsActive;
+
   // And these are the references between inner and outer windows.
   nsPIDOMWindow         *mInnerWindow;
   nsPIDOMWindow         *mOuterWindow;
 };
 
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindow, NS_PIDOMWINDOW_IID)
 
--- a/dom/tests/mochitest/chrome/Makefile.in
+++ b/dom/tests/mochitest/chrome/Makefile.in
@@ -55,12 +55,14 @@ include $(topsrcdir)/config/rules.mk
 		focus_window2.xul \
 		focus_frameset.html \
 		child_focus_frame.html \
 		test_focus_switchbinding.xul \
 		test_focus.xul \
 		window_focus.xul \
 		test_focused_link_scroll.xul \
 		test_geolocation.xul \
+		test_activation.xul \
+		window_activation.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/chrome/test_activation.xul
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<?xml-stylesheet href="data:text/css,
+
+#box {
+  background: blue;
+}
+
+#box:-moz-window-inactive {
+  background: cyan;
+}
+
+" type="text/css"?>
+<!--
+  Test for the :-moz-window-inactive pseudoclass
+  -->
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        sizemode="fullscreen">
+
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>      
+
+<box id="box" height="100"/>
+
+<script>
+SimpleTest.waitForExplicitFinish();
+
+newwindow = window.open("window_activation.xul", "_blank","chrome,width=300,height=200");
+
+function complete()
+{
+  newwindow.close();
+  SimpleTest.finish();
+}
+
+</script>
+
+
+<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/chrome/window_activation.xul
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="data:text/css,
+
+#box {
+  background: blue;
+}
+
+#box:-moz-window-inactive {
+  background: cyan;
+}
+
+" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<box id="box" height="100"/>
+
+<script type="application/javascript"><![CDATA[
+
+var ok = window.opener.wrappedJSObject.ok;
+var complete = window.opener.wrappedJSObject.complete;
+var openerDoc = window.opener.wrappedJSObject.document;
+
+window.onfocus = function () {
+  ok(getComputedStyle(document.getElementById("box"), "").backgroundColor, "rgb(0, 0, 255)");
+  ok(getComputedStyle(openerDoc.getElementById("box"), "").backgroundColor, "rgb(0, 255, 255)");
+  window.opener.focus();
+  ok(getComputedStyle(document.getElementById("box"), "").backgroundColor, "rgb(0, 255, 255)");
+  ok(getComputedStyle(openerDoc.getElementById("box"), "").backgroundColor, "rgb(0, 0, 255)");
+  complete();
+}
+
+]]></script>
+
+</window>
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1430,16 +1430,30 @@ nsPresContext::SetBidi(PRUint32 aSource,
 
 PRUint32
 nsPresContext::GetBidi() const
 {
   return Document()->GetBidiOptions();
 }
 #endif //IBMBIDI
 
+PRBool
+nsPresContext::IsTopLevelWindowInactive()
+{
+  nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryReferent(mContainer));
+  if (!treeItem)
+    return PR_FALSE;
+
+  nsCOMPtr<nsIDocShellTreeItem> rootItem;
+  treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
+  nsCOMPtr<nsPIDOMWindow> domWindow(do_GetInterface(rootItem));
+
+  return domWindow && !domWindow->IsActive();
+}
+
 nsITheme*
 nsPresContext::GetTheme()
 {
   if (!sNoTheme && !mTheme) {
     mTheme = do_GetService("@mozilla.org/chrome/chrome-native-theme;1");
     if (!mTheme)
       sNoTheme = PR_TRUE;
   }
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -752,16 +752,18 @@ public:
   void SetIsRenderingOnlySelection(PRBool aResult)
   {
     NS_ASSERTION(!(aResult & ~1), "Value must be true or false");
     mIsRenderingOnlySelection = aResult;
   }
 
   PRBool IsRenderingOnlySelection() const { return mIsRenderingOnlySelection; }
 
+  NS_HIDDEN_(PRBool) IsTopLevelWindowInactive();
+
   /*
    * Obtain a native them for rendering our widgets (both form controls and html)
    */
   NS_HIDDEN_(nsITheme*) GetTheme();
 
   /*
    * Notify the pres context that the theme has changed.  An internal switch
    * means it's one of our Mozilla themes that changed (e.g., Modern to Classic).
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -851,16 +851,18 @@ public:
   virtual void BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);
   virtual void EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);
   virtual void BeginLoad(nsIDocument* aDocument);
   virtual void EndLoad(nsIDocument* aDocument);
   virtual void ContentStatesChanged(nsIDocument* aDocument,
                                     nsIContent* aContent1,
                                     nsIContent* aContent2,
                                     PRInt32 aStateMask);
+  virtual void DocumentStatesChanged(nsIDocument* aDocument,
+                                     PRInt32 aStateMask);
   virtual void StyleSheetAdded(nsIDocument* aDocument,
                                nsIStyleSheet* aStyleSheet,
                                PRBool aDocumentSheet);
   virtual void StyleSheetRemoved(nsIDocument* aDocument,
                                  nsIStyleSheet* aStyleSheet,
                                  PRBool aDocumentSheet);
   virtual void StyleSheetApplicableStateChanged(nsIDocument* aDocument,
                                                 nsIStyleSheet* aStyleSheet,
@@ -4907,16 +4909,33 @@ PresShell::ContentStatesChanged(nsIDocum
   if (mDidInitialReflow) {
     nsAutoCauseReflowNotifier crNotifier(this);
     mFrameConstructor->ContentStatesChanged(aContent1, aContent2, aStateMask);
     VERIFY_STYLE_TREE;
   }
 }
 
 void
+PresShell::DocumentStatesChanged(nsIDocument* aDocument,
+                                 PRInt32 aStateMask)
+{
+  NS_PRECONDITION(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
+  NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
+
+  if (mDidInitialReflow &&
+      mStyleSet->HasDocumentStateDependentStyle(mPresContext,
+                                                mDocument->GetRootContent(),
+                                                aStateMask)) {
+    mFrameConstructor->PostRestyleEvent(mDocument->GetRootContent(),
+                                        eReStyle_Self, NS_STYLE_HINT_NONE);
+    VERIFY_STYLE_TREE;
+  }
+}
+
+void
 PresShell::AttributeWillChange(nsIDocument* aDocument,
                                nsIContent*  aContent,
                                PRInt32      aNameSpaceID,
                                nsIAtom*     aAttribute,
                                PRInt32      aModType)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
--- a/layout/style/nsCSSPseudoClassList.h
+++ b/layout/style/nsCSSPseudoClassList.h
@@ -136,16 +136,19 @@ CSS_PSEUDO_CLASS(mozLocaleDir, ":-moz-lo
 CSS_PSEUDO_CLASS(mozLWTheme, ":-moz-lwtheme")
 
 // -moz-lwtheme-brighttext matches a document that has a bright lightweight theme
 CSS_PSEUDO_CLASS(mozLWThemeBrightText, ":-moz-lwtheme-brighttext")
 
 // -moz-lwtheme-darktext matches a document that has a bright lightweight theme
 CSS_PSEUDO_CLASS(mozLWThemeDarkText, ":-moz-lwtheme-darktext")
 
+// Matches anything when the containing window is inactive
+CSS_PSEUDO_CLASS(mozWindowInactive, ":-moz-window-inactive")
+
 #ifdef MOZ_MATHML
 CSS_STATE_PSEUDO_CLASS(mozMathIncrementScriptLevel,
                        ":-moz-math-increment-script-level",
                        NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL)
 #endif
 
 // CSS 3 UI
 // http://www.w3.org/TR/2004/CR-css3-ui-20040511/#pseudo-classes
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -702,16 +702,17 @@ static const PLDHashTableOps AttributeSe
 };
 
 
 //--------------------------------
 
 struct RuleCascadeData {
   RuleCascadeData(nsIAtom *aMedium, PRBool aQuirksMode)
     : mRuleHash(aQuirksMode),
+      mSelectorDocumentStates(0),
       mStateSelectors(),
       mCacheKey(aMedium),
       mNext(nsnull),
       mQuirksMode(aQuirksMode)
   {
     PL_DHashTableInit(&mAttributeSelectors, &AttributeSelectorOps, nsnull,
                       sizeof(AttributeSelectorEntry), 16);
     PL_DHashTableInit(&mAnonBoxRules, &RuleHash_TagTable_Ops, nsnull,
@@ -731,16 +732,17 @@ struct RuleCascadeData {
     for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mPseudoElementRuleHashes); ++i) {
       delete mPseudoElementRuleHashes[i];
     }
   }
   RuleHash                 mRuleHash;
   RuleHash*
     mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
   nsTArray<nsCSSSelector*> mStateSelectors;
+  PRUint32                 mSelectorDocumentStates;
   nsTArray<nsCSSSelector*> mClassSelectors;
   nsTArray<nsCSSSelector*> mIDSelectors;
   PLDHashTable             mAttributeSelectors;
   PLDHashTable             mAnonBoxRules;
 #ifdef MOZ_XUL
   PLDHashTable             mXULTreeRules;
 #endif
 
@@ -1118,16 +1120,22 @@ RuleProcessorData::ContentState()
         (mContentState & NS_EVENT_STATE_VISITED)) {
       mContentState = (mContentState & ~PRUint32(NS_EVENT_STATE_VISITED)) |
                       NS_EVENT_STATE_UNVISITED;
     }
   }
   return mContentState;
 }
 
+PRUint32
+RuleProcessorData::DocumentState()
+{
+  return mContent->GetOwnerDoc()->GetDocumentState();
+}
+
 PRBool
 RuleProcessorData::IsLink()
 {
   PRUint32 state = ContentState();
   return (state & (NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) != 0;
 }
 
 PRInt32
@@ -1699,23 +1707,18 @@ mozIsHTMLMatches(RuleProcessorData& data
 }
 
 static PRBool NS_FASTCALL
 mozLocaleDirMatches(RuleProcessorData& data, PRBool setNodeFlags,
                     nsPseudoClassList* pseudoClass)
 {
   NS_PRECONDITION(pseudoClass->mAtom == nsCSSPseudoClasses::mozLocaleDir,
                   "Unexpected atom");
-  nsIDocument* doc = data.mContent->GetDocument();
 
-  if (!doc) {
-    return PR_FALSE;
-  }
-
-  PRBool docIsRTL = doc && doc->IsDocumentRightToLeft();
+  PRBool docIsRTL = (data.DocumentState() & NS_DOCUMENT_STATE_RTL_LOCALE) != 0;
 
   nsDependentString dirString(pseudoClass->u.mString);
   NS_ASSERTION(dirString.EqualsLiteral("ltr") || dirString.EqualsLiteral("rtl"),
                "invalid value for -moz-locale-dir");
 
   return dirString.EqualsLiteral("rtl") == docIsRTL;
 }
 
@@ -1747,16 +1750,26 @@ mozLWThemeDarkTextMatches(RuleProcessorD
   NS_PRECONDITION(pseudoClass->mAtom ==
                     nsCSSPseudoClasses::mozLWThemeDarkText,
                   "Unexpected atom");
   nsIDocument* doc = data.mContent->GetOwnerDoc();
   return doc && doc->GetDocumentLWTheme() == nsIDocument::Doc_Theme_Dark;
 }
 
 static PRBool NS_FASTCALL
+mozWindowInactiveMatches(RuleProcessorData& data, PRBool setNodeFlags,
+                         nsPseudoClassList* pseudoClass)
+{
+  NS_PRECONDITION(pseudoClass->mAtom ==
+                    nsCSSPseudoClasses::mozWindowInactive,
+                  "Unexpected atom");
+  return (data.DocumentState() & NS_DOCUMENT_STATE_WINDOW_INACTIVE) != 0;
+}
+
+static PRBool NS_FASTCALL
 notPseudoMatches(RuleProcessorData& data, PRBool setNodeFlags,
                  nsPseudoClassList* pseudoClass)
 {
   NS_NOTREACHED("Why did this get called?");
   return PR_FALSE;
 }
 
 typedef PRBool
@@ -2287,16 +2300,24 @@ nsCSSRuleProcessor::HasStateDependentSty
           SelectorMatchesTree(*aData, selector->mNext, PR_FALSE)) {
         hint = nsReStyleHint(hint | possibleChange);
       }
     }
   }
   return hint;
 }
 
+PRBool
+nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
+{
+  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
+
+  return cascade && (cascade->mSelectorDocumentStates & aData->mStateMask) != 0;
+}
+
 struct AttributeEnumData {
   AttributeEnumData(AttributeRuleProcessorData *aData)
     : data(aData), change(nsReStyleHint(0)) {}
 
   AttributeRuleProcessorData *data;
   nsReStyleHint change;
 };
 
@@ -2451,16 +2472,30 @@ PRBool IsStateSelector(nsCSSSelector& aS
     }
     if (sPseudoClassInfo[pseudoClass->mType].mBits) {
       return PR_TRUE;
     }
   }
   return PR_FALSE;
 }
 
+inline
+void AddSelectorDocumentStates(nsCSSSelector& aSelector, PRUint32* aStateMask)
+{
+  for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList;
+       pseudoClass; pseudoClass = pseudoClass->mNext) {
+    if (pseudoClass->mAtom == nsCSSPseudoClasses::mozLocaleDir) {
+      *aStateMask |= NS_DOCUMENT_STATE_RTL_LOCALE;
+    }
+    else if (pseudoClass->mAtom == nsCSSPseudoClasses::mozWindowInactive) {
+      *aStateMask |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
+    }
+  }
+}
+
 static PRBool
 AddRule(RuleValue* aRuleInfo, RuleCascadeData* aCascade)
 {
   RuleCascadeData * const cascade = aCascade;
 
   // Build the rule hash.
   nsCSSPseudoElements::Type pseudoType = aRuleInfo->mSelector->PseudoType();
   if (NS_LIKELY(pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement)) {
@@ -2528,16 +2563,19 @@ AddRule(RuleValue* aRuleInfo, RuleCascad
     // these lists twice, which means we'll check it twice, but I don't
     // think that's worth worrying about.   (We do the same for multiple
     // attribute selectors on the same attribute.)  Two, we don't really
     // need to check negations past the first in the current
     // implementation (and they're rare as well), but that might change
     // in the future if :not() is extended. 
     for (nsCSSSelector* negation = selector; negation;
          negation = negation->mNegations) {
+      // Track the selectors that depend on document states.
+      AddSelectorDocumentStates(*negation, &cascade->mSelectorDocumentStates);
+
       // Build mStateSelectors.
       if (IsStateSelector(*negation))
         stateArray->AppendElement(selector);
 
       // Build mIDSelectors
       if (negation->mIDList) {
         idArray->AppendElement(selector);
       }
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -99,16 +99,18 @@ public:
   NS_IMETHOD RulesMatching(AnonBoxRuleProcessorData* aData);
 
 #ifdef MOZ_XUL
   NS_IMETHOD RulesMatching(XULTreeRuleProcessorData* aData);
 #endif
 
   virtual nsReStyleHint HasStateDependentStyle(StateRuleProcessorData* aData);
 
+  virtual PRBool HasDocumentStateDependentStyle(StateRuleProcessorData* aData);
+
   virtual nsReStyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData);
 
   NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext,
                                    PRBool* aRulesChanged);
 
   // Append all the currently-active font face rules to aArray.  Return
   // true for success and false for failure.
--- a/layout/style/nsHTMLCSSStyleSheet.cpp
+++ b/layout/style/nsHTMLCSSStyleSheet.cpp
@@ -143,16 +143,22 @@ nsHTMLCSSStyleSheet::Init(nsIURI* aURL, 
 
 // Test if style is dependent on content state
 nsReStyleHint
 nsHTMLCSSStyleSheet::HasStateDependentStyle(StateRuleProcessorData* aData)
 {
   return nsReStyleHint(0);
 }
 
+PRBool
+nsHTMLCSSStyleSheet::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
+{
+  return PR_FALSE;
+}
+
 // Test if style is dependent on attribute
 nsReStyleHint
 nsHTMLCSSStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
 {
   return nsReStyleHint(0);
 }
 
 NS_IMETHODIMP
--- a/layout/style/nsHTMLCSSStyleSheet.h
+++ b/layout/style/nsHTMLCSSStyleSheet.h
@@ -75,16 +75,17 @@ public:
   // nsIStyleRuleProcessor
   NS_IMETHOD RulesMatching(ElementRuleProcessorData* aData);
   NS_IMETHOD RulesMatching(PseudoElementRuleProcessorData* aData);
   NS_IMETHOD RulesMatching(AnonBoxRuleProcessorData* aData);
 #ifdef MOZ_XUL
   NS_IMETHOD RulesMatching(XULTreeRuleProcessorData* aData);
 #endif
   virtual nsReStyleHint HasStateDependentStyle(StateRuleProcessorData* aData);
+  virtual PRBool HasDocumentStateDependentStyle(StateRuleProcessorData* aData);
   virtual nsReStyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData);
   NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext,
                                   PRBool* aResult);
 
 private: 
   // These are not supported and are not implemented! 
   nsHTMLCSSStyleSheet(const nsHTMLCSSStyleSheet& aCopy); 
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -296,16 +296,22 @@ nsHTMLStyleSheet::HasStateDependentStyle
        (mLinkRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)) ||
        (mVisitedRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)))) {
     return eReStyle_Self;
   }
   
   return nsReStyleHint(0);
 }
 
+PRBool
+nsHTMLStyleSheet::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
+{
+  return PR_FALSE;
+}
+
 nsReStyleHint
 nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
 {
   // Do nothing on before-change checks
   if (!aData->mAttrHasChanged) {
     return nsReStyleHint(0);
   }
 
--- a/layout/style/nsHTMLStyleSheet.h
+++ b/layout/style/nsHTMLStyleSheet.h
@@ -80,16 +80,17 @@ public:
   // nsIStyleRuleProcessor API
   NS_IMETHOD RulesMatching(ElementRuleProcessorData* aData);
   NS_IMETHOD RulesMatching(PseudoElementRuleProcessorData* aData);
   NS_IMETHOD RulesMatching(AnonBoxRuleProcessorData* aData);
 #ifdef MOZ_XUL
   NS_IMETHOD RulesMatching(XULTreeRuleProcessorData* aData);
 #endif
   virtual nsReStyleHint HasStateDependentStyle(StateRuleProcessorData* aData);
+  virtual PRBool HasDocumentStateDependentStyle(StateRuleProcessorData* aData);
   virtual nsReStyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData);
   NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext,
                                    PRBool* aRulesChanged);
 
   nsresult Init(nsIURI* aURL, nsIDocument* aDocument);
   nsresult Reset(nsIURI* aURL);
   nsresult GetLinkColor(nscolor& aColor);
--- a/layout/style/nsIStyleRuleProcessor.h
+++ b/layout/style/nsIStyleRuleProcessor.h
@@ -105,16 +105,24 @@ public:
   /**
    * Just like the previous |RulesMatching|, except for a given content
    * node <em>and tree pseudo</em>.
    */
   NS_IMETHOD RulesMatching(XULTreeRuleProcessorData* aData) = 0;
 #endif
 
   /**
+   * Return whether style can depend on a change of the given document state.
+   *
+   * Document states are defined in nsIDocument.h.
+   */
+  virtual PRBool
+    HasDocumentStateDependentStyle(StateRuleProcessorData* aData) = 0;
+
+  /**
    * Return how (as described by nsReStyleHint) style can depend on a
    * change of the given content state on the given content node.  This
    * test is used for optimization only, and may err on the side of
    * reporting more dependencies than really exist.
    *
    * Event states are defined in nsIEventStateManager.h.
    */
   virtual nsReStyleHint
--- a/layout/style/nsRuleProcessorData.h
+++ b/layout/style/nsRuleProcessorData.h
@@ -103,16 +103,17 @@ private:
     return aContext->AllocateFromShell(sz);
   }
   void* operator new(size_t sz) CPP_THROW_NEW {
     return ::operator new(sz);
   }
 public:
   const nsString* GetLang();
   PRUint32 ContentState();
+  PRUint32 DocumentState();
   PRBool IsLink();
 
   // Returns a 1-based index of the child in its parent.  If the child
   // is not in its parent's child list (i.e., it is anonymous content),
   // returns 0.
   // If aCheckEdgeOnly is true, the function will return 1 if the result
   // is 1, and something other than 1 (maybe or maybe not a valid
   // result) otherwise.
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -1065,17 +1065,42 @@ nsStyleSet::ReParentStyleContext(nsPresC
 
 struct StatefulData : public StateRuleProcessorData {
   StatefulData(nsPresContext* aPresContext,
                nsIContent* aContent, PRInt32 aStateMask)
     : StateRuleProcessorData(aPresContext, aContent, aStateMask),
       mHint(nsReStyleHint(0))
   {}
   nsReStyleHint   mHint;
-}; 
+};
+
+static PRBool SheetHasDocumentStateStyle(nsIStyleRuleProcessor* aProcessor,
+                                         void *aData)
+{
+  StatefulData* data = (StatefulData*)aData;
+  if (aProcessor->HasDocumentStateDependentStyle(data)) {
+    data->mHint = eReStyle_Self;
+    return PR_FALSE; // don't continue
+  }
+  return PR_TRUE; // continue
+}
+
+// Test if style is dependent on a document state.
+PRBool
+nsStyleSet::HasDocumentStateDependentStyle(nsPresContext* aPresContext,
+                                           nsIContent*    aContent,
+                                           PRInt32        aStateMask)
+{
+  if (!aContent || !aContent->IsNodeOfType(nsINode::eELEMENT))
+    return PR_FALSE;
+
+  StatefulData data(aPresContext, aContent, aStateMask);
+  WalkRuleProcessors(SheetHasDocumentStateStyle, &data);
+  return data.mHint != 0;
+}
 
 static PRBool SheetHasStatefulStyle(nsIStyleRuleProcessor* aProcessor,
                                     void *aData)
 {
   StatefulData* data = (StatefulData*)aData;
   nsReStyleHint hint = aProcessor->HasStateDependentStyle(data);
   data->mHint = nsReStyleHint(data->mHint | hint);
   return PR_TRUE; // continue
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -172,16 +172,21 @@ class nsStyleSet
   // Get a new style context that lives in a different parent
   // The new context will be the same as the old if the new parent is the
   // same as the old parent.
   already_AddRefed<nsStyleContext>
     ReParentStyleContext(nsPresContext* aPresContext,
                          nsStyleContext* aStyleContext,
                          nsStyleContext* aNewParentContext);
 
+  // Test if style is dependent on a document state.
+  PRBool HasDocumentStateDependentStyle(nsPresContext* aPresContext,
+                                        nsIContent*    aContent,
+                                        PRInt32        aStateMask);
+
   // Test if style is dependent on content state
   nsReStyleHint HasStateDependentStyle(nsPresContext* aPresContext,
                                        nsIContent*     aContent,
                                        PRInt32         aStateMask);
 
   // Test if style is dependent on the presence of an attribute.
   nsReStyleHint HasAttributeDependentStyle(nsPresContext* aPresContext,
                                            nsIContent*    aContent,
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -887,16 +887,22 @@ nsTransitionManager::RulesMatching(XULTr
 #endif
 
 nsReStyleHint
 nsTransitionManager::HasStateDependentStyle(StateRuleProcessorData* aData)
 {
   return nsReStyleHint(0);
 }
 
+PRBool
+nsTransitionManager::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
+{
+  return PR_FALSE;
+}
+
 nsReStyleHint
 nsTransitionManager::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
 {
   return nsReStyleHint(0);
 }
 
 NS_IMETHODIMP
 nsTransitionManager::MediumFeaturesChanged(nsPresContext* aPresContext,
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -88,16 +88,17 @@ public:
   // nsIStyleRuleProcessor
   NS_IMETHOD RulesMatching(ElementRuleProcessorData* aData);
   NS_IMETHOD RulesMatching(PseudoElementRuleProcessorData* aData);
   NS_IMETHOD RulesMatching(AnonBoxRuleProcessorData* aData);
 #ifdef MOZ_XUL
   NS_IMETHOD RulesMatching(XULTreeRuleProcessorData* aData);
 #endif
   virtual nsReStyleHint HasStateDependentStyle(StateRuleProcessorData* aData);
+  virtual PRBool HasDocumentStateDependentStyle(StateRuleProcessorData* aData);
   virtual nsReStyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData);
   NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext,
                                    PRBool* aRulesChanged);
 
   // nsARefreshObserver
   virtual void WillRefresh(mozilla::TimeStamp aTime);
 
--- a/layout/style/test/test_selectors.html
+++ b/layout/style/test/test_selectors.html
@@ -699,16 +699,19 @@ function run() {
     test_parseable("::-moz-tree-row(selected,focus)");
     test_parseable(":-moz-tree-row(selected     focus)");
     test_parseable("::-moz-tree-row(selected    ,   focus)");
     test_parseable("::-moz-tree-twisty(  hover open  )");
     test_balanced_unparseable("::-moz-tree-row(selected {[]} )");
     test_balanced_unparseable(":-moz-tree-twisty(open())");
     test_balanced_unparseable("::-moz-tree-twisty(hover ())");
 
+    test_parseable(":-moz-window-inactive");
+    test_parseable("div p:-moz-window-inactive:hover span");
+
     // Plugin pseudoclasses
     test_parseable(":-moz-type-unsupported");
     test_parseable(":-moz-handler-disabled");
     test_parseable(":-moz-handler-blocked");
     test_parseable(":-moz-handler-crashed");
     test_parseable(":-moz-has-handlerref");
 
     // Case sensitivity of tag selectors