Bug 833808 part 2. Add some utilities for working with selectors to inspector utils. r=dbaron
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 13 Feb 2013 10:11:53 -0500
changeset 121749 05524e628b83
parent 121748 04f0afec7a03
child 121750 a9cfb608a633
push id24307
push useremorley@mozilla.com
push dateThu, 14 Feb 2013 10:47:46 +0000
treeherdermozilla-central@aceeea086ccb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs833808
milestone21.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 833808 part 2. Add some utilities for working with selectors to inspector utils. r=dbaron
layout/inspector/public/inIDOMUtils.idl
layout/inspector/src/inDOMUtils.cpp
layout/style/StyleRule.h
--- a/layout/inspector/public/inIDOMUtils.idl
+++ b/layout/inspector/public/inIDOMUtils.idl
@@ -11,23 +11,41 @@ interface nsIDOMElement;
 interface nsIDOMDocument;
 interface nsIDOMCSSStyleRule;
 interface nsIDOMNode;
 interface nsIDOMNodeList;
 interface nsIDOMFontFaceList;
 interface nsIDOMRange;
 interface nsIDOMCSSStyleSheet;
 
-[scriptable, uuid(f7a37305-a963-4a2a-b951-2c97a6b27fb4)]
+[scriptable, uuid(dd8a9dfd-336f-4cce-8ec1-0365ede9a3a8)]
 interface inIDOMUtils : nsISupports
 {
   // CSS utilities
   nsISupportsArray getCSSStyleRules(in nsIDOMElement aElement, [optional] in DOMString aPseudo);
   unsigned long getRuleLine(in nsIDOMCSSStyleRule aRule);
 
+  // Utilities for working with selectors.  We don't have a JS OM representation
+  // of a single selector or a selector list yet, but given a rule we can index
+  // into the selector list.
+  //
+  // This is a somewhat backwards API; once we move StyleRule to WebIDL we
+  // should consider using [ChromeOnly] APIs on that.
+  unsigned long getSelectorCount(in nsIDOMCSSStyleRule aRule);
+  // For all three functions below, aSelectorIndex is 0-based
+  AString getSelectorText(in nsIDOMCSSStyleRule aRule,
+                          in unsigned long aSelectorIndex);
+  unsigned long long getSpecificity(in nsIDOMCSSStyleRule aRule,
+                                    in unsigned long aSelectorIndex);
+  // Note: This does not handle scoped selectors correctly, because it has no
+  // idea what the right scope is.
+  bool selectorMatchesElement(in nsIDOMElement aElement,
+                              in nsIDOMCSSStyleRule aRule,
+                              in unsigned long aSelectorIndex);
+
   // Returns true if the string names a property that is inherited by default.
   bool isInheritedProperty(in AString aPropertyName);
 
   // DOM Node utilities
   boolean isIgnorableWhitespace(in nsIDOMCharacterData aDataNode);
   // Returns the "parent" of a node.  The parent of a document node is the
   // frame/iframe containing that document.  aShowingAnonymousContent says
   // whether we are showing anonymous content.
--- a/layout/inspector/src/inDOMUtils.cpp
+++ b/layout/inspector/src/inDOMUtils.cpp
@@ -22,17 +22,23 @@
 #include "nsIMutableArray.h"
 #include "nsBindingManager.h"
 #include "nsComputedDOMStyle.h"
 #include "nsEventStateManager.h"
 #include "nsIAtom.h"
 #include "nsRange.h"
 #include "mozilla/dom/Element.h"
 #include "nsCSSStyleSheet.h"
+#include "nsRuleWalker.h"
+#include "nsRuleProcessorData.h"
+#include "nsCSSRuleProcessor.h"
 
+using namespace mozilla;
+using namespace mozilla::css;
+using namespace mozilla::dom;
 
 ///////////////////////////////////////////////////////////////////////////////
 
 inDOMUtils::inDOMUtils()
 {
 }
 
 inDOMUtils::~inDOMUtils()
@@ -183,29 +189,146 @@ inDOMUtils::GetCSSStyleRules(nsIDOMEleme
   }
 
   *_retval = rules;
   NS_ADDREF(*_retval);
 
   return NS_OK;
 }
 
+static already_AddRefed<StyleRule>
+GetRuleFromDOMRule(nsIDOMCSSStyleRule *aRule, ErrorResult& rv)
+{
+  nsCOMPtr<nsICSSStyleRuleDOMWrapper> rule = do_QueryInterface(aRule);
+  if (!rule) {
+    rv.Throw(NS_ERROR_INVALID_ARG);
+    return nullptr;
+  }
+
+  nsRefPtr<StyleRule> cssrule;
+  rv = rule->GetCSSStyleRule(getter_AddRefs(cssrule));
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  if (!cssrule) {
+    rv.Throw(NS_ERROR_FAILURE);
+  }
+  return cssrule.forget();
+}
+
 NS_IMETHODIMP
 inDOMUtils::GetRuleLine(nsIDOMCSSStyleRule *aRule, uint32_t *_retval)
 {
-  *_retval = 0;
+  ErrorResult rv;
+  nsRefPtr<StyleRule> rule = GetRuleFromDOMRule(aRule, rv);
+  if (rv.Failed()) {
+    return rv.ErrorCode();
+  }
+
+  *_retval = rule->GetLineNumber();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetSelectorCount(nsIDOMCSSStyleRule* aRule, uint32_t *aCount)
+{
+  ErrorResult rv;
+  nsRefPtr<StyleRule> rule = GetRuleFromDOMRule(aRule, rv);
+  if (rv.Failed()) {
+    return rv.ErrorCode();
+  }
 
-  NS_ENSURE_ARG_POINTER(aRule);
+  uint32_t count = 0;
+  for (nsCSSSelectorList* sel = rule->Selector(); sel; sel = sel->mNext) {
+    ++count;
+  }
+  *aCount = count;
+  return NS_OK;
+}
+
+static nsCSSSelectorList*
+GetSelectorAtIndex(nsIDOMCSSStyleRule* aRule, uint32_t aIndex, ErrorResult& rv)
+{
+  nsRefPtr<StyleRule> rule = GetRuleFromDOMRule(aRule, rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+
+  for (nsCSSSelectorList* sel = rule->Selector(); sel;
+       sel = sel->mNext, --aIndex) {
+    if (aIndex == 0) {
+      return sel;
+    }
+  }
+
+  // Ran out of selectors
+  rv.Throw(NS_ERROR_INVALID_ARG);
+  return nullptr;
+}
 
-  nsCOMPtr<nsICSSStyleRuleDOMWrapper> rule = do_QueryInterface(aRule);
-  nsRefPtr<mozilla::css::StyleRule> cssrule;
-  nsresult rv = rule->GetCSSStyleRule(getter_AddRefs(cssrule));
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(cssrule != nullptr, NS_ERROR_FAILURE);
-  *_retval = cssrule->GetLineNumber();
+NS_IMETHODIMP
+inDOMUtils::GetSelectorText(nsIDOMCSSStyleRule* aRule,
+                            uint32_t aSelectorIndex,
+                            nsAString& aText)
+{
+  ErrorResult rv;
+  nsCSSSelectorList* sel = GetSelectorAtIndex(aRule, aSelectorIndex, rv);
+  if (rv.Failed()) {
+    return rv.ErrorCode();
+  }
+
+  nsRefPtr<StyleRule> rule = GetRuleFromDOMRule(aRule, rv);
+  MOZ_ASSERT(!rv.Failed(), "How could we get a selector but not a rule?");
+
+  sel->mSelectors->ToString(aText, rule->GetStyleSheet(), false);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetSpecificity(nsIDOMCSSStyleRule* aRule,
+                            uint32_t aSelectorIndex,
+                            uint64_t* aSpecificity)
+{
+  ErrorResult rv;
+  nsCSSSelectorList* sel = GetSelectorAtIndex(aRule, aSelectorIndex, rv);
+  if (rv.Failed()) {
+    return rv.ErrorCode();
+  }
+
+  *aSpecificity = sel->mWeight;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::SelectorMatchesElement(nsIDOMElement* aElement,
+                                   nsIDOMCSSStyleRule* aRule,
+                                   uint32_t aSelectorIndex,
+                                   bool* aMatches)
+{
+  nsCOMPtr<Element> element = do_QueryInterface(aElement);
+  NS_ENSURE_ARG_POINTER(element);
+
+  ErrorResult rv;
+  nsCSSSelectorList* tail = GetSelectorAtIndex(aRule, aSelectorIndex, rv);
+  if (rv.Failed()) {
+    return rv.ErrorCode();
+  }
+
+  // We want just the one list item, not the whole list tail
+  nsAutoPtr<nsCSSSelectorList> sel(tail->Clone(false));
+
+  element->OwnerDoc()->FlushPendingLinkUpdates();
+  // XXXbz what exactly should we do with visited state here?
+  TreeMatchContext matchingContext(false,
+                                   nsRuleWalker::eRelevantLinkUnvisited,
+                                   element->OwnerDoc(),
+                                   TreeMatchContext::eNeverMatchVisited);
+  *aMatches = nsCSSRuleProcessor::SelectorListMatches(element, matchingContext,
+                                                      sel);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 inDOMUtils::IsInheritedProperty(const nsAString &aPropertyName, bool *_retval)
 {
   nsCSSProperty prop = nsCSSProps::LookupProperty(aPropertyName,
                                                   nsCSSProps::eAny);
--- a/layout/style/StyleRule.h
+++ b/layout/style/StyleRule.h
@@ -215,16 +215,18 @@ private:
 };
 
 /**
  * A selector list is the unit of selectors that each style rule has.
  * For example, "P B, H1 B { ... }" would be a selector list with two
  * items (where each |nsCSSSelectorList| object's |mSelectors| has
  * an |mNext| for the P or H1).  We represent them as linked lists.
  */
+class inDOMUtils;
+
 struct nsCSSSelectorList {
   nsCSSSelectorList(void);
   ~nsCSSSelectorList(void);
 
   /**
    * Create a new selector and push it onto the beginning of |mSelectors|,
    * setting its |mNext| to the current value of |mSelectors|.  If there is an
    * earlier selector, set its |mOperator| to |aOperator|; else |aOperator|
@@ -245,19 +247,21 @@ struct nsCSSSelectorList {
    */
   nsCSSSelectorList* Clone() const { return Clone(true); }
 
   size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
 
   nsCSSSelector*     mSelectors;
   int32_t            mWeight;
   nsCSSSelectorList* mNext;
-private: 
+protected:
+  friend class inDOMUtils;
   nsCSSSelectorList* Clone(bool aDeep) const;
 
+private:
   nsCSSSelectorList(const nsCSSSelectorList& aCopy) MOZ_DELETE;
   nsCSSSelectorList& operator=(const nsCSSSelectorList& aCopy) MOZ_DELETE;
 };
 
 // 464bab7a-2fce-4f30-ab44-b7a5f3aae57d
 #define NS_CSS_STYLE_RULE_IMPL_CID \
 { 0x464bab7a, 0x2fce, 0x4f30, \
   { 0xab, 0x44, 0xb7, 0xa5, 0xf3, 0xaa, 0xe5, 0x7d } }