diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /layout/inspector/inDOMUtils.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/inspector/inDOMUtils.cpp')
-rw-r--r-- | layout/inspector/inDOMUtils.cpp | 1351 |
1 files changed, 1351 insertions, 0 deletions
diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp new file mode 100644 index 000000000..9f1dcaad3 --- /dev/null +++ b/layout/inspector/inDOMUtils.cpp @@ -0,0 +1,1351 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/EventStates.h" + +#include "inDOMUtils.h" +#include "inLayoutUtils.h" + +#include "nsArray.h" +#include "nsAutoPtr.h" +#include "nsIServiceManager.h" +#include "nsString.h" +#include "nsIStyleSheetLinkingElement.h" +#include "nsIContentInlines.h" +#include "nsIDOMElement.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +#include "nsIDOMDocument.h" +#include "nsIDOMCharacterData.h" +#include "nsRuleNode.h" +#include "nsIStyleRule.h" +#include "mozilla/css/StyleRule.h" +#include "nsICSSStyleRuleDOMWrapper.h" +#include "nsIDOMWindow.h" +#include "nsXBLBinding.h" +#include "nsXBLPrototypeBinding.h" +#include "nsIMutableArray.h" +#include "nsBindingManager.h" +#include "ChildIterator.h" +#include "nsComputedDOMStyle.h" +#include "mozilla/EventStateManager.h" +#include "nsIAtom.h" +#include "nsRange.h" +#include "nsContentList.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/dom/Element.h" +#include "nsRuleWalker.h" +#include "nsRuleProcessorData.h" +#include "nsCSSPseudoClasses.h" +#include "nsCSSRuleProcessor.h" +#include "mozilla/dom/CSSLexer.h" +#include "mozilla/dom/InspectorUtilsBinding.h" +#include "mozilla/dom/ToJSValue.h" +#include "nsCSSParser.h" +#include "nsCSSProps.h" +#include "nsCSSValue.h" +#include "nsColor.h" +#include "nsStyleSet.h" +#include "nsStyleUtil.h" +#include "nsQueryObject.h" + +using namespace mozilla; +using namespace mozilla::css; +using namespace mozilla::dom; + +/////////////////////////////////////////////////////////////////////////////// + +inDOMUtils::inDOMUtils() +{ +} + +inDOMUtils::~inDOMUtils() +{ +} + +NS_IMPL_ISUPPORTS(inDOMUtils, inIDOMUtils) + +/////////////////////////////////////////////////////////////////////////////// +// inIDOMUtils + +NS_IMETHODIMP +inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength, + nsISupports ***aSheets) +{ + NS_ENSURE_ARG_POINTER(aDocument); + + nsTArray<RefPtr<CSSStyleSheet>> sheets; + + nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument); + MOZ_ASSERT(document); + + // Get the agent, then user and finally xbl sheets in the style set. + nsIPresShell* presShell = document->GetShell(); + + if (presShell && presShell->StyleSet()->IsServo()) { + // XXXheycam ServoStyleSets don't have the ability to expose their + // sheets in a script-accessible way yet. + NS_ERROR("stylo: ServoStyleSets cannot expose their sheets to script yet"); + return NS_ERROR_FAILURE; + } + + if (presShell) { + nsStyleSet* styleSet = presShell->StyleSet()->AsGecko(); + SheetType sheetType = SheetType::Agent; + for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) { + sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i)); + } + sheetType = SheetType::User; + for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) { + sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i)); + } + AutoTArray<CSSStyleSheet*, 32> xblSheetArray; + styleSet->AppendAllXBLStyleSheets(xblSheetArray); + + // The XBL stylesheet array will quite often be full of duplicates. Cope: + nsTHashtable<nsPtrHashKey<CSSStyleSheet>> sheetSet; + for (CSSStyleSheet* sheet : xblSheetArray) { + if (!sheetSet.Contains(sheet)) { + sheetSet.PutEntry(sheet); + sheets.AppendElement(sheet); + } + } + } + + // Get the document sheets. + for (int32_t i = 0; i < document->GetNumberOfStyleSheets(); i++) { + // XXXheycam ServoStyleSets don't have the ability to expose their + // sheets in a script-accessible way yet. + sheets.AppendElement(document->GetStyleSheetAt(i)->AsGecko()); + } + + nsISupports** ret = static_cast<nsISupports**>(moz_xmalloc(sheets.Length() * + sizeof(nsISupports*))); + + for (size_t i = 0; i < sheets.Length(); i++) { + NS_ADDREF(ret[i] = NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, sheets[i])); + } + + *aLength = sheets.Length(); + *aSheets = ret; + + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::IsIgnorableWhitespace(nsIDOMCharacterData *aDataNode, + bool *aReturn) +{ + NS_PRECONDITION(aReturn, "Must have an out parameter"); + + NS_ENSURE_ARG_POINTER(aDataNode); + + *aReturn = false; + + nsCOMPtr<nsIContent> content = do_QueryInterface(aDataNode); + NS_ASSERTION(content, "Does not implement nsIContent!"); + + if (!content->TextIsOnlyWhitespace()) { + return NS_OK; + } + + // Okay. We have only white space. Let's check the white-space + // property now and make sure that this isn't preformatted text... + nsIFrame* frame = content->GetPrimaryFrame(); + if (frame) { + const nsStyleText* text = frame->StyleText(); + *aReturn = !text->WhiteSpaceIsSignificant(); + } + else { + // empty inter-tag text node without frame, e.g., in between <table>\n<tr> + *aReturn = true; + } + + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetParentForNode(nsIDOMNode* aNode, + bool aShowingAnonymousContent, + nsIDOMNode** aParent) +{ + NS_ENSURE_ARG_POINTER(aNode); + + // First do the special cases -- document nodes and anonymous content + nsCOMPtr<nsIDocument> doc(do_QueryInterface(aNode)); + nsCOMPtr<nsIDOMNode> parent; + + if (doc) { + parent = inLayoutUtils::GetContainerFor(*doc); + } else if (aShowingAnonymousContent) { + nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); + if (content) { + nsIContent* bparent = content->GetFlattenedTreeParent(); + parent = do_QueryInterface(bparent); + } + } + + if (!parent) { + // Ok, just get the normal DOM parent node + aNode->GetParentNode(getter_AddRefs(parent)); + } + + NS_IF_ADDREF(*aParent = parent); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetChildrenForNode(nsIDOMNode* aNode, + bool aShowingAnonymousContent, + nsIDOMNodeList** aChildren) +{ + NS_ENSURE_ARG_POINTER(aNode); + NS_PRECONDITION(aChildren, "Must have an out parameter"); + + nsCOMPtr<nsIDOMNodeList> kids; + + if (aShowingAnonymousContent) { + nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); + if (content) { + kids = content->GetChildren(nsIContent::eAllChildren); + } + } + + if (!kids) { + aNode->GetChildNodes(getter_AddRefs(kids)); + } + + kids.forget(aChildren); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetCSSStyleRules(nsIDOMElement *aElement, + const nsAString& aPseudo, + nsIArrayExtensions **_retval) +{ + NS_ENSURE_ARG_POINTER(aElement); + + *_retval = nullptr; + + nsCOMPtr<nsIAtom> pseudoElt; + if (!aPseudo.IsEmpty()) { + pseudoElt = NS_Atomize(aPseudo); + } + + nsRuleNode* ruleNode = nullptr; + nsCOMPtr<Element> element = do_QueryInterface(aElement); + NS_ENSURE_STATE(element); + RefPtr<nsStyleContext> styleContext; + GetRuleNodeForElement(element, pseudoElt, getter_AddRefs(styleContext), &ruleNode); + if (!ruleNode) { + // This can fail for elements that are not in the document or + // if the document they're in doesn't have a presshell. Bail out. + return NS_OK; + } + + AutoTArray<nsRuleNode*, 16> ruleNodes; + while (!ruleNode->IsRoot()) { + ruleNodes.AppendElement(ruleNode); + ruleNode = ruleNode->GetParent(); + } + + nsCOMPtr<nsIMutableArray> rules = nsArray::Create(); + for (nsRuleNode* ruleNode : Reversed(ruleNodes)) { + RefPtr<Declaration> decl = do_QueryObject(ruleNode->GetRule()); + if (decl) { + RefPtr<mozilla::css::StyleRule> styleRule = + do_QueryObject(decl->GetOwningRule()); + if (styleRule) { + nsCOMPtr<nsIDOMCSSRule> domRule = styleRule->GetDOMRule(); + if (domRule) { + rules->AppendElement(domRule, /*weak =*/ false); + } + } + } + } + + rules.forget(_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_POINTER); + return nullptr; + } + + RefPtr<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(nsIDOMCSSRule* aRule, uint32_t* _retval) +{ + NS_ENSURE_ARG_POINTER(aRule); + + Rule* rule = aRule->GetCSSRule(); + if (!rule) { + return NS_ERROR_FAILURE; + } + + *_retval = rule->GetLineNumber(); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetRuleColumn(nsIDOMCSSRule* aRule, uint32_t* _retval) +{ + NS_ENSURE_ARG_POINTER(aRule); + + Rule* rule = aRule->GetCSSRule(); + if (!rule) { + return NS_ERROR_FAILURE; + } + + *_retval = rule->GetColumnNumber(); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetRelativeRuleLine(nsIDOMCSSRule* aRule, uint32_t* _retval) +{ + NS_ENSURE_ARG_POINTER(aRule); + + Rule* rule = aRule->GetCSSRule(); + if (!rule) { + return NS_ERROR_FAILURE; + } + + uint32_t lineNumber = rule->GetLineNumber(); + CSSStyleSheet* sheet = rule->GetStyleSheet(); + if (sheet && lineNumber != 0) { + nsINode* owningNode = sheet->GetOwnerNode(); + if (owningNode) { + nsCOMPtr<nsIStyleSheetLinkingElement> link = + do_QueryInterface(owningNode); + if (link) { + lineNumber -= link->GetLineNumber() - 1; + } + } + } + + *_retval = lineNumber; + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetCSSLexer(const nsAString& aText, JSContext* aCx, + JS::MutableHandleValue aResult) +{ + MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx)); + JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx)); + nsAutoPtr<CSSLexer> lexer(new CSSLexer(aText)); + if (!WrapNewBindingNonWrapperCachedObject(aCx, scope, lexer, aResult)) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetSelectorCount(nsIDOMCSSStyleRule* aRule, uint32_t *aCount) +{ + ErrorResult rv; + RefPtr<StyleRule> rule = GetRuleFromDOMRule(aRule, rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + 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) +{ + RefPtr<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; +} + +NS_IMETHODIMP +inDOMUtils::GetSelectorText(nsIDOMCSSStyleRule* aRule, + uint32_t aSelectorIndex, + nsAString& aText) +{ + ErrorResult rv; + nsCSSSelectorList* sel = GetSelectorAtIndex(aRule, aSelectorIndex, rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + RefPtr<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.StealNSResult(); + } + + *aSpecificity = sel->mWeight; + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::SelectorMatchesElement(nsIDOMElement* aElement, + nsIDOMCSSStyleRule* aRule, + uint32_t aSelectorIndex, + const nsAString& aPseudo, + 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.StealNSResult(); + } + + // We want just the one list item, not the whole list tail + nsAutoPtr<nsCSSSelectorList> sel(tail->Clone(false)); + + // Do not attempt to match if a pseudo element is requested and this is not + // a pseudo element selector, or vice versa. + if (aPseudo.IsEmpty() == sel->mSelectors->IsPseudoElement()) { + *aMatches = false; + return NS_OK; + } + + if (!aPseudo.IsEmpty()) { + // We need to make sure that the requested pseudo element type + // matches the selector pseudo element type before proceeding. + nsCOMPtr<nsIAtom> pseudoElt = NS_Atomize(aPseudo); + if (sel->mSelectors->PseudoType() != nsCSSPseudoElements:: + GetPseudoType(pseudoElt, CSSEnabledState::eIgnoreEnabledState)) { + *aMatches = false; + return NS_OK; + } + + // We have a matching pseudo element, now remove it so we can compare + // directly against |element| when proceeding into SelectorListMatches. + // It's OK to do this - we just cloned sel and nothing else is using it. + sel->RemoveRightmostSelector(); + } + + 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) +{ + nsCSSPropertyID prop = nsCSSProps:: + LookupProperty(aPropertyName, CSSEnabledState::eIgnoreEnabledState); + if (prop == eCSSProperty_UNKNOWN) { + *_retval = false; + return NS_OK; + } + + if (prop == eCSSPropertyExtra_variable) { + *_retval = true; + return NS_OK; + } + + if (nsCSSProps::IsShorthand(prop)) { + prop = nsCSSProps::SubpropertyEntryFor(prop)[0]; + } + + nsStyleStructID sid = nsCSSProps::kSIDTable[prop]; + *_retval = !nsCachedStyleData::IsReset(sid); + return NS_OK; +} + +extern const char* const kCSSRawProperties[]; + +NS_IMETHODIMP +inDOMUtils::GetCSSPropertyNames(uint32_t aFlags, uint32_t* aCount, + char16_t*** aProps) +{ + // maxCount is the largest number of properties we could have; our actual + // number might be smaller because properties might be disabled. + uint32_t maxCount; + if (aFlags & EXCLUDE_SHORTHANDS) { + maxCount = eCSSProperty_COUNT_no_shorthands; + } else { + maxCount = eCSSProperty_COUNT; + } + + if (aFlags & INCLUDE_ALIASES) { + maxCount += (eCSSProperty_COUNT_with_aliases - eCSSProperty_COUNT); + } + + char16_t** props = + static_cast<char16_t**>(moz_xmalloc(maxCount * sizeof(char16_t*))); + +#define DO_PROP(_prop) \ + PR_BEGIN_MACRO \ + nsCSSPropertyID cssProp = nsCSSPropertyID(_prop); \ + if (nsCSSProps::IsEnabled(cssProp, CSSEnabledState::eForAllContent)) { \ + props[propCount] = \ + ToNewUnicode(nsDependentCString(kCSSRawProperties[_prop])); \ + ++propCount; \ + } \ + PR_END_MACRO + + // prop is the property id we're considering; propCount is how many properties + // we've put into props so far. + uint32_t prop = 0, propCount = 0; + for ( ; prop < eCSSProperty_COUNT_no_shorthands; ++prop) { + if (nsCSSProps::PropertyParseType(nsCSSPropertyID(prop)) != + CSS_PROPERTY_PARSE_INACCESSIBLE) { + DO_PROP(prop); + } + } + + if (!(aFlags & EXCLUDE_SHORTHANDS)) { + for ( ; prop < eCSSProperty_COUNT; ++prop) { + // Some shorthands are also aliases + if ((aFlags & INCLUDE_ALIASES) || + !nsCSSProps::PropHasFlags(nsCSSPropertyID(prop), + CSS_PROPERTY_IS_ALIAS)) { + DO_PROP(prop); + } + } + } + + if (aFlags & INCLUDE_ALIASES) { + for (prop = eCSSProperty_COUNT; prop < eCSSProperty_COUNT_with_aliases; ++prop) { + DO_PROP(prop); + } + } + +#undef DO_PROP + + *aCount = propCount; + *aProps = props; + + return NS_OK; +} + +static void InsertNoDuplicates(nsTArray<nsString>& aArray, + const nsAString& aString) +{ + size_t i = aArray.IndexOfFirstElementGt(aString); + if (i > 0 && aArray[i-1].Equals(aString)) { + return; + } + aArray.InsertElementAt(i, aString); +} + +static void GetKeywordsForProperty(const nsCSSPropertyID aProperty, + nsTArray<nsString>& aArray) +{ + if (nsCSSProps::IsShorthand(aProperty)) { + // Shorthand props have no keywords. + return; + } + const nsCSSProps::KTableEntry* keywordTable = + nsCSSProps::kKeywordTableTable[aProperty]; + if (keywordTable) { + for (size_t i = 0; keywordTable[i].mKeyword != eCSSKeyword_UNKNOWN; ++i) { + nsCSSKeyword word = keywordTable[i].mKeyword; + InsertNoDuplicates(aArray, + NS_ConvertASCIItoUTF16(nsCSSKeywords::GetStringValue(word))); + } + } +} + +static void GetColorsForProperty(const uint32_t aParserVariant, + nsTArray<nsString>& aArray) +{ + if (aParserVariant & VARIANT_COLOR) { + // GetKeywordsForProperty and GetOtherValuesForProperty assume aArray is sorted, + // and if aArray is not empty here, then it's not going to be sorted coming out. + MOZ_ASSERT(aArray.Length() == 0); + size_t size; + const char * const *allColorNames = NS_AllColorNames(&size); + nsString* utf16Names = aArray.AppendElements(size); + for (size_t i = 0; i < size; i++) { + CopyASCIItoUTF16(allColorNames[i], utf16Names[i]); + } + InsertNoDuplicates(aArray, NS_LITERAL_STRING("currentColor")); + } + return; +} + +static void GetOtherValuesForProperty(const uint32_t aParserVariant, + nsTArray<nsString>& aArray) +{ + if (aParserVariant & VARIANT_AUTO) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("auto")); + } + if (aParserVariant & VARIANT_NORMAL) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("normal")); + } + if(aParserVariant & VARIANT_ALL) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("all")); + } + if (aParserVariant & VARIANT_NONE) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("none")); + } + if (aParserVariant & VARIANT_ELEMENT) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-element")); + } + if (aParserVariant & VARIANT_IMAGE_RECT) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-image-rect")); + } + if (aParserVariant & VARIANT_COLOR) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("rgb")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("hsl")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("rgba")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("hsla")); + } + if (aParserVariant & VARIANT_TIMING_FUNCTION) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("cubic-bezier")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("steps")); + } + if (aParserVariant & VARIANT_CALC) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("calc")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-calc")); + } + if (aParserVariant & VARIANT_URL) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("url")); + } + if (aParserVariant & VARIANT_GRADIENT) { + InsertNoDuplicates(aArray, NS_LITERAL_STRING("linear-gradient")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("radial-gradient")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("repeating-linear-gradient")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("repeating-radial-gradient")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-linear-gradient")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-radial-gradient")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-repeating-linear-gradient")); + InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-repeating-radial-gradient")); + } +} + +NS_IMETHODIMP +inDOMUtils::GetSubpropertiesForCSSProperty(const nsAString& aProperty, + uint32_t* aLength, + char16_t*** aValues) +{ + nsCSSPropertyID propertyID = + nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent); + + if (propertyID == eCSSProperty_UNKNOWN) { + return NS_ERROR_FAILURE; + } + + if (propertyID == eCSSPropertyExtra_variable) { + *aValues = static_cast<char16_t**>(moz_xmalloc(sizeof(char16_t*))); + (*aValues)[0] = ToNewUnicode(aProperty); + *aLength = 1; + return NS_OK; + } + + if (!nsCSSProps::IsShorthand(propertyID)) { + *aValues = static_cast<char16_t**>(moz_xmalloc(sizeof(char16_t*))); + (*aValues)[0] = ToNewUnicode(nsCSSProps::GetStringValue(propertyID)); + *aLength = 1; + return NS_OK; + } + + // Count up how many subproperties we have. + size_t subpropCount = 0; + for (const nsCSSPropertyID *props = nsCSSProps::SubpropertyEntryFor(propertyID); + *props != eCSSProperty_UNKNOWN; ++props) { + ++subpropCount; + } + + *aValues = + static_cast<char16_t**>(moz_xmalloc(subpropCount * sizeof(char16_t*))); + *aLength = subpropCount; + for (const nsCSSPropertyID *props = nsCSSProps::SubpropertyEntryFor(propertyID), + *props_start = props; + *props != eCSSProperty_UNKNOWN; ++props) { + (*aValues)[props-props_start] = ToNewUnicode(nsCSSProps::GetStringValue(*props)); + } + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::CssPropertyIsShorthand(const nsAString& aProperty, bool *_retval) +{ + nsCSSPropertyID propertyID = + nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent); + if (propertyID == eCSSProperty_UNKNOWN) { + return NS_ERROR_FAILURE; + } + + if (propertyID == eCSSPropertyExtra_variable) { + *_retval = false; + } else { + *_retval = nsCSSProps::IsShorthand(propertyID); + } + return NS_OK; +} + +// A helper function that determines whether the given property +// supports the given type. +static bool +PropertySupportsVariant(nsCSSPropertyID aPropertyID, uint32_t aVariant) +{ + if (nsCSSProps::IsShorthand(aPropertyID)) { + // We need a special case for border here, because while it resets + // border-image, it can't actually parse an image. + if (aPropertyID == eCSSProperty_border) { + return (aVariant & (VARIANT_COLOR | VARIANT_LENGTH)) != 0; + } + + for (const nsCSSPropertyID* props = nsCSSProps::SubpropertyEntryFor(aPropertyID); + *props != eCSSProperty_UNKNOWN; ++props) { + if (PropertySupportsVariant(*props, aVariant)) { + return true; + } + } + return false; + } + + // Properties that are parsed by functions must have their + // attributes hand-maintained here. + if (nsCSSProps::PropHasFlags(aPropertyID, CSS_PROPERTY_VALUE_PARSER_FUNCTION) || + nsCSSProps::PropertyParseType(aPropertyID) == CSS_PROPERTY_PARSE_FUNCTION) { + // These must all be special-cased. + uint32_t supported; + switch (aPropertyID) { + case eCSSProperty_border_image_slice: + case eCSSProperty_grid_template: + case eCSSProperty_grid: + supported = VARIANT_PN; + break; + + case eCSSProperty_border_image_outset: + supported = VARIANT_LN; + break; + + case eCSSProperty_border_image_width: + case eCSSProperty_stroke_dasharray: + supported = VARIANT_LPN; + break; + + case eCSSProperty_border_top_left_radius: + case eCSSProperty_border_top_right_radius: + case eCSSProperty_border_bottom_left_radius: + case eCSSProperty_border_bottom_right_radius: + case eCSSProperty_background_position: + case eCSSProperty_background_position_x: + case eCSSProperty_background_position_y: + case eCSSProperty_background_size: +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND + case eCSSProperty_mask_position: + case eCSSProperty_mask_position_x: + case eCSSProperty_mask_position_y: + case eCSSProperty_mask_size: +#endif + case eCSSProperty_grid_auto_columns: + case eCSSProperty_grid_auto_rows: + case eCSSProperty_grid_template_columns: + case eCSSProperty_grid_template_rows: + case eCSSProperty_object_position: + case eCSSProperty_scroll_snap_coordinate: + case eCSSProperty_scroll_snap_destination: + case eCSSProperty_transform_origin: + case eCSSProperty_perspective_origin: + case eCSSProperty__moz_outline_radius_topLeft: + case eCSSProperty__moz_outline_radius_topRight: + case eCSSProperty__moz_outline_radius_bottomLeft: + case eCSSProperty__moz_outline_radius_bottomRight: + supported = VARIANT_LP; + break; + + case eCSSProperty_border_bottom_colors: + case eCSSProperty_border_left_colors: + case eCSSProperty_border_right_colors: + case eCSSProperty_border_top_colors: + supported = VARIANT_COLOR; + break; + + case eCSSProperty_text_shadow: + case eCSSProperty_box_shadow: + supported = VARIANT_LENGTH | VARIANT_COLOR; + break; + + case eCSSProperty_border_spacing: + supported = VARIANT_LENGTH; + break; + + case eCSSProperty_content: + case eCSSProperty_cursor: + case eCSSProperty_clip_path: + case eCSSProperty_shape_outside: + supported = VARIANT_URL; + break; + + case eCSSProperty_fill: + case eCSSProperty_stroke: + supported = VARIANT_COLOR | VARIANT_URL; + break; + + case eCSSProperty_image_orientation: + supported = VARIANT_ANGLE; + break; + + case eCSSProperty_filter: + supported = VARIANT_URL; + break; + + case eCSSProperty_grid_column_start: + case eCSSProperty_grid_column_end: + case eCSSProperty_grid_row_start: + case eCSSProperty_grid_row_end: + case eCSSProperty_font_weight: + case eCSSProperty_initial_letter: + supported = VARIANT_NUMBER; + break; + + default: + supported = 0; + break; + } + + return (supported & aVariant) != 0; + } + + return (nsCSSProps::ParserVariant(aPropertyID) & aVariant) != 0; +} + +NS_IMETHODIMP +inDOMUtils::CssPropertySupportsType(const nsAString& aProperty, uint32_t aType, + bool *_retval) +{ + nsCSSPropertyID propertyID = + nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent); + if (propertyID == eCSSProperty_UNKNOWN) { + return NS_ERROR_FAILURE; + } + + if (propertyID >= eCSSProperty_COUNT) { + *_retval = false; + return NS_OK; + } + + uint32_t variant; + switch (aType) { + case TYPE_LENGTH: + variant = VARIANT_LENGTH; + break; + case TYPE_PERCENTAGE: + variant = VARIANT_PERCENT; + break; + case TYPE_COLOR: + variant = VARIANT_COLOR; + break; + case TYPE_URL: + variant = VARIANT_URL; + break; + case TYPE_ANGLE: + variant = VARIANT_ANGLE; + break; + case TYPE_FREQUENCY: + variant = VARIANT_FREQUENCY; + break; + case TYPE_TIME: + variant = VARIANT_TIME; + break; + case TYPE_GRADIENT: + variant = VARIANT_GRADIENT; + break; + case TYPE_TIMING_FUNCTION: + variant = VARIANT_TIMING_FUNCTION; + break; + case TYPE_IMAGE_RECT: + variant = VARIANT_IMAGE_RECT; + break; + case TYPE_NUMBER: + // Include integers under "number"? + variant = VARIANT_NUMBER | VARIANT_INTEGER; + break; + default: + // Unknown type + return NS_ERROR_NOT_AVAILABLE; + } + + *_retval = PropertySupportsVariant(propertyID, variant); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetCSSValuesForProperty(const nsAString& aProperty, + uint32_t* aLength, + char16_t*** aValues) +{ + nsCSSPropertyID propertyID = nsCSSProps:: + LookupProperty(aProperty, CSSEnabledState::eForAllContent); + if (propertyID == eCSSProperty_UNKNOWN) { + return NS_ERROR_FAILURE; + } + + nsTArray<nsString> array; + // We start collecting the values, BUT colors need to go in first, because array + // needs to stay sorted, and the colors are sorted, so we just append them. + if (propertyID == eCSSPropertyExtra_variable) { + // No other values we can report. + } else if (!nsCSSProps::IsShorthand(propertyID)) { + // Property is longhand. + uint32_t propertyParserVariant = nsCSSProps::ParserVariant(propertyID); + // Get colors first. + GetColorsForProperty(propertyParserVariant, array); + if (propertyParserVariant & VARIANT_KEYWORD) { + GetKeywordsForProperty(propertyID, array); + } + GetOtherValuesForProperty(propertyParserVariant, array); + } else { + // Property is shorthand. + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subproperty, propertyID, + CSSEnabledState::eForAllContent) { + // Get colors (once) first. + uint32_t propertyParserVariant = nsCSSProps::ParserVariant(*subproperty); + if (propertyParserVariant & VARIANT_COLOR) { + GetColorsForProperty(propertyParserVariant, array); + break; + } + } + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subproperty, propertyID, + CSSEnabledState::eForAllContent) { + uint32_t propertyParserVariant = nsCSSProps::ParserVariant(*subproperty); + if (propertyParserVariant & VARIANT_KEYWORD) { + GetKeywordsForProperty(*subproperty, array); + } + GetOtherValuesForProperty(propertyParserVariant, array); + } + } + // All CSS properties take initial, inherit and unset. + InsertNoDuplicates(array, NS_LITERAL_STRING("initial")); + InsertNoDuplicates(array, NS_LITERAL_STRING("inherit")); + InsertNoDuplicates(array, NS_LITERAL_STRING("unset")); + + *aLength = array.Length(); + char16_t** ret = + static_cast<char16_t**>(moz_xmalloc(*aLength * sizeof(char16_t*))); + for (uint32_t i = 0; i < *aLength; ++i) { + ret[i] = ToNewUnicode(array[i]); + } + *aValues = ret; + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::ColorNameToRGB(const nsAString& aColorName, JSContext* aCx, + JS::MutableHandle<JS::Value> aValue) +{ + nscolor color; + if (!NS_ColorNameToRGB(aColorName, &color)) { + return NS_ERROR_INVALID_ARG; + } + + InspectorRGBTriple triple; + triple.mR = NS_GET_R(color); + triple.mG = NS_GET_G(color); + triple.mB = NS_GET_B(color); + + if (!ToJSValue(aCx, triple, aValue)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::RgbToColorName(uint8_t aR, uint8_t aG, uint8_t aB, + nsAString& aColorName) +{ + const char* color = NS_RGBToColorName(NS_RGB(aR, aG, aB)); + if (!color) { + aColorName.Truncate(); + return NS_ERROR_INVALID_ARG; + } + + aColorName.AssignASCII(color); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::ColorToRGBA(const nsAString& aColorString, JSContext* aCx, + JS::MutableHandle<JS::Value> aValue) +{ + nscolor color = 0; + nsCSSParser cssParser; + nsCSSValue cssValue; + + bool isColor = cssParser.ParseColorString(aColorString, nullptr, 0, + cssValue, true); + + if (!isColor) { + aValue.setNull(); + return NS_OK; + } + + nsRuleNode::ComputeColor(cssValue, nullptr, nullptr, color); + + InspectorRGBATuple tuple; + tuple.mR = NS_GET_R(color); + tuple.mG = NS_GET_G(color); + tuple.mB = NS_GET_B(color); + tuple.mA = nsStyleUtil::ColorComponentToFloat(NS_GET_A(color)); + + if (!ToJSValue(aCx, tuple, aValue)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::IsValidCSSColor(const nsAString& aColorString, bool *_retval) +{ + nsCSSParser cssParser; + nsCSSValue cssValue; + *_retval = cssParser.ParseColorString(aColorString, nullptr, 0, cssValue, true); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::CssPropertyIsValid(const nsAString& aPropertyName, + const nsAString& aPropertyValue, + bool *_retval) +{ + nsCSSPropertyID propertyID = nsCSSProps:: + LookupProperty(aPropertyName, CSSEnabledState::eIgnoreEnabledState); + + if (propertyID == eCSSProperty_UNKNOWN) { + *_retval = false; + return NS_OK; + } + + if (propertyID == eCSSPropertyExtra_variable) { + *_retval = true; + return NS_OK; + } + + // Get a parser, parse the property. + nsCSSParser parser; + *_retval = parser.IsValueValidForProperty(propertyID, aPropertyValue); + + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetBindingURLs(nsIDOMElement *aElement, nsIArray **_retval) +{ + NS_ENSURE_ARG_POINTER(aElement); + + *_retval = nullptr; + + nsCOMPtr<nsIMutableArray> urls = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (!urls) + return NS_ERROR_FAILURE; + + nsCOMPtr<nsIContent> content = do_QueryInterface(aElement); + NS_ENSURE_ARG_POINTER(content); + + nsXBLBinding *binding = content->GetXBLBinding(); + + while (binding) { + urls->AppendElement(binding->PrototypeBinding()->BindingURI(), false); + binding = binding->GetBaseBinding(); + } + + urls.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::SetContentState(nsIDOMElement* aElement, + EventStates::InternalType aState, + bool* aRetVal) +{ + NS_ENSURE_ARG_POINTER(aElement); + + RefPtr<EventStateManager> esm = + inLayoutUtils::GetEventStateManagerFor(aElement); + NS_ENSURE_TRUE(esm, NS_ERROR_INVALID_ARG); + + nsCOMPtr<nsIContent> content; + content = do_QueryInterface(aElement); + NS_ENSURE_TRUE(content, NS_ERROR_INVALID_ARG); + + *aRetVal = esm->SetContentState(content, EventStates(aState)); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::RemoveContentState(nsIDOMElement* aElement, + EventStates::InternalType aState, + bool* aRetVal) +{ + NS_ENSURE_ARG_POINTER(aElement); + + RefPtr<EventStateManager> esm = + inLayoutUtils::GetEventStateManagerFor(aElement); + NS_ENSURE_TRUE(esm, NS_ERROR_INVALID_ARG); + + *aRetVal = esm->SetContentState(nullptr, EventStates(aState)); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetContentState(nsIDOMElement* aElement, + EventStates::InternalType* aState) +{ + *aState = 0; + nsCOMPtr<nsIContent> content = do_QueryInterface(aElement); + NS_ENSURE_ARG_POINTER(content); + + // NOTE: if this method is removed, + // please remove GetInternalValue from EventStates + *aState = content->AsElement()->State().GetInternalValue(); + return NS_OK; +} + +/* static */ nsresult +inDOMUtils::GetRuleNodeForElement(dom::Element* aElement, + nsIAtom* aPseudo, + nsStyleContext** aStyleContext, + nsRuleNode** aRuleNode) +{ + MOZ_ASSERT(aElement); + + *aRuleNode = nullptr; + *aStyleContext = nullptr; + + nsIDocument* doc = aElement->GetComposedDoc(); + NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); + + nsIPresShell *presShell = doc->GetShell(); + NS_ENSURE_TRUE(presShell, NS_ERROR_UNEXPECTED); + + nsPresContext *presContext = presShell->GetPresContext(); + NS_ENSURE_TRUE(presContext, NS_ERROR_UNEXPECTED); + + presContext->EnsureSafeToHandOutCSSRules(); + + RefPtr<nsStyleContext> sContext = + nsComputedDOMStyle::GetStyleContextForElement(aElement, aPseudo, presShell); + if (sContext) { + *aRuleNode = sContext->RuleNode(); + sContext.forget(aStyleContext); + } + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::GetUsedFontFaces(nsIDOMRange* aRange, + nsIDOMFontFaceList** aFontFaceList) +{ + return static_cast<nsRange*>(aRange)->GetUsedFontFaces(aFontFaceList); +} + +static EventStates +GetStatesForPseudoClass(const nsAString& aStatePseudo) +{ + // An array of the states that are relevant for various pseudoclasses. + // XXXbz this duplicates code in nsCSSRuleProcessor + static const EventStates sPseudoClassStates[] = { +#define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \ + EventStates(), +#define CSS_STATE_PSEUDO_CLASS(_name, _value, _flags, _pref, _states) \ + _states, +#include "nsCSSPseudoClassList.h" +#undef CSS_STATE_PSEUDO_CLASS +#undef CSS_PSEUDO_CLASS + + // Add more entries for our fake values to make sure we can't + // index out of bounds into this array no matter what. + EventStates(), + EventStates() + }; + static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) == + static_cast<size_t>(CSSPseudoClassType::MAX), + "Length of PseudoClassStates array is incorrect"); + + nsCOMPtr<nsIAtom> atom = NS_Atomize(aStatePseudo); + CSSPseudoClassType type = nsCSSPseudoClasses:: + GetPseudoType(atom, CSSEnabledState::eIgnoreEnabledState); + + // Ignore :any-link so we don't give the element simultaneous + // visited and unvisited style state + if (type == CSSPseudoClassType::anyLink || + type == CSSPseudoClassType::mozAnyLink) { + return EventStates(); + } + // Our array above is long enough that indexing into it with + // NotPseudo is ok. + return sPseudoClassStates[static_cast<CSSPseudoClassTypeBase>(type)]; +} + +NS_IMETHODIMP +inDOMUtils::GetCSSPseudoElementNames(uint32_t* aLength, char16_t*** aNames) +{ + nsTArray<nsIAtom*> array; + + const CSSPseudoElementTypeBase pseudoCount = + static_cast<CSSPseudoElementTypeBase>(CSSPseudoElementType::Count); + for (CSSPseudoElementTypeBase i = 0; i < pseudoCount; ++i) { + CSSPseudoElementType type = static_cast<CSSPseudoElementType>(i); + if (nsCSSPseudoElements::IsEnabled(type, CSSEnabledState::eForAllContent)) { + nsIAtom* atom = nsCSSPseudoElements::GetPseudoAtom(type); + array.AppendElement(atom); + } + } + + *aLength = array.Length(); + char16_t** ret = + static_cast<char16_t**>(moz_xmalloc(*aLength * sizeof(char16_t*))); + for (uint32_t i = 0; i < *aLength; ++i) { + ret[i] = ToNewUnicode(nsDependentAtomString(array[i])); + } + *aNames = ret; + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::AddPseudoClassLock(nsIDOMElement *aElement, + const nsAString &aPseudoClass) +{ + EventStates state = GetStatesForPseudoClass(aPseudoClass); + if (state.IsEmpty()) { + return NS_OK; + } + + nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement); + NS_ENSURE_ARG_POINTER(element); + + element->LockStyleStates(state); + + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::RemovePseudoClassLock(nsIDOMElement *aElement, + const nsAString &aPseudoClass) +{ + EventStates state = GetStatesForPseudoClass(aPseudoClass); + if (state.IsEmpty()) { + return NS_OK; + } + + nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement); + NS_ENSURE_ARG_POINTER(element); + + element->UnlockStyleStates(state); + + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::HasPseudoClassLock(nsIDOMElement *aElement, + const nsAString &aPseudoClass, + bool *_retval) +{ + EventStates state = GetStatesForPseudoClass(aPseudoClass); + if (state.IsEmpty()) { + *_retval = false; + return NS_OK; + } + + nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement); + NS_ENSURE_ARG_POINTER(element); + + EventStates locks = element->LockedStyleStates(); + + *_retval = locks.HasAllStates(state); + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::ClearPseudoClassLocks(nsIDOMElement *aElement) +{ + nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement); + NS_ENSURE_ARG_POINTER(element); + + element->ClearStyleStateLocks(); + + return NS_OK; +} + +NS_IMETHODIMP +inDOMUtils::ParseStyleSheet(nsIDOMCSSStyleSheet *aSheet, + const nsAString& aInput) +{ + RefPtr<CSSStyleSheet> sheet = do_QueryObject(aSheet); + NS_ENSURE_ARG_POINTER(sheet); + + return sheet->ReparseSheet(aInput); +} + +NS_IMETHODIMP +inDOMUtils::ScrollElementIntoView(nsIDOMElement *aElement) +{ + nsCOMPtr<nsIContent> content = do_QueryInterface(aElement); + NS_ENSURE_ARG_POINTER(content); + + nsIPresShell* presShell = content->OwnerDoc()->GetShell(); + if (!presShell) { + return NS_OK; + } + + presShell->ScrollContentIntoView(content, + nsIPresShell::ScrollAxis(), + nsIPresShell::ScrollAxis(), + nsIPresShell::SCROLL_OVERFLOW_HIDDEN); + + return NS_OK; +} |