diff --git a/layout/inspector/public/inIDOMUtils.idl b/layout/inspector/public/inIDOMUtils.idl index 6d6bd852b40b..c5592cda86aa 100644 --- a/layout/inspector/public/inIDOMUtils.idl +++ b/layout/inspector/public/inIDOMUtils.idl @@ -16,13 +16,31 @@ 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); diff --git a/layout/inspector/src/inDOMUtils.cpp b/layout/inspector/src/inDOMUtils.cpp index 8c220ca1746f..9b33a16e659e 100644 --- a/layout/inspector/src/inDOMUtils.cpp +++ b/layout/inspector/src/inDOMUtils.cpp @@ -27,7 +27,13 @@ #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; /////////////////////////////////////////////////////////////////////////////// @@ -188,19 +194,136 @@ inDOMUtils::GetCSSStyleRules(nsIDOMElement *aElement, return NS_OK; } +static already_AddRefed +GetRuleFromDOMRule(nsIDOMCSSStyleRule *aRule, ErrorResult& rv) +{ + nsCOMPtr rule = do_QueryInterface(aRule); + if (!rule) { + rv.Throw(NS_ERROR_INVALID_ARG); + return nullptr; + } + + nsRefPtr 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 rule = GetRuleFromDOMRule(aRule, rv); + if (rv.Failed()) { + return rv.ErrorCode(); + } - NS_ENSURE_ARG_POINTER(aRule); + *_retval = rule->GetLineNumber(); + return NS_OK; +} - nsCOMPtr rule = do_QueryInterface(aRule); - nsRefPtr 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::GetSelectorCount(nsIDOMCSSStyleRule* aRule, uint32_t *aCount) +{ + ErrorResult rv; + nsRefPtr rule = GetRuleFromDOMRule(aRule, rv); + if (rv.Failed()) { + return rv.ErrorCode(); + } + + 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 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; +} + +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 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 = 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 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; } diff --git a/layout/style/StyleRule.h b/layout/style/StyleRule.h index 94b6ca237a3d..6f8563c22ea9 100644 --- a/layout/style/StyleRule.h +++ b/layout/style/StyleRule.h @@ -220,6 +220,8 @@ private: * 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); @@ -250,9 +252,11 @@ struct nsCSSSelectorList { 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; };