gecko/layout/inspector/inDOMUtils.cpp
L. David Baron 99260b9bb7 Bug 978833 patch 12 - Use the css::Declaration instead of the css::StyleRule as the matching rule. r=heycam
This is the key change in this patch series; it changes the object we
use for style data (currently nsIStyleRule) identity.  It allows
removing some hacks we have to deal with that for StyleRule, and avoids
having to write similar hacks for nsCSSKeyframeRule and nsCSSPageRule
(which are broken without this).

I confirmed locally that it is this patch that fixes both of the todo_is
mochitests, by building and testing with the patch queue through patch
11, and again through patch 12.
2015-11-05 16:44:10 +08:00

1287 lines
37 KiB
C++

/* -*- 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 "nsIServiceManager.h"
#include "nsISupportsArray.h"
#include "nsString.h"
#include "nsIStyleSheetLinkingElement.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/CSSStyleSheet.h"
#include "mozilla/dom/Element.h"
#include "nsRuleWalker.h"
#include "nsRuleProcessorData.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);
nsCOMArray<nsIStyleSheet> 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) {
nsStyleSet* styleSet = presShell->StyleSet();
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));
}
nsAutoTArray<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++) {
sheets.AppendElement(document->GetStyleSheetAt(i));
}
nsISupports** ret = static_cast<nsISupports**>(moz_xmalloc(sheets.Count() *
sizeof(nsISupports*)));
for (int32_t i = 0; i < sheets.Count(); i++) {
NS_ADDREF(ret[i] = sheets[i]);
}
*aLength = sheets.Count();
*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,
nsISupportsArray **_retval)
{
NS_ENSURE_ARG_POINTER(aElement);
*_retval = nullptr;
nsCOMPtr<nsIAtom> pseudoElt;
if (!aPseudo.IsEmpty()) {
pseudoElt = do_GetAtom(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;
}
nsCOMPtr<nsISupportsArray> rules;
NS_NewISupportsArray(getter_AddRefs(rules));
if (!rules) return NS_ERROR_OUT_OF_MEMORY;
for ( ; !ruleNode->IsRoot(); ruleNode = ruleNode->GetParent()) {
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->InsertElementAt(domRule, 0);
}
}
}
}
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) {
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 = do_GetAtom(aPseudo);
if (sel->mSelectors->PseudoType() !=
nsCSSPseudoElements::GetPseudoType(pseudoElt)) {
*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)
{
nsCSSProperty prop =
nsCSSProps::LookupProperty(aPropertyName, nsCSSProps::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 \
nsCSSProperty cssProp = nsCSSProperty(_prop); \
if (nsCSSProps::IsEnabled(cssProp, nsCSSProps::eEnabledForAllContent)) { \
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(nsCSSProperty(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(nsCSSProperty(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 nsCSSProperty aProperty,
nsTArray<nsString>& aArray)
{
if (nsCSSProps::IsShorthand(aProperty)) {
// Shorthand props have no keywords.
return;
}
const nsCSSProps::KTableValue *keywordTable =
nsCSSProps::kKeywordTableTable[aProperty];
if (keywordTable) {
size_t i = 0;
while (nsCSSKeyword(keywordTable[i]) != eCSSKeyword_UNKNOWN) {
nsCSSKeyword word = nsCSSKeyword(keywordTable[i]);
InsertNoDuplicates(aArray,
NS_ConvertASCIItoUTF16(nsCSSKeywords::GetStringValue(word)));
// Increment counter by 2, because in this table every second
// element is a nsCSSKeyword.
i += 2;
}
}
}
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);
for (size_t i = 0; i < size; i++) {
CopyASCIItoUTF16(allColorNames[i], *aArray.AppendElement());
}
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)
{
nsCSSProperty propertyID =
nsCSSProps::LookupProperty(aProperty, nsCSSProps::eEnabledForAllContent);
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 nsCSSProperty *props = nsCSSProps::SubpropertyEntryFor(propertyID);
*props != eCSSProperty_UNKNOWN; ++props) {
++subpropCount;
}
*aValues =
static_cast<char16_t**>(moz_xmalloc(subpropCount * sizeof(char16_t*)));
*aLength = subpropCount;
for (const nsCSSProperty *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)
{
nsCSSProperty propertyID =
nsCSSProps::LookupProperty(aProperty, nsCSSProps::eEnabledForAllContent);
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(nsCSSProperty 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 nsCSSProperty* 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_size:
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:
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:
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)
{
nsCSSProperty propertyID =
nsCSSProps::LookupProperty(aProperty, nsCSSProps::eEnabledForAllContent);
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)
{
nsCSSProperty propertyID = nsCSSProps::LookupProperty(aProperty,
nsCSSProps::eEnabledForAllContent);
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,
nsCSSProps::eEnabledForAllContent) {
// Get colors (once) first.
uint32_t propertyParserVariant = nsCSSProps::ParserVariant(*subproperty);
if (propertyParserVariant & VARIANT_COLOR) {
GetColorsForProperty(propertyParserVariant, array);
break;
}
}
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subproperty, propertyID,
nsCSSProps::eEnabledForAllContent) {
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)
{
nsCSSProperty propertyID =
nsCSSProps::LookupProperty(aPropertyName, nsCSSProps::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)
{
NS_ENSURE_ARG_POINTER(aElement);
RefPtr<EventStateManager> esm =
inLayoutUtils::GetEventStateManagerFor(aElement);
if (esm) {
nsCOMPtr<nsIContent> content;
content = do_QueryInterface(aElement);
// XXX Invalid cast of bool to nsresult (bug 778108)
return (nsresult)esm->SetContentState(content, EventStates(aState));
}
return NS_ERROR_FAILURE;
}
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) ==
nsCSSPseudoClasses::ePseudoClass_NotPseudoClass + 1,
"Length of PseudoClassStates array is incorrect");
nsCOMPtr<nsIAtom> atom = do_GetAtom(aStatePseudo);
// Ignore :moz-any-link so we don't give the element simultaneous
// visited and unvisited style state
if (nsCSSPseudoClasses::GetPseudoType(atom) ==
nsCSSPseudoClasses::ePseudoClass_mozAnyLink) {
return EventStates();
}
// Our array above is long enough that indexing into it with
// NotPseudoClass is ok.
return sPseudoClassStates[nsCSSPseudoClasses::GetPseudoType(atom)];
}
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;
}