Bug 800983. Expose a getter for default computed style. r=dbaron
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 17 Oct 2012 17:01:56 -0400
changeset 110727 fd12a2c69e93b58cb79f6c3d0482d3df6943397d
parent 110726 e711e4cdb4d5cec821d29f17c2f552cb35297303
child 110728 557cfb0bdc396835d11d6d1f62316fbd92df96cf
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersdbaron
bugs800983
milestone19.0a1
Bug 800983. Expose a getter for default computed style. r=dbaron An alternative implementation strategy is to add "default-only" versions of ResolveStyleFor and ResolvePseudoElementStyle and then modify FileRules to support those, or use a cut-down custom version of FileRules for them. That would be faster, but more complicated on the style set side. We can always make that switch if we need to, I guess.
content/canvas/src/CanvasRenderingContext2D.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/interfaces/base/nsIDOMWindow.idl
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
layout/style/test/Makefile.in
layout/style/test/test_default_computed_style.html
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -2599,18 +2599,16 @@ CanvasRenderingContext2D::SetFont(const 
 
   nsIPresShell* presShell = GetPresShell();
   if (!presShell) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
   nsIDocument* document = presShell->GetDocument();
 
-  nsCOMArray<nsIStyleRule> rules;
-
   nsRefPtr<css::StyleRule> rule;
   error = CreateFontStyleRule(font, document, getter_AddRefs(rule));
 
   if (error.Failed()) {
     return;
   }
 
   css::Declaration *declaration = rule->GetDeclaration();
@@ -2624,17 +2622,18 @@ CanvasRenderingContext2D::SetFont(const 
     declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
   if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
                   fsaVal->GetUnit() != eCSSUnit_System_Font)) {
       // We got an all-property value or a syntax error.  The spec says
       // this value must be ignored.
     return;
   }
 
-  rules.AppendObject(rule);
+  nsTArray< nsCOMPtr<nsIStyleRule> > rules;
+  rules.AppendElement(rule);
 
   nsStyleSet* styleSet = presShell->StyleSet();
 
   // have to get a parent style context for inherit-like relative
   // values (2em, bolder, etc.)
   nsRefPtr<nsStyleContext> parentContext;
 
   if (mCanvasElement && mCanvasElement->IsInDoc()) {
@@ -2649,18 +2648,18 @@ CanvasRenderingContext2D::SetFont(const 
     error = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
                                 document,
                                 getter_AddRefs(parentRule));
 
     if (error.Failed()) {
       return;
     }
 
-    nsCOMArray<nsIStyleRule> parentRules;
-    parentRules.AppendObject(parentRule);
+    nsTArray< nsCOMPtr<nsIStyleRule> > parentRules;
+    parentRules.AppendElement(parentRule);
     parentContext = styleSet->ResolveStyleForRules(nullptr, parentRules);
   }
 
   if (!parentContext) {
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8262,17 +8262,35 @@ nsGlobalWindow::UpdateCanvasFocus(bool a
   }
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetComputedStyle(nsIDOMElement* aElt,
                                  const nsAString& aPseudoElt,
                                  nsIDOMCSSStyleDeclaration** aReturn)
 {
-  FORWARD_TO_OUTER(GetComputedStyle, (aElt, aPseudoElt, aReturn),
+  return GetComputedStyleHelper(aElt, aPseudoElt, false, aReturn);
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::GetDefaultComputedStyle(nsIDOMElement* aElt,
+                                        const nsAString& aPseudoElt,
+                                        nsIDOMCSSStyleDeclaration** aReturn)
+{
+  return GetComputedStyleHelper(aElt, aPseudoElt, true, aReturn);
+}
+
+nsresult
+nsGlobalWindow::GetComputedStyleHelper(nsIDOMElement* aElt,
+                                       const nsAString& aPseudoElt,
+                                       bool aDefaultStylesOnly,
+                                       nsIDOMCSSStyleDeclaration** aReturn)
+{
+  FORWARD_TO_OUTER(GetComputedStyleHelper, (aElt, aPseudoElt,
+                                            aDefaultStylesOnly, aReturn),
                    NS_ERROR_NOT_INITIALIZED);
 
   NS_ENSURE_ARG_POINTER(aReturn);
   *aReturn = nullptr;
 
   if (!aElt) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
@@ -8305,17 +8323,19 @@ nsGlobalWindow::GetComputedStyle(nsIDOME
     if (!presShell) {
       return NS_OK;
     }
   }
 
   nsCOMPtr<dom::Element> element = do_QueryInterface(aElt);
   NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
   nsRefPtr<nsComputedDOMStyle> compStyle =
-    NS_NewComputedDOMStyle(element, aPseudoElt, presShell);
+    NS_NewComputedDOMStyle(element, aPseudoElt, presShell,
+                           aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly :
+                                                nsComputedDOMStyle::eAll);
 
   *aReturn = compStyle.forget().get();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -927,16 +927,22 @@ protected:
   nsresult GetTopImpl(nsIDOMWindow **aWindow, bool aScriptable);
 
   // Helper for creating performance objects.
   void CreatePerformanceObjectIfNeeded();
 
   // Outer windows only.
   nsDOMWindowList* GetWindowList();
 
+  // Helper for getComputedStyle and getDefaultComputedStyle
+  nsresult GetComputedStyleHelper(nsIDOMElement* aElt,
+                                  const nsAString& aPseudoElt,
+                                  bool aDefaultStylesOnly,
+                                  nsIDOMCSSStyleDeclaration** aReturn);
+
   // When adding new member variables, be careful not to create cycles
   // through JavaScript.  If there is any chance that a member variable
   // could own objects that are implemented in JavaScript, then those
   // objects will keep the global object (this object) alive.  To prevent
   // these cycles, ownership of such members must be released in
   // |CleanUp| and |DetachFromDocShell|.
 
   // This member is also used on both inner and outer windows, but
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -27,17 +27,17 @@ interface nsIDOMMozURLProperty : nsISupp
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(25404a1b-6c73-4850-af95-69aa095c8ad0)]
+[scriptable, uuid(43933989-912e-4b6a-b889-3c9fc9dd9ed4)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -311,16 +311,18 @@ interface nsIDOMWindow : nsISupports
 
 
   // CSSOM
   /**
    * @see <http://dev.w3.org/csswg/cssom/#dom-window-getcomputedstyle>
    */
   nsIDOMCSSStyleDeclaration getComputedStyle(in nsIDOMElement elt, 
                                              [optional] in DOMString pseudoElt);
+  nsIDOMCSSStyleDeclaration getDefaultComputedStyle(in nsIDOMElement elt,
+                                                    [optional] in DOMString pseudoElt);
 
 
   // Mozilla extensions
   /**
    * Get the window root for this window. This is useful for hooking
    * up event listeners to this window and every other window nested
    * in the window root.
    */
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -59,50 +59,54 @@ using namespace mozilla::dom;
  * This is the implementation of the readonly CSSStyleDeclaration that is
  * returned by the getComputedStyle() function.
  */
 
 static nsComputedDOMStyle *sCachedComputedDOMStyle;
 
 already_AddRefed<nsComputedDOMStyle>
 NS_NewComputedDOMStyle(dom::Element* aElement, const nsAString& aPseudoElt,
-                       nsIPresShell* aPresShell)
+                       nsIPresShell* aPresShell,
+                       nsComputedDOMStyle::StyleType aStyleType)
 {
   nsRefPtr<nsComputedDOMStyle> computedStyle;
   if (sCachedComputedDOMStyle) {
     // There's an unused nsComputedDOMStyle cached, use it.
     // But before we use it, re-initialize the object.
 
     // Oh yeah baby, placement new!
     computedStyle = new (sCachedComputedDOMStyle)
-      nsComputedDOMStyle(aElement, aPseudoElt, aPresShell);
+      nsComputedDOMStyle(aElement, aPseudoElt, aPresShell, aStyleType);
 
     sCachedComputedDOMStyle = nullptr;
   } else {
     // No nsComputedDOMStyle cached, create a new one.
 
-    computedStyle = new nsComputedDOMStyle(aElement, aPseudoElt, aPresShell);
+    computedStyle = new nsComputedDOMStyle(aElement, aPseudoElt, aPresShell,
+                                           aStyleType);
   }
 
   return computedStyle.forget();
 }
 
 static nsIFrame*
 GetContainingBlockFor(nsIFrame* aFrame) {
   if (!aFrame) {
     return nullptr;
   }
   return aFrame->GetContainingBlock();
 }
 
 nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement,
                                        const nsAString& aPseudoElt,
-                                       nsIPresShell* aPresShell)
+                                       nsIPresShell* aPresShell,
+                                       StyleType aStyleType)
   : mDocumentWeak(nullptr), mOuterFrame(nullptr),
     mInnerFrame(nullptr), mPresShell(nullptr),
+    mStyleType(aStyleType),
     mExposeVisitedStyle(false)
 {
   MOZ_ASSERT(aElement && aPresShell);
 
   mDocumentWeak = do_GetWeakReference(aPresShell->GetDocument());
 
   mContent = aElement;
 
@@ -263,94 +267,125 @@ nsComputedDOMStyle::GetPropertyValue(con
 
   return rv;
 }
 
 /* static */
 already_AddRefed<nsStyleContext>
 nsComputedDOMStyle::GetStyleContextForElement(Element* aElement,
                                               nsIAtom* aPseudo,
-                                              nsIPresShell* aPresShell)
+                                              nsIPresShell* aPresShell,
+                                              StyleType aStyleType)
 {
   // If the content has a pres shell, we must use it.  Otherwise we'd
   // potentially mix rule trees by using the wrong pres shell's style
   // set.  Using the pres shell from the content also means that any
   // content that's actually *in* a document will get the style from the
   // correct document.
   nsIPresShell *presShell = GetPresShellForContent(aElement);
   if (!presShell) {
     presShell = aPresShell;
     if (!presShell)
       return nullptr;
   }
 
   presShell->FlushPendingNotifications(Flush_Style);
 
-  return GetStyleContextForElementNoFlush(aElement, aPseudo, presShell);
+  return GetStyleContextForElementNoFlush(aElement, aPseudo, presShell,
+                                          aStyleType);
 }
 
 /* static */
 already_AddRefed<nsStyleContext>
 nsComputedDOMStyle::GetStyleContextForElementNoFlush(Element* aElement,
                                                      nsIAtom* aPseudo,
-                                                     nsIPresShell* aPresShell)
+                                                     nsIPresShell* aPresShell,
+                                                     StyleType aStyleType)
 {
   NS_ABORT_IF_FALSE(aElement, "NULL element");
   // If the content has a pres shell, we must use it.  Otherwise we'd
   // potentially mix rule trees by using the wrong pres shell's style
   // set.  Using the pres shell from the content also means that any
   // content that's actually *in* a document will get the style from the
   // correct document.
   nsIPresShell *presShell = GetPresShellForContent(aElement);
   if (!presShell) {
     presShell = aPresShell;
     if (!presShell)
       return nullptr;
   }
 
-  if (!aPseudo) {
+  if (!aPseudo && aStyleType == eAll) {
     nsIFrame* frame = aElement->GetPrimaryFrame();
     if (frame) {
       nsStyleContext* result =
         nsLayoutUtils::GetStyleFrame(frame)->GetStyleContext();
       // Don't use the style context if it was influenced by
       // pseudo-elements, since then it's not the primary style
       // for this element.
       if (!result->HasPseudoElementData()) {
         // this function returns an addrefed style context
         result->AddRef();
         return result;
       }
     }
   }
 
-  // No frame has been created or we have a pseudo, so resolve the
-  // style ourselves
+  // No frame has been created, or we have a pseudo, or we're looking
+  // for the default style, so resolve the style ourselves.
   nsRefPtr<nsStyleContext> parentContext;
   nsIContent* parent = aPseudo ? aElement : aElement->GetParent();
   // Don't resolve parent context for document fragments.
   if (parent && parent->IsElement())
     parentContext = GetStyleContextForElementNoFlush(parent->AsElement(),
-                                                     nullptr, presShell);
+                                                     nullptr, presShell,
+                                                     aStyleType);
 
   nsPresContext *presContext = presShell->GetPresContext();
   if (!presContext)
     return nullptr;
 
   nsStyleSet *styleSet = presShell->StyleSet();
 
+  nsRefPtr<nsStyleContext> sc;
   if (aPseudo) {
     nsCSSPseudoElements::Type type = nsCSSPseudoElements::GetPseudoType(aPseudo);
     if (type >= nsCSSPseudoElements::ePseudo_PseudoElementCount) {
       return nullptr;
     }
-    return styleSet->ResolvePseudoElementStyle(aElement, type, parentContext);
+    sc = styleSet->ResolvePseudoElementStyle(aElement, type, parentContext);
+  } else {
+    sc = styleSet->ResolveStyleFor(aElement, parentContext);
   }
 
-  return styleSet->ResolveStyleFor(aElement, parentContext);
+  if (aStyleType == eDefaultOnly) {
+    // We really only want the user and UA rules.  Filter out the other ones.
+    nsTArray< nsCOMPtr<nsIStyleRule> > rules;
+    for (nsRuleNode* ruleNode = sc->GetRuleNode();
+         !ruleNode->IsRoot();
+         ruleNode = ruleNode->GetParent()) {
+      if (ruleNode->GetLevel() == nsStyleSet::eAgentSheet ||
+          ruleNode->GetLevel() == nsStyleSet::eUserSheet) {
+        rules.AppendElement(ruleNode->GetRule());
+      }
+    }
+
+    // We want to build a list of user/ua rules that is in order from least to
+    // most important, so we have to reverse the list.
+    // Integer division to get "stop" is purposeful here: if length is odd, we
+    // don't have to do anything with the middle element of the array.
+    for (uint32_t i = 0, length = rules.Length(), stop = length / 2;
+         i < stop; ++i) {
+      rules[i].swap(rules[length - i - 1]);
+    }
+    
+    sc = styleSet->ResolveStyleForRules(parentContext, rules);
+  }
+
+  return sc.forget();
 }
 
 /* static */
 nsIPresShell*
 nsComputedDOMStyle::GetPresShellForContent(nsIContent* aContent)
 {
   nsIDocument* currentDoc = aContent->GetCurrentDoc();
   if (!currentDoc)
@@ -449,17 +484,17 @@ nsComputedDOMStyle::GetPropertyCSSValue(
 #ifdef DEBUG
   mFlushedPendingReflows = propEntry->mNeedsLayoutFlush;
 #endif
 
   mPresShell = document->GetShell();
   NS_ENSURE_TRUE(mPresShell && mPresShell->GetPresContext(),
                  NS_ERROR_NOT_AVAILABLE);
 
-  if (!mPseudo) {
+  if (!mPseudo && mStyleType == eAll) {
     mOuterFrame = mContent->GetPrimaryFrame();
     mInnerFrame = mOuterFrame;
     if (mOuterFrame) {
       nsIAtom* type = mOuterFrame->GetType();
       if (type == nsGkAtoms::tableOuterFrame) {
         // If the frame is an outer table frame then we should get the style
         // from the inner table frame.
         mInnerFrame = mOuterFrame->GetFirstPrincipalChild();
@@ -490,17 +525,18 @@ nsComputedDOMStyle::GetPropertyCSSValue(
                    "we should be in a pseudo-element that is expected to "
                    "contain elements");
     }
 #endif
     // Need to resolve a style context
     mStyleContextHolder =
       nsComputedDOMStyle::GetStyleContextForElement(mContent->AsElement(),
                                                     mPseudo,
-                                                    mPresShell);
+                                                    mPresShell,
+                                                    mStyleType);
     NS_ENSURE_TRUE(mStyleContextHolder, NS_ERROR_OUT_OF_MEMORY);
     NS_ASSERTION(mPseudo || !mStyleContextHolder->HasPseudoElementData(),
                  "should not have pseudo-element data");
   }
 
   // mExposeVisitedStyle is set to true only by testing APIs that
   // require UniversalXPConnect.
   NS_ABORT_IF_FALSE(!mExposeVisitedStyle ||
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -33,36 +33,44 @@ public:
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsComputedDOMStyle,
                                                                    nsICSSDeclaration)
 
   NS_DECL_NSICSSDECLARATION
 
   NS_DECL_NSIDOMCSSSTYLEDECLARATION
   virtual void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName);
 
+  enum StyleType {
+    eDefaultOnly, // Only includes UA and user sheets
+    eAll // Includes all stylesheets
+  };
+
   nsComputedDOMStyle(mozilla::dom::Element* aElement,
                      const nsAString& aPseudoElt,
-                     nsIPresShell* aPresShell);
+                     nsIPresShell* aPresShell,
+                     StyleType aStyleType);
   virtual ~nsComputedDOMStyle();
 
   static void Shutdown();
 
   virtual nsINode *GetParentObject() MOZ_OVERRIDE
   {
     return mContent;
   }
 
   static already_AddRefed<nsStyleContext>
   GetStyleContextForElement(mozilla::dom::Element* aElement, nsIAtom* aPseudo,
-                            nsIPresShell* aPresShell);
+                            nsIPresShell* aPresShell,
+                            StyleType aStyleType = eAll);
 
   static already_AddRefed<nsStyleContext>
   GetStyleContextForElementNoFlush(mozilla::dom::Element* aElement,
                                    nsIAtom* aPseudo,
-                                   nsIPresShell* aPresShell);
+                                   nsIPresShell* aPresShell,
+                                   StyleType aStyleType = eAll);
 
   static nsIPresShell*
   GetPresShellForContent(nsIContent* aContent);
 
   // Helper for nsDOMWindowUtils::GetVisitedDependentComputedStyle
   void SetExposeVisitedStyle(bool aExpose) {
     NS_ASSERTION(aExpose != mExposeVisitedStyle, "should always be changing");
     mExposeVisitedStyle = aExpose;
@@ -500,22 +508,29 @@ private:
    */
   nsIFrame* mInnerFrame;
   /*
    * While computing style data, the presshell we're working with.  Null
    * otherwise.
    */
   nsIPresShell* mPresShell;
 
+  /*
+   * The kind of styles we should be returning.
+   */
+  StyleType mStyleType;
+
   bool mExposeVisitedStyle;
 
 #ifdef DEBUG
   bool mFlushedPendingReflows;
 #endif
 };
 
 already_AddRefed<nsComputedDOMStyle>
 NS_NewComputedDOMStyle(mozilla::dom::Element* aElement,
                        const nsAString& aPseudoElt,
-                       nsIPresShell* aPresShell);
+                       nsIPresShell* aPresShell,
+                       nsComputedDOMStyle::StyleType aStyleType =
+                         nsComputedDOMStyle::eAll);
 
 #endif /* nsComputedDOMStyle_h__ */
 
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -964,26 +964,26 @@ nsStyleSet::ResolveStyleFor(Element* aEl
                     nsCSSRuleProcessor::GetContentState(aElement, aTreeMatchContext).
                       HasState(NS_EVENT_STATE_VISITED),
                     nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement,
                     true, aElement);
 }
 
 already_AddRefed<nsStyleContext>
 nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext,
-                                 const nsCOMArray<nsIStyleRule> &aRules)
+                                 const nsTArray< nsCOMPtr<nsIStyleRule> > &aRules)
 {
   NS_ENSURE_FALSE(mInShutdown, nullptr);
 
   nsRuleWalker ruleWalker(mRuleTree);
   // FIXME: Perhaps this should be passed in, but it probably doesn't
   // matter.
   ruleWalker.SetLevel(eDocSheet, false, false);
-  for (int32_t i = 0; i < aRules.Count(); i++) {
-    ruleWalker.ForwardOnPossiblyCSSRule(aRules.ObjectAt(i));
+  for (uint32_t i = 0; i < aRules.Length(); i++) {
+    ruleWalker.ForwardOnPossiblyCSSRule(aRules.ElementAt(i));
   }
 
   return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr,
                     false, false,
                     nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement,
                     false, nullptr);
 }
 
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -85,17 +85,17 @@ class nsStyleSet
   ResolveStyleFor(mozilla::dom::Element* aElement,
                   nsStyleContext* aParentContext,
                   TreeMatchContext& aTreeMatchContext);
 
   // Get a style context (with the given parent) for the
   // sequence of style rules in the |aRules| array.
   already_AddRefed<nsStyleContext>
   ResolveStyleForRules(nsStyleContext* aParentContext,
-                       const nsCOMArray<nsIStyleRule> &aRules);
+                       const nsTArray< nsCOMPtr<nsIStyleRule> > &aRules);
 
   // Get a style context that represents aBaseContext, but as though
   // it additionally matched the rules in the aRules array (in that
   // order, as more specific than any other rules).
   already_AddRefed<nsStyleContext>
   ResolveStyleByAddingRules(nsStyleContext* aBaseContext,
                             const nsCOMArray<nsIStyleRule> &aRules);
 
--- a/layout/style/test/Makefile.in
+++ b/layout/style/test/Makefile.in
@@ -87,16 +87,17 @@ MOCHITEST_FILES =	test_acid3_test46.html
 		file_bug645998-2.css \
 		test_bug716226.html \
 		test_bug765590.html \
 		test_cascade.html \
 		test_ch_ex_no_infloops.html \
 		test_compute_data_with_start_struct.html \
 		test_computed_style.html \
 		test_computed_style_no_pseudo.html \
+		test_default_computed_style.html \
 		test_css_cross_domain.html \
 		test_css_eof_handling.html \
 		test_default_bidi_css.html \
 		test_descriptor_storage.html \
 		test_descriptor_syntax_errors.html \
 		test_dont_use_document_colors.html \
 		test_font_face_parser.html \
 		test_garbage_at_end_of_declarations.html \
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_default_computed_style.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=800983
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 800983</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style>
+    #display::before { content: "Visible"; display: block }
+    #display {
+      display: inline;
+      margin-top: 0;
+      background: yellow;
+      color: blue;
+    }
+  </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=800983">Mozilla Bug 800983</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 800983 **/
+var cs = getComputedStyle($("display"));
+var cs_pseudo = getComputedStyle($("display"), "::before")
+
+var cs_default = getDefaultComputedStyle($("display"));
+var cs_default_pseudo = getDefaultComputedStyle($("display"), "::before");
+
+// Sanity checks for normal computed style
+is(cs.display, "inline", "We have inline display");
+is(cs.marginTop, "0px", "We have 0 margin");
+is(cs.backgroundColor, "rgb(255, 255, 0)", "We have yellow background");
+is(cs.color, "rgb(0, 0, 255)", "We have blue text");
+is(cs_pseudo.content, '"Visible"', "We have some content");
+is(cs_pseudo.display, "block", "Our ::before is block");
+
+// And now our actual tests
+is(cs_default.display, "block", "We have block display by default");
+is(cs_default.marginTop, "16px", "We have 16px margin by default");
+is(cs_default.backgroundColor, "transparent",
+   "We have transparent background by default");
+is(cs_default.color, "rgb(0, 0, 0)", "We have black text by default");
+is(cs_default_pseudo.content, "none", "We have no content by default");
+is(cs_default_pseudo.display, "inline", "Our ::before is inline by default");
+
+
+</script>
+</pre>
+</body>
+</html>