diff --git a/mozilla/content/base/public/nsIStyleRuleProcessor.h b/mozilla/content/base/public/nsIStyleRuleProcessor.h index c3e7cb4c1f4..78e612019b8 100644 --- a/mozilla/content/base/public/nsIStyleRuleProcessor.h +++ b/mozilla/content/base/public/nsIStyleRuleProcessor.h @@ -43,6 +43,7 @@ #include "nsISupports.h" #include "nsIPresContext.h" // for nsCompatability #include "nsILinkHandler.h" +#include "nsString.h" class nsISizeOfHandler; @@ -78,6 +79,8 @@ struct RuleProcessorData { aContext->FreeToShell(sizeof(RuleProcessorData), this); }; + const nsString* GetLang(); + nsIPresContext* mPresContext; nsIContent* mContent; nsIContent* mParentContent; // if content, content->GetParent() @@ -98,6 +101,10 @@ struct RuleProcessorData { PRInt32 mNameSpaceID; // if content, content->GetNameSapce() RuleProcessorData* mPreviousSiblingData; RuleProcessorData* mParentData; + +protected: + nsAutoString mLanguage; + PRBool mIsLanguageValid; }; struct ElementRuleProcessorData : public RuleProcessorData { diff --git a/mozilla/content/base/src/nsStyleContext.cpp b/mozilla/content/base/src/nsStyleContext.cpp index 3bc83916c92..bd760db159a 100644 --- a/mozilla/content/base/src/nsStyleContext.cpp +++ b/mozilla/content/base/src/nsStyleContext.cpp @@ -565,7 +565,7 @@ nsStyleContext::CalcStyleDifference(nsIStyleContext* aOther, PRInt32& aHint) // We begin by examining those style structs that are capable of causing the maximal // difference, a FRAMECHANGE. - // FRAMECHANGE Structs: Display, XUL, Content, UserInterface + // FRAMECHANGE Structs: Display, XUL, Content, UserInterface, Quotes if (aHint < maxHint) { const nsStyleDisplay* display = (const nsStyleDisplay*)PeekStyleData(eStyleStruct_Display); if (display) { @@ -630,12 +630,26 @@ nsStyleContext::CalcStyleDifference(nsIStyleContext* aOther, PRInt32& aHint) } #endif + // If the quotes implementation is ever going to change we might not need + // a framechange here and a reflow should be sufficient. See bug 35768. + if (aHint < maxHint) { + const nsStyleQuotes* quotes = (const nsStyleQuotes*)PeekStyleData(eStyleStruct_Quotes); + if (quotes) { + const nsStyleQuotes* otherQuotes = (const nsStyleQuotes*)aOther->GetStyleData(eStyleStruct_Quotes); + if (quotes != otherQuotes) { + hint = quotes->CalcDifference(*otherQuotes); + if (aHint < hint) + aHint = hint; + } + } + } + // At this point, we know that the worst kind of damage we could do is a reflow. maxHint = NS_STYLE_HINT_REFLOW; // The following structs cause (as their maximal difference) a reflow to occur. // REFLOW Structs: Font, Margin, Padding, Border, List, Position, Text, TextReset, - // Visibility, Quotes, Table, TableBorder + // Visibility, Table, TableBorder if (aHint < maxHint) { const nsStyleFont* font = (const nsStyleFont*)PeekStyleData(eStyleStruct_Font); if (font) { @@ -768,18 +782,6 @@ nsStyleContext::CalcStyleDifference(nsIStyleContext* aOther, PRInt32& aHint) } } - if (aHint < maxHint) { - const nsStyleQuotes* quotes = (const nsStyleQuotes*)PeekStyleData(eStyleStruct_Quotes); - if (quotes) { - const nsStyleQuotes* otherQuotes = (const nsStyleQuotes*)aOther->GetStyleData(eStyleStruct_Quotes); - if (quotes != otherQuotes) { - hint = quotes->CalcDifference(*otherQuotes); - if (aHint < hint) - aHint = hint; - } - } - } - // At this point, we know that the worst kind of damage we could do is a re-render // (i.e., a VISUAL change). maxHint = NS_STYLE_HINT_VISUAL; diff --git a/mozilla/content/html/style/src/nsCSSParser.cpp b/mozilla/content/html/style/src/nsCSSParser.cpp index 29fcbddd7a7..5d6e80c7cd2 100644 --- a/mozilla/content/html/style/src/nsCSSParser.cpp +++ b/mozilla/content/html/style/src/nsCSSParser.cpp @@ -237,6 +237,8 @@ protected: PRBool aIsNegated); void ParseNegatedSimpleSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, PRInt32& aErrorCode); + void ParseLangSelector(nsCSSSelector& aSelector, PRInt32& aParsingStatus, + PRInt32& aErrorCode); PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead); @@ -1636,8 +1638,8 @@ PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, nsCSSSelector* listSel = list->mSelectors; // pull out pseudo elements here - nsAtomList* prevList = nsnull; - nsAtomList* pseudoClassList = listSel->mPseudoClassList; + nsAtomStringList* prevList = nsnull; + nsAtomStringList* pseudoClassList = listSel->mPseudoClassList; while (nsnull != pseudoClassList) { if (! IsPseudoClass(pseudoClassList->mAtom)) { havePseudoElement = PR_TRUE; @@ -2201,7 +2203,7 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, buffer.Append(PRUnichar(':')); buffer.Append(mToken.mIdent); ToLowerCase(buffer); - nsIAtom* pseudo = NS_NewAtom(buffer); + nsCOMPtr pseudo = do_GetAtom(buffer); if (eCSSToken_Ident != mToken.mType) { // malformed selector if (eCSSToken_Function != mToken.mType || @@ -2211,19 +2213,17 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, (!aIsNegated && IsTreePseudoElement(mToken.mIdent)) || #endif // the negation pseudo-class is a function - (nsCSSAtoms::notPseudo == pseudo))) { + (nsCSSAtoms::notPseudo == pseudo) || + // as is the lang pseudo-class + (nsCSSAtoms::langPseudo == pseudo))) { REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector not found")); - NS_RELEASE(pseudo); UngetToken(); aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; return; -#ifdef INCLUDE_XUL } -#endif } if (nsCSSAtoms::notPseudo == pseudo) { - NS_RELEASE(pseudo); if (aIsNegated) { // :not() can't be itself negated REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Negation pseudo-class can't be negated")); aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; @@ -2236,10 +2236,17 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, } } else if (IsPseudoClass(pseudo)) { - // XXX parse pseudo classes accepting arguments aDataMask |= SEL_MASK_PCLASS; - aSelector.AddPseudoClass(pseudo); - NS_RELEASE(pseudo); + if (nsCSSAtoms::langPseudo == pseudo) { + ParseLangSelector(aSelector, aParsingStatus, aErrorCode); + } + // XXX are there more pseudo classes which accept arguments ? + else { + aSelector.AddPseudoClass(pseudo); + } + if (SELECTOR_PARSING_ENDED_OK != aParsingStatus) { + return; + } } else { if (aIsNegated) { // pseudo-elements can't be negated @@ -2250,7 +2257,6 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, if (0 == (aDataMask & SEL_MASK_PELEM)) { aDataMask |= SEL_MASK_PELEM; aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later - NS_RELEASE(pseudo); #ifdef INCLUDE_XUL if (eCSSToken_Function == mToken.mType && @@ -2282,7 +2288,6 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, else { // multiple pseudo elements, not legal REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Extra pseudo-element")); UngetToken(); - NS_RELEASE(pseudo); aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; return; } @@ -2360,6 +2365,43 @@ void CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask, } } +// +// Parse the argument of a pseudo-class :lang() +// +void CSSParserImpl::ParseLangSelector(nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + // Check if we have the first parenthesis + if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) { + + if (! GetToken(aErrorCode, PR_TRUE)) { // premature eof + REPORT_UNEXPECTED_EOF(NS_LITERAL_STRING("argument to :lang selector")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + // We expect an identifier with a language abbreviation + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for lang pseudo-class parameter")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + + // Add the pseudo with the language parameter + aSelector.AddPseudoClass(nsCSSAtoms::langPseudo, mToken.mIdent.get()); + + // close the parenthesis + if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Missing closing ')' in lang pseudo-class")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + } + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Missing argument in lang pseudo-class")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + } +} + /** * This is the format for selectors: * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* diff --git a/mozilla/content/html/style/src/nsCSSStyleRule.cpp b/mozilla/content/html/style/src/nsCSSStyleRule.cpp index 75e4a94ffae..dd826627550 100644 --- a/mozilla/content/html/style/src/nsCSSStyleRule.cpp +++ b/mozilla/content/html/style/src/nsCSSStyleRule.cpp @@ -69,6 +69,7 @@ #include "nsINameSpace.h" #include "nsILookAndFeel.h" #include "nsRuleNode.h" +#include "nsUnicharUtils.h" #include "nsIStyleSet.h" #include "nsISizeOfHandler.h" @@ -117,7 +118,6 @@ nsAtomList::nsAtomList(nsIAtom* aAtom) mNext(nsnull) { MOZ_COUNT_CTOR(nsAtomList); - NS_IF_ADDREF(mAtom); } nsAtomList::nsAtomList(const nsString& aAtomValue) @@ -125,7 +125,7 @@ nsAtomList::nsAtomList(const nsString& aAtomValue) mNext(nsnull) { MOZ_COUNT_CTOR(nsAtomList); - mAtom = NS_NewAtom(aAtomValue); + mAtom = do_GetAtom(aAtomValue); } nsAtomList::nsAtomList(const nsAtomList& aCopy) @@ -133,14 +133,12 @@ nsAtomList::nsAtomList(const nsAtomList& aCopy) mNext(nsnull) { MOZ_COUNT_CTOR(nsAtomList); - NS_IF_ADDREF(mAtom); NS_IF_COPY(mNext, aCopy.mNext, nsAtomList); } nsAtomList::~nsAtomList(void) { MOZ_COUNT_DTOR(nsAtomList); - NS_IF_RELEASE(mAtom); NS_IF_DELETE(mNext); } @@ -160,6 +158,63 @@ PRBool nsAtomList::Equals(const nsAtomList* aOther) const return PR_FALSE; } +MOZ_DECL_CTOR_COUNTER(nsAtomStringList) + +nsAtomStringList::nsAtomStringList(nsIAtom* aAtom, const PRUnichar* aString) + : mAtom(aAtom), + mString(nsnull), + mNext(nsnull) +{ + MOZ_COUNT_CTOR(nsAtomStringList); + if (aString) + mString = nsCRT::strdup(aString); +} + +nsAtomStringList::nsAtomStringList(const nsString& aAtomValue, + const PRUnichar* aString) + : mAtom(nsnull), + mString(nsnull), + mNext(nsnull) +{ + MOZ_COUNT_CTOR(nsAtomStringList); + mAtom = do_GetAtom(aAtomValue); + if (aString) + mString = nsCRT::strdup(aString); +} + +nsAtomStringList::nsAtomStringList(const nsAtomStringList& aCopy) + : mAtom(aCopy.mAtom), + mString(nsnull), + mNext(nsnull) +{ + MOZ_COUNT_CTOR(nsAtomStringList); + if (aCopy.mString) + mString = nsCRT::strdup(aCopy.mString); + NS_IF_COPY(mNext, aCopy.mNext, nsAtomStringList); +} + +nsAtomStringList::~nsAtomStringList(void) +{ + MOZ_COUNT_DTOR(nsAtomStringList); + if (mString) + nsCRT::free(mString); + NS_IF_DELETE(mNext); +} + +PRBool nsAtomStringList::Equals(const nsAtomStringList* aOther) const +{ + return (this == aOther) || + (aOther && + mAtom == aOther->mAtom && + !mString == !aOther->mString && + !mNext == !aOther->mNext && + (!mNext || mNext->Equals(aOther->mNext)) && + // Check strings last, since it's the slowest check. + (!mString || nsDependentString(mString).Equals( + nsDependentString(aOther->mString), + nsCaseInsensitiveStringComparator()))); +} + MOZ_DECL_CTOR_COUNTER(nsAttrSelector) #ifdef DEBUG_REFS @@ -336,10 +391,9 @@ nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) mNext(nsnull) { MOZ_COUNT_CTOR(nsCSSSelector); - NS_IF_ADDREF(mTag); NS_IF_COPY(mIDList, aCopy.mIDList, nsAtomList); NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); - NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomStringList); NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); NS_IF_COPY(mNegations, aCopy.mNegations, nsCSSSelector); @@ -362,7 +416,6 @@ nsCSSSelector::~nsCSSSelector(void) nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) { - NS_IF_RELEASE(mTag); NS_IF_DELETE(mIDList); NS_IF_DELETE(mClassList); NS_IF_DELETE(mPseudoClassList); @@ -373,12 +426,11 @@ nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) mTag = aCopy.mTag; NS_IF_COPY(mIDList, aCopy.mIDList, nsAtomList); NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); - NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomStringList); NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); mOperator = aCopy.mOperator; NS_IF_COPY(mNegations, aCopy.mNegations, nsCSSSelector); - NS_IF_ADDREF(mTag); return *this; } @@ -446,7 +498,7 @@ PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const void nsCSSSelector::Reset(void) { mNameSpace = kNameSpaceID_Unknown; - NS_IF_RELEASE(mTag); + mTag = nsnull; NS_IF_DELETE(mIDList); NS_IF_DELETE(mClassList); NS_IF_DELETE(mPseudoClassList); @@ -462,10 +514,10 @@ void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace) void nsCSSSelector::SetTag(const nsString& aTag) { - NS_IF_RELEASE(mTag); - if (!aTag.IsEmpty()) { - mTag = NS_NewAtom(aTag); - } + if (aTag.IsEmpty()) + mTag = nsnull; + else + mTag = do_GetAtom(aTag); } void nsCSSSelector::AddID(const nsString& aID) @@ -490,25 +542,27 @@ void nsCSSSelector::AddClass(const nsString& aClass) } } -void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass) +void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass, + const PRUnichar* aString) { if (!aPseudoClass.IsEmpty()) { - nsAtomList** list = &mPseudoClassList; + nsAtomStringList** list = &mPseudoClassList; while (nsnull != *list) { list = &((*list)->mNext); } - *list = new nsAtomList(aPseudoClass); + *list = new nsAtomStringList(aPseudoClass, aString); } } -void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass) +void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass, + const PRUnichar* aString) { if (nsnull != aPseudoClass) { - nsAtomList** list = &mPseudoClassList; + nsAtomStringList** list = &mPseudoClassList; while (nsnull != *list) { list = &((*list)->mNext); } - *list = new nsAtomList(aPseudoClass); + *list = new nsAtomStringList(aPseudoClass, aString); } } @@ -557,10 +611,10 @@ PRInt32 nsCSSSelector::CalcWeight(void) const weight += 0x000100; list = list->mNext; } - list = mPseudoClassList; - while (nsnull != list) { + nsAtomStringList *plist = mPseudoClassList; + while (nsnull != plist) { weight += 0x000100; - list = list->mNext; + plist = plist->mNext; } nsAttrSelector* attr = mAttrList; while (nsnull != attr) { @@ -647,7 +701,7 @@ void nsCSSSelector::SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize) } } if(mPseudoClassList && uniqueItems->AddItem(mPseudoClassList)){ - nsAtomList *pNext = nsnull; + nsAtomStringList *pNext = nsnull; pNext = mPseudoClassList; while(pNext){ if(pNext->mAtom && uniqueItems->AddItem(pNext->mAtom)){ @@ -832,11 +886,16 @@ nsresult nsCSSSelector::ToString( nsAString& aString, nsICSSStyleSheet* aSheet, // Append each pseudo-class in the linked list if (mPseudoClassList) { - nsAtomList* list = mPseudoClassList; + nsAtomStringList* list = mPseudoClassList; while (list != nsnull) { list->mAtom->GetUnicode(&temp); NS_IF_NEGATED_START(aIsNegated, aString) aString.Append(temp); + if (nsnull != list->mString) { + aString.Append(PRUnichar('(')); + aString.Append(list->mString); + aString.Append(PRUnichar(')')); + } NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } diff --git a/mozilla/content/html/style/src/nsCSSStyleSheet.cpp b/mozilla/content/html/style/src/nsCSSStyleSheet.cpp index a7ecbb6e5a8..5785d7af8c1 100644 --- a/mozilla/content/html/style/src/nsCSSStyleSheet.cpp +++ b/mozilla/content/html/style/src/nsCSSStyleSheet.cpp @@ -245,7 +245,7 @@ RuleHash_CIMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, nsIAtom *entry_atom = NS_CONST_CAST(nsIAtom*, NS_STATIC_CAST(const nsIAtom*, table->ops->getKey(table, NS_CONST_CAST(PLDHashEntryHdr*, hdr)))); - // Check for case-insensitive match first. + // Check for case-sensitive match first. if (match_atom == entry_atom) return PR_TRUE; @@ -2065,7 +2065,7 @@ CSSStyleSheetImpl::ClearMedia(void) NS_IMETHODIMP CSSStyleSheetImpl::GetEnabled(PRBool& aEnabled) const { - aEnabled = ((PR_TRUE == mDisabled) ? PR_FALSE : PR_TRUE); + aEnabled = !mDisabled; return NS_OK; } @@ -2073,7 +2073,7 @@ NS_IMETHODIMP CSSStyleSheetImpl::SetEnabled(PRBool aEnabled) { PRBool oldState = mDisabled; - mDisabled = ((PR_TRUE == aEnabled) ? PR_FALSE : PR_TRUE); + mDisabled = !aEnabled; if ((nsnull != mDocument) && (mDisabled != oldState)) { mDocument->SetStyleSheetDisabledState(this, mDisabled); @@ -2353,6 +2353,16 @@ CSSStyleSheetImpl::CheckRuleForAttributes(nsICSSRule *aRule) DependentAtomKey key(sel->mAttr); mInner->mRelevantAttributes.Put(&key, sel->mAttr); } + + // Search for the :lang() pseudo class. If it is there add + // the lang attribute to the relevant attribute list. + for (nsAtomStringList* p = iter->mPseudoClassList; p; p = p->mNext) { + if (p->mAtom == nsCSSAtoms::langPseudo) { + DependentAtomKey langKey(nsHTMLAtoms::lang); + mInner->mRelevantAttributes.Put(&langKey, nsHTMLAtoms::lang); + break; + } + } } } /* fall-through */ default: @@ -3373,6 +3383,7 @@ RuleProcessorData::RuleProcessorData(nsIPresContext* aPresContext, mNameSpaceID = kNameSpaceID_Unknown; mPreviousSiblingData = nsnull; mParentData = nsnull; + mIsLanguageValid = PR_FALSE; // get the compat. mode (unless it is provided) if(!aCompat) { @@ -3418,7 +3429,7 @@ RuleProcessorData::RuleProcessorData(nsIPresContext* aPresContext, // if HTML content and it has some attributes, check for an HTML link // NOTE: optimization: cannot be a link if no attributes (since it needs an href) - if (PR_TRUE == mIsHTMLContent && mHasAttributes) { + if (mIsHTMLContent && mHasAttributes) { // check if it is an HTML Link if(nsStyleUtil::IsHTMLLink(aContent, mContentTag, mPresContext, &mLinkState)) { mIsHTMLLink = PR_TRUE; @@ -3427,9 +3438,9 @@ RuleProcessorData::RuleProcessorData(nsIPresContext* aPresContext, // if not an HTML link, check for a simple xlink (cannot be both HTML link and xlink) // NOTE: optimization: cannot be an XLink if no attributes (since it needs an - if(PR_FALSE == mIsHTMLLink && + if(!mIsHTMLLink && mHasAttributes && - !(aContent->IsContentOfType(nsIContent::eHTML) || aContent->IsContentOfType(nsIContent::eXUL)) && + !(mIsHTMLContent || aContent->IsContentOfType(nsIContent::eXUL)) && nsStyleUtil::IsSimpleXlink(aContent, mPresContext, &mLinkState)) { mIsSimpleXLink = PR_TRUE; } @@ -3463,6 +3474,39 @@ RuleProcessorData::~RuleProcessorData() NS_IF_RELEASE(mStyledContent); } +const nsString* RuleProcessorData::GetLang(void) +{ + if (!mIsLanguageValid) { + mIsLanguageValid = PR_TRUE; + mLanguage.SetLength(0); + nsCOMPtr content = mContent; + while (content) { + PRInt32 attrCount = 0; + content->GetAttrCount(attrCount); + if (attrCount > 0) { + // xml:lang has precedence over lang on HTML elements (see + // XHTML1 section C.7). + nsAutoString value; + nsresult attrState = content->GetAttr(kNameSpaceID_XML, + nsHTMLAtoms::lang, value); + if (attrState != NS_CONTENT_ATTR_HAS_VALUE && + content->IsContentOfType(nsIContent::eHTML)) { + attrState = content->GetAttr(kNameSpaceID_None, + nsHTMLAtoms::lang, value); + } + if (attrState == NS_CONTENT_ATTR_HAS_VALUE) { + mLanguage = value; + break; + } + } + nsIContent *parent; + content->GetParent(parent); + content = dont_AddRef(parent); + } + } + return &mLanguage; +} + static const PRUnichar kNullCh = PRUnichar('\0'); static PRBool ValueIncludes(const nsString& aValueList, const nsString& aValue, PRBool aCaseSensitive) @@ -3564,6 +3608,39 @@ static PRBool IsSignificantChild(nsIContent* aChild, PRBool aAcceptNonWhitespace return PR_FALSE; } +static PRBool +DashMatchCompare(const nsAString& aAttributeValue, + const nsAString& aSelectorValue, + const PRBool aCaseSensitive) +{ + PRBool result; + PRUint32 selectorLen = aSelectorValue.Length(); + PRUint32 attributeLen = aAttributeValue.Length(); + if (selectorLen > attributeLen) { + result = PR_FALSE; + } + else { + nsAString::const_iterator iter; + if (selectorLen != attributeLen && + *aAttributeValue.BeginReading(iter).advance(selectorLen) != + PRUnichar('-')) { + // to match, the aAttributeValue must have a dash after the end of + // the aSelectorValue's text (unless the aSelectorValue and the + // aAttributeValue have the same text) + result = PR_FALSE; + } + else { + const nsAString& attributeSubstring = + Substring(aAttributeValue, 0, selectorLen); + if (aCaseSensitive) + result = attributeSubstring.Equals(aSelectorValue); + else + result = attributeSubstring.Equals(aSelectorValue, + nsCaseInsensitiveStringComparator()); + } + } + return result; +} // NOTE: The |aStateMask| code isn't going to work correctly anymore if // we start batching style changes, because if multiple states change in @@ -3606,7 +3683,7 @@ static PRBool SelectorMatches(RuleProcessorData &data, if (nsnull != aSelector->mPseudoClassList) { // test for pseudo class match // first-child, root, lang, active, focus, hover, link, visited... // XXX disabled, enabled, selected, selection - nsAtomList* pseudoClass = aSelector->mPseudoClassList; + nsAtomStringList* pseudoClass = aSelector->mPseudoClassList; while (result && (nsnull != pseudoClass)) { if ((nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) || @@ -3686,8 +3763,49 @@ static PRBool SelectorMatches(RuleProcessorData &data, ? localTrue : localFalse; } else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { - // XXX not yet implemented - result = PR_FALSE; + NS_ASSERTION(nsnull != pseudoClass->mString, "null lang parameter"); + result = localFalse; + if (pseudoClass->mString && *pseudoClass->mString) { + // We have to determine the language of the current element. Since + // this is currently no property and since the language is inherited + // from the parent we have to be prepared to look at all parent + // nodes. The language itself is encoded in the LANG attribute. + const nsString* lang = data.GetLang(); + if (!lang->IsEmpty()) { + result = localTrue == DashMatchCompare(*lang, + nsDependentString(pseudoClass->mString), PR_FALSE); + } + else { + nsCOMPtr doc; + data.mContent->GetDocument(*getter_AddRefs(doc)); + if (doc) { + // Try to get the language from the HTTP header or if this + // is missing as well from the preferences. + // The content language can be a comma-separated list of + // language codes. + nsAutoString language; + if (NS_SUCCEEDED(doc->GetContentLanguage(language))) { + nsDependentString langString(pseudoClass->mString); + language.StripWhitespace(); + PRInt32 begin = 0; + PRInt32 len = language.Length(); + while (begin < len) { + PRInt32 end = language.FindChar(PRUnichar(','), begin); + if (end == kNotFound) { + end = len; + } + if (DashMatchCompare(Substring(language, begin, end-begin), + langString, + PR_FALSE)) { + result = localTrue; + break; + } + begin = end + 1; + } + } + } + } + } } else if (IsEventPseudo(pseudoClass->mAtom)) { // check if the element is event-sensitive @@ -3783,36 +3901,17 @@ static PRBool SelectorMatches(RuleProcessorData &data, switch (attr->mFunction) { case NS_ATTR_FUNC_EQUALS: if (isCaseSensitive) { - result = PRBool(localTrue == value.Equals(attr->mValue)); + result = localTrue == value.Equals(attr->mValue); } else { - result = PRBool(localTrue == value.Equals(attr->mValue, nsCaseInsensitiveStringComparator())); + result = localTrue == value.Equals(attr->mValue, nsCaseInsensitiveStringComparator()); } break; case NS_ATTR_FUNC_INCLUDES: - result = PRBool(localTrue == ValueIncludes(value, attr->mValue, isCaseSensitive)); + result = localTrue == ValueIncludes(value, attr->mValue, isCaseSensitive); break; case NS_ATTR_FUNC_DASHMATCH: - { - PRUint32 selLen = attr->mValue.Length(); - PRUint32 valLen = value.Length(); - if (selLen > valLen) { - result = localFalse; - } else { - if (selLen != valLen && - value.CharAt(selLen) != PRUnichar('-')) { - // to match, the value must have a dash after the end of - // the selector's text (unless the selector and the value - // have the same text) - result = localFalse; - break; - } - if (isCaseSensitive) - result = PRBool(localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsDefaultStringComparator())); - else - result = PRBool(localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator())); - } - } + result = localTrue == DashMatchCompare(value, attr->mValue, isCaseSensitive); break; case NS_ATTR_FUNC_ENDSMATCH: { @@ -3822,9 +3921,9 @@ static PRBool SelectorMatches(RuleProcessorData &data, result = localFalse; } else { if (isCaseSensitive) - result = PRBool(localTrue == Substring(value, valLen - selLen, selLen).Equals(attr->mValue, nsDefaultStringComparator())); + result = localTrue == Substring(value, valLen - selLen, selLen).Equals(attr->mValue, nsDefaultStringComparator()); else - result = PRBool(localTrue == Substring(value, valLen - selLen, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator())); + result = localTrue == Substring(value, valLen - selLen, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator()); } } break; @@ -3836,14 +3935,14 @@ static PRBool SelectorMatches(RuleProcessorData &data, result = localFalse; } else { if (isCaseSensitive) - result = PRBool(localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsDefaultStringComparator())); + result = localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsDefaultStringComparator()); else - result = PRBool(localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator())); + result = localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator()); } } break; case NS_ATTR_FUNC_CONTAINSMATCH: - result = PRBool(localTrue == (FindInReadable(attr->mValue, value, nsCaseInsensitiveStringComparator()))); + result = localTrue == (FindInReadable(attr->mValue, value, nsCaseInsensitiveStringComparator())); break; } } @@ -4210,6 +4309,7 @@ PRBool WeightedRulesSizeEnumFunc( nsISupports *aRule, void *aData ) { nsICSSStyleRule* rule = (nsICSSStyleRule*)aRule; CascadeSizeEnumData *pData = (CascadeSizeEnumData *)aData; + NS_ASSERTION(rule && pData, "null arguments not supported"); if(! pData->uniqueItems->AddItem((void*)rule)){ @@ -4335,7 +4435,7 @@ CSSRuleProcessor::ClearRuleCascades(void) inline PRBool IsStateSelector(nsCSSSelector& aSelector) { - nsAtomList* pseudoClass = aSelector.mPseudoClassList; + nsAtomStringList* pseudoClass = aSelector.mPseudoClassList; while (pseudoClass) { if ((pseudoClass->mAtom == nsCSSAtoms::activePseudo) || (pseudoClass->mAtom == nsCSSAtoms::checkedPseudo) || diff --git a/mozilla/content/html/style/src/nsICSSStyleRule.h b/mozilla/content/html/style/src/nsICSSStyleRule.h index 7d9e4e1b93f..f19ed554166 100644 --- a/mozilla/content/html/style/src/nsICSSStyleRule.h +++ b/mozilla/content/html/style/src/nsICSSStyleRule.h @@ -42,8 +42,10 @@ //#include #include "nsICSSRule.h" #include "nsString.h" +#include "nsCOMPtr.h" #include "nsCSSProps.h" #include "nsCSSValue.h" +#include "nsIAtom.h" class nsISizeOfHandler; @@ -60,8 +62,21 @@ public: ~nsAtomList(void); PRBool Equals(const nsAtomList* aOther) const; - nsIAtom* mAtom; - nsAtomList* mNext; + nsCOMPtr mAtom; + nsAtomList* mNext; +}; + +struct nsAtomStringList { +public: + nsAtomStringList(nsIAtom* aAtom, const PRUnichar *aString = nsnull); + nsAtomStringList(const nsString& aAtomValue, const PRUnichar *aString = nsnull); + nsAtomStringList(const nsAtomStringList& aCopy); + ~nsAtomStringList(void); + PRBool Equals(const nsAtomStringList* aOther) const; + + nsCOMPtr mAtom; + PRUnichar* mString; + nsAtomStringList* mNext; }; #define NS_ATTR_FUNC_SET 0 // [attr] @@ -112,8 +127,8 @@ public: void SetTag(const nsString& aTag); void AddID(const nsString& aID); void AddClass(const nsString& aClass); - void AddPseudoClass(const nsString& aPseudoClass); - void AddPseudoClass(nsIAtom* aPseudoClass); + void AddPseudoClass(const nsString& aPseudoClass, const PRUnichar* aString = nsnull); + void AddPseudoClass(nsIAtom* aPseudoClass, const PRUnichar* aString = nsnull); void AddAttribute(PRInt32 aNameSpace, const nsString& aAttr); void AddAttribute(PRInt32 aNameSpace, const nsString& aAttr, PRUint8 aFunc, const nsString& aValue, PRBool aCaseSensitive); @@ -133,10 +148,11 @@ private: public: PRInt32 mNameSpace; - nsIAtom* mTag; + nsCOMPtr mTag; nsAtomList* mIDList; nsAtomList* mClassList; - nsAtomList* mPseudoClassList; + nsAtomStringList* mPseudoClassList; // atom for the pseudo, string for + // the argument to functional pseudos nsAttrSelector* mAttrList; PRUnichar mOperator; nsCSSSelector* mNegations; diff --git a/mozilla/content/shared/src/nsStyleStruct.cpp b/mozilla/content/shared/src/nsStyleStruct.cpp index f8ca3208f5d..c9660ee1377 100644 --- a/mozilla/content/shared/src/nsStyleStruct.cpp +++ b/mozilla/content/shared/src/nsStyleStruct.cpp @@ -1246,17 +1246,19 @@ nsStyleQuotes::nsStyleQuotes(const nsStyleQuotes& aSource) PRInt32 nsStyleQuotes::CalcDifference(const nsStyleQuotes& aOther) const { + // If the quotes implementation is ever going to change we might not need + // a framechange here and a reflow should be sufficient. See bug 35768. if (mQuotesCount == aOther.mQuotesCount) { PRUint32 ix = (mQuotesCount * 2); while (0 < ix--) { if (mQuotes[ix] != aOther.mQuotes[ix]) { - return NS_STYLE_HINT_REFLOW; + return NS_STYLE_HINT_FRAMECHANGE; } } return NS_STYLE_HINT_NONE; } - return NS_STYLE_HINT_REFLOW; + return NS_STYLE_HINT_FRAMECHANGE; } // -------------------- diff --git a/mozilla/layout/style/nsCSSParser.cpp b/mozilla/layout/style/nsCSSParser.cpp index 29fcbddd7a7..5d6e80c7cd2 100644 --- a/mozilla/layout/style/nsCSSParser.cpp +++ b/mozilla/layout/style/nsCSSParser.cpp @@ -237,6 +237,8 @@ protected: PRBool aIsNegated); void ParseNegatedSimpleSelector(PRInt32& aDataMask, nsCSSSelector& aSelector, PRInt32& aParsingStatus, PRInt32& aErrorCode); + void ParseLangSelector(nsCSSSelector& aSelector, PRInt32& aParsingStatus, + PRInt32& aErrorCode); PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead); PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead); @@ -1636,8 +1638,8 @@ PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode, nsCSSSelector* listSel = list->mSelectors; // pull out pseudo elements here - nsAtomList* prevList = nsnull; - nsAtomList* pseudoClassList = listSel->mPseudoClassList; + nsAtomStringList* prevList = nsnull; + nsAtomStringList* pseudoClassList = listSel->mPseudoClassList; while (nsnull != pseudoClassList) { if (! IsPseudoClass(pseudoClassList->mAtom)) { havePseudoElement = PR_TRUE; @@ -2201,7 +2203,7 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, buffer.Append(PRUnichar(':')); buffer.Append(mToken.mIdent); ToLowerCase(buffer); - nsIAtom* pseudo = NS_NewAtom(buffer); + nsCOMPtr pseudo = do_GetAtom(buffer); if (eCSSToken_Ident != mToken.mType) { // malformed selector if (eCSSToken_Function != mToken.mType || @@ -2211,19 +2213,17 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, (!aIsNegated && IsTreePseudoElement(mToken.mIdent)) || #endif // the negation pseudo-class is a function - (nsCSSAtoms::notPseudo == pseudo))) { + (nsCSSAtoms::notPseudo == pseudo) || + // as is the lang pseudo-class + (nsCSSAtoms::langPseudo == pseudo))) { REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for pseudo-class selector not found")); - NS_RELEASE(pseudo); UngetToken(); aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; return; -#ifdef INCLUDE_XUL } -#endif } if (nsCSSAtoms::notPseudo == pseudo) { - NS_RELEASE(pseudo); if (aIsNegated) { // :not() can't be itself negated REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Negation pseudo-class can't be negated")); aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; @@ -2236,10 +2236,17 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, } } else if (IsPseudoClass(pseudo)) { - // XXX parse pseudo classes accepting arguments aDataMask |= SEL_MASK_PCLASS; - aSelector.AddPseudoClass(pseudo); - NS_RELEASE(pseudo); + if (nsCSSAtoms::langPseudo == pseudo) { + ParseLangSelector(aSelector, aParsingStatus, aErrorCode); + } + // XXX are there more pseudo classes which accept arguments ? + else { + aSelector.AddPseudoClass(pseudo); + } + if (SELECTOR_PARSING_ENDED_OK != aParsingStatus) { + return; + } } else { if (aIsNegated) { // pseudo-elements can't be negated @@ -2250,7 +2257,6 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, if (0 == (aDataMask & SEL_MASK_PELEM)) { aDataMask |= SEL_MASK_PELEM; aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later - NS_RELEASE(pseudo); #ifdef INCLUDE_XUL if (eCSSToken_Function == mToken.mType && @@ -2282,7 +2288,6 @@ void CSSParserImpl::ParsePseudoSelector(PRInt32& aDataMask, else { // multiple pseudo elements, not legal REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Extra pseudo-element")); UngetToken(); - NS_RELEASE(pseudo); aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; return; } @@ -2360,6 +2365,43 @@ void CSSParserImpl::ParseNegatedSimpleSelector(PRInt32& aDataMask, } } +// +// Parse the argument of a pseudo-class :lang() +// +void CSSParserImpl::ParseLangSelector(nsCSSSelector& aSelector, + PRInt32& aParsingStatus, + PRInt32& aErrorCode) +{ + // Check if we have the first parenthesis + if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) { + + if (! GetToken(aErrorCode, PR_TRUE)) { // premature eof + REPORT_UNEXPECTED_EOF(NS_LITERAL_STRING("argument to :lang selector")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + // We expect an identifier with a language abbreviation + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Expected identifier for lang pseudo-class parameter")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + return; + } + + // Add the pseudo with the language parameter + aSelector.AddPseudoClass(nsCSSAtoms::langPseudo, mToken.mIdent.get()); + + // close the parenthesis + if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Missing closing ')' in lang pseudo-class")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + } + } + else { + REPORT_UNEXPECTED_TOKEN(NS_LITERAL_STRING("Missing argument in lang pseudo-class")); + aParsingStatus = SELECTOR_PARSING_STOPPED_ERROR; + } +} + /** * This is the format for selectors: * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* diff --git a/mozilla/layout/style/nsCSSStyleRule.cpp b/mozilla/layout/style/nsCSSStyleRule.cpp index 75e4a94ffae..dd826627550 100644 --- a/mozilla/layout/style/nsCSSStyleRule.cpp +++ b/mozilla/layout/style/nsCSSStyleRule.cpp @@ -69,6 +69,7 @@ #include "nsINameSpace.h" #include "nsILookAndFeel.h" #include "nsRuleNode.h" +#include "nsUnicharUtils.h" #include "nsIStyleSet.h" #include "nsISizeOfHandler.h" @@ -117,7 +118,6 @@ nsAtomList::nsAtomList(nsIAtom* aAtom) mNext(nsnull) { MOZ_COUNT_CTOR(nsAtomList); - NS_IF_ADDREF(mAtom); } nsAtomList::nsAtomList(const nsString& aAtomValue) @@ -125,7 +125,7 @@ nsAtomList::nsAtomList(const nsString& aAtomValue) mNext(nsnull) { MOZ_COUNT_CTOR(nsAtomList); - mAtom = NS_NewAtom(aAtomValue); + mAtom = do_GetAtom(aAtomValue); } nsAtomList::nsAtomList(const nsAtomList& aCopy) @@ -133,14 +133,12 @@ nsAtomList::nsAtomList(const nsAtomList& aCopy) mNext(nsnull) { MOZ_COUNT_CTOR(nsAtomList); - NS_IF_ADDREF(mAtom); NS_IF_COPY(mNext, aCopy.mNext, nsAtomList); } nsAtomList::~nsAtomList(void) { MOZ_COUNT_DTOR(nsAtomList); - NS_IF_RELEASE(mAtom); NS_IF_DELETE(mNext); } @@ -160,6 +158,63 @@ PRBool nsAtomList::Equals(const nsAtomList* aOther) const return PR_FALSE; } +MOZ_DECL_CTOR_COUNTER(nsAtomStringList) + +nsAtomStringList::nsAtomStringList(nsIAtom* aAtom, const PRUnichar* aString) + : mAtom(aAtom), + mString(nsnull), + mNext(nsnull) +{ + MOZ_COUNT_CTOR(nsAtomStringList); + if (aString) + mString = nsCRT::strdup(aString); +} + +nsAtomStringList::nsAtomStringList(const nsString& aAtomValue, + const PRUnichar* aString) + : mAtom(nsnull), + mString(nsnull), + mNext(nsnull) +{ + MOZ_COUNT_CTOR(nsAtomStringList); + mAtom = do_GetAtom(aAtomValue); + if (aString) + mString = nsCRT::strdup(aString); +} + +nsAtomStringList::nsAtomStringList(const nsAtomStringList& aCopy) + : mAtom(aCopy.mAtom), + mString(nsnull), + mNext(nsnull) +{ + MOZ_COUNT_CTOR(nsAtomStringList); + if (aCopy.mString) + mString = nsCRT::strdup(aCopy.mString); + NS_IF_COPY(mNext, aCopy.mNext, nsAtomStringList); +} + +nsAtomStringList::~nsAtomStringList(void) +{ + MOZ_COUNT_DTOR(nsAtomStringList); + if (mString) + nsCRT::free(mString); + NS_IF_DELETE(mNext); +} + +PRBool nsAtomStringList::Equals(const nsAtomStringList* aOther) const +{ + return (this == aOther) || + (aOther && + mAtom == aOther->mAtom && + !mString == !aOther->mString && + !mNext == !aOther->mNext && + (!mNext || mNext->Equals(aOther->mNext)) && + // Check strings last, since it's the slowest check. + (!mString || nsDependentString(mString).Equals( + nsDependentString(aOther->mString), + nsCaseInsensitiveStringComparator()))); +} + MOZ_DECL_CTOR_COUNTER(nsAttrSelector) #ifdef DEBUG_REFS @@ -336,10 +391,9 @@ nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) mNext(nsnull) { MOZ_COUNT_CTOR(nsCSSSelector); - NS_IF_ADDREF(mTag); NS_IF_COPY(mIDList, aCopy.mIDList, nsAtomList); NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); - NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomStringList); NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); NS_IF_COPY(mNegations, aCopy.mNegations, nsCSSSelector); @@ -362,7 +416,6 @@ nsCSSSelector::~nsCSSSelector(void) nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) { - NS_IF_RELEASE(mTag); NS_IF_DELETE(mIDList); NS_IF_DELETE(mClassList); NS_IF_DELETE(mPseudoClassList); @@ -373,12 +426,11 @@ nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy) mTag = aCopy.mTag; NS_IF_COPY(mIDList, aCopy.mIDList, nsAtomList); NS_IF_COPY(mClassList, aCopy.mClassList, nsAtomList); - NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomList); + NS_IF_COPY(mPseudoClassList, aCopy.mPseudoClassList, nsAtomStringList); NS_IF_COPY(mAttrList, aCopy.mAttrList, nsAttrSelector); mOperator = aCopy.mOperator; NS_IF_COPY(mNegations, aCopy.mNegations, nsCSSSelector); - NS_IF_ADDREF(mTag); return *this; } @@ -446,7 +498,7 @@ PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const void nsCSSSelector::Reset(void) { mNameSpace = kNameSpaceID_Unknown; - NS_IF_RELEASE(mTag); + mTag = nsnull; NS_IF_DELETE(mIDList); NS_IF_DELETE(mClassList); NS_IF_DELETE(mPseudoClassList); @@ -462,10 +514,10 @@ void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace) void nsCSSSelector::SetTag(const nsString& aTag) { - NS_IF_RELEASE(mTag); - if (!aTag.IsEmpty()) { - mTag = NS_NewAtom(aTag); - } + if (aTag.IsEmpty()) + mTag = nsnull; + else + mTag = do_GetAtom(aTag); } void nsCSSSelector::AddID(const nsString& aID) @@ -490,25 +542,27 @@ void nsCSSSelector::AddClass(const nsString& aClass) } } -void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass) +void nsCSSSelector::AddPseudoClass(const nsString& aPseudoClass, + const PRUnichar* aString) { if (!aPseudoClass.IsEmpty()) { - nsAtomList** list = &mPseudoClassList; + nsAtomStringList** list = &mPseudoClassList; while (nsnull != *list) { list = &((*list)->mNext); } - *list = new nsAtomList(aPseudoClass); + *list = new nsAtomStringList(aPseudoClass, aString); } } -void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass) +void nsCSSSelector::AddPseudoClass(nsIAtom* aPseudoClass, + const PRUnichar* aString) { if (nsnull != aPseudoClass) { - nsAtomList** list = &mPseudoClassList; + nsAtomStringList** list = &mPseudoClassList; while (nsnull != *list) { list = &((*list)->mNext); } - *list = new nsAtomList(aPseudoClass); + *list = new nsAtomStringList(aPseudoClass, aString); } } @@ -557,10 +611,10 @@ PRInt32 nsCSSSelector::CalcWeight(void) const weight += 0x000100; list = list->mNext; } - list = mPseudoClassList; - while (nsnull != list) { + nsAtomStringList *plist = mPseudoClassList; + while (nsnull != plist) { weight += 0x000100; - list = list->mNext; + plist = plist->mNext; } nsAttrSelector* attr = mAttrList; while (nsnull != attr) { @@ -647,7 +701,7 @@ void nsCSSSelector::SizeOf(nsISizeOfHandler *aSizeOfHandler, PRUint32 &aSize) } } if(mPseudoClassList && uniqueItems->AddItem(mPseudoClassList)){ - nsAtomList *pNext = nsnull; + nsAtomStringList *pNext = nsnull; pNext = mPseudoClassList; while(pNext){ if(pNext->mAtom && uniqueItems->AddItem(pNext->mAtom)){ @@ -832,11 +886,16 @@ nsresult nsCSSSelector::ToString( nsAString& aString, nsICSSStyleSheet* aSheet, // Append each pseudo-class in the linked list if (mPseudoClassList) { - nsAtomList* list = mPseudoClassList; + nsAtomStringList* list = mPseudoClassList; while (list != nsnull) { list->mAtom->GetUnicode(&temp); NS_IF_NEGATED_START(aIsNegated, aString) aString.Append(temp); + if (nsnull != list->mString) { + aString.Append(PRUnichar('(')); + aString.Append(list->mString); + aString.Append(PRUnichar(')')); + } NS_IF_NEGATED_END(aIsNegated, aString) list = list->mNext; } diff --git a/mozilla/layout/style/nsCSSStyleSheet.cpp b/mozilla/layout/style/nsCSSStyleSheet.cpp index a7ecbb6e5a8..5785d7af8c1 100644 --- a/mozilla/layout/style/nsCSSStyleSheet.cpp +++ b/mozilla/layout/style/nsCSSStyleSheet.cpp @@ -245,7 +245,7 @@ RuleHash_CIMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, nsIAtom *entry_atom = NS_CONST_CAST(nsIAtom*, NS_STATIC_CAST(const nsIAtom*, table->ops->getKey(table, NS_CONST_CAST(PLDHashEntryHdr*, hdr)))); - // Check for case-insensitive match first. + // Check for case-sensitive match first. if (match_atom == entry_atom) return PR_TRUE; @@ -2065,7 +2065,7 @@ CSSStyleSheetImpl::ClearMedia(void) NS_IMETHODIMP CSSStyleSheetImpl::GetEnabled(PRBool& aEnabled) const { - aEnabled = ((PR_TRUE == mDisabled) ? PR_FALSE : PR_TRUE); + aEnabled = !mDisabled; return NS_OK; } @@ -2073,7 +2073,7 @@ NS_IMETHODIMP CSSStyleSheetImpl::SetEnabled(PRBool aEnabled) { PRBool oldState = mDisabled; - mDisabled = ((PR_TRUE == aEnabled) ? PR_FALSE : PR_TRUE); + mDisabled = !aEnabled; if ((nsnull != mDocument) && (mDisabled != oldState)) { mDocument->SetStyleSheetDisabledState(this, mDisabled); @@ -2353,6 +2353,16 @@ CSSStyleSheetImpl::CheckRuleForAttributes(nsICSSRule *aRule) DependentAtomKey key(sel->mAttr); mInner->mRelevantAttributes.Put(&key, sel->mAttr); } + + // Search for the :lang() pseudo class. If it is there add + // the lang attribute to the relevant attribute list. + for (nsAtomStringList* p = iter->mPseudoClassList; p; p = p->mNext) { + if (p->mAtom == nsCSSAtoms::langPseudo) { + DependentAtomKey langKey(nsHTMLAtoms::lang); + mInner->mRelevantAttributes.Put(&langKey, nsHTMLAtoms::lang); + break; + } + } } } /* fall-through */ default: @@ -3373,6 +3383,7 @@ RuleProcessorData::RuleProcessorData(nsIPresContext* aPresContext, mNameSpaceID = kNameSpaceID_Unknown; mPreviousSiblingData = nsnull; mParentData = nsnull; + mIsLanguageValid = PR_FALSE; // get the compat. mode (unless it is provided) if(!aCompat) { @@ -3418,7 +3429,7 @@ RuleProcessorData::RuleProcessorData(nsIPresContext* aPresContext, // if HTML content and it has some attributes, check for an HTML link // NOTE: optimization: cannot be a link if no attributes (since it needs an href) - if (PR_TRUE == mIsHTMLContent && mHasAttributes) { + if (mIsHTMLContent && mHasAttributes) { // check if it is an HTML Link if(nsStyleUtil::IsHTMLLink(aContent, mContentTag, mPresContext, &mLinkState)) { mIsHTMLLink = PR_TRUE; @@ -3427,9 +3438,9 @@ RuleProcessorData::RuleProcessorData(nsIPresContext* aPresContext, // if not an HTML link, check for a simple xlink (cannot be both HTML link and xlink) // NOTE: optimization: cannot be an XLink if no attributes (since it needs an - if(PR_FALSE == mIsHTMLLink && + if(!mIsHTMLLink && mHasAttributes && - !(aContent->IsContentOfType(nsIContent::eHTML) || aContent->IsContentOfType(nsIContent::eXUL)) && + !(mIsHTMLContent || aContent->IsContentOfType(nsIContent::eXUL)) && nsStyleUtil::IsSimpleXlink(aContent, mPresContext, &mLinkState)) { mIsSimpleXLink = PR_TRUE; } @@ -3463,6 +3474,39 @@ RuleProcessorData::~RuleProcessorData() NS_IF_RELEASE(mStyledContent); } +const nsString* RuleProcessorData::GetLang(void) +{ + if (!mIsLanguageValid) { + mIsLanguageValid = PR_TRUE; + mLanguage.SetLength(0); + nsCOMPtr content = mContent; + while (content) { + PRInt32 attrCount = 0; + content->GetAttrCount(attrCount); + if (attrCount > 0) { + // xml:lang has precedence over lang on HTML elements (see + // XHTML1 section C.7). + nsAutoString value; + nsresult attrState = content->GetAttr(kNameSpaceID_XML, + nsHTMLAtoms::lang, value); + if (attrState != NS_CONTENT_ATTR_HAS_VALUE && + content->IsContentOfType(nsIContent::eHTML)) { + attrState = content->GetAttr(kNameSpaceID_None, + nsHTMLAtoms::lang, value); + } + if (attrState == NS_CONTENT_ATTR_HAS_VALUE) { + mLanguage = value; + break; + } + } + nsIContent *parent; + content->GetParent(parent); + content = dont_AddRef(parent); + } + } + return &mLanguage; +} + static const PRUnichar kNullCh = PRUnichar('\0'); static PRBool ValueIncludes(const nsString& aValueList, const nsString& aValue, PRBool aCaseSensitive) @@ -3564,6 +3608,39 @@ static PRBool IsSignificantChild(nsIContent* aChild, PRBool aAcceptNonWhitespace return PR_FALSE; } +static PRBool +DashMatchCompare(const nsAString& aAttributeValue, + const nsAString& aSelectorValue, + const PRBool aCaseSensitive) +{ + PRBool result; + PRUint32 selectorLen = aSelectorValue.Length(); + PRUint32 attributeLen = aAttributeValue.Length(); + if (selectorLen > attributeLen) { + result = PR_FALSE; + } + else { + nsAString::const_iterator iter; + if (selectorLen != attributeLen && + *aAttributeValue.BeginReading(iter).advance(selectorLen) != + PRUnichar('-')) { + // to match, the aAttributeValue must have a dash after the end of + // the aSelectorValue's text (unless the aSelectorValue and the + // aAttributeValue have the same text) + result = PR_FALSE; + } + else { + const nsAString& attributeSubstring = + Substring(aAttributeValue, 0, selectorLen); + if (aCaseSensitive) + result = attributeSubstring.Equals(aSelectorValue); + else + result = attributeSubstring.Equals(aSelectorValue, + nsCaseInsensitiveStringComparator()); + } + } + return result; +} // NOTE: The |aStateMask| code isn't going to work correctly anymore if // we start batching style changes, because if multiple states change in @@ -3606,7 +3683,7 @@ static PRBool SelectorMatches(RuleProcessorData &data, if (nsnull != aSelector->mPseudoClassList) { // test for pseudo class match // first-child, root, lang, active, focus, hover, link, visited... // XXX disabled, enabled, selected, selection - nsAtomList* pseudoClass = aSelector->mPseudoClassList; + nsAtomStringList* pseudoClass = aSelector->mPseudoClassList; while (result && (nsnull != pseudoClass)) { if ((nsCSSAtoms::firstChildPseudo == pseudoClass->mAtom) || @@ -3686,8 +3763,49 @@ static PRBool SelectorMatches(RuleProcessorData &data, ? localTrue : localFalse; } else if (nsCSSAtoms::langPseudo == pseudoClass->mAtom) { - // XXX not yet implemented - result = PR_FALSE; + NS_ASSERTION(nsnull != pseudoClass->mString, "null lang parameter"); + result = localFalse; + if (pseudoClass->mString && *pseudoClass->mString) { + // We have to determine the language of the current element. Since + // this is currently no property and since the language is inherited + // from the parent we have to be prepared to look at all parent + // nodes. The language itself is encoded in the LANG attribute. + const nsString* lang = data.GetLang(); + if (!lang->IsEmpty()) { + result = localTrue == DashMatchCompare(*lang, + nsDependentString(pseudoClass->mString), PR_FALSE); + } + else { + nsCOMPtr doc; + data.mContent->GetDocument(*getter_AddRefs(doc)); + if (doc) { + // Try to get the language from the HTTP header or if this + // is missing as well from the preferences. + // The content language can be a comma-separated list of + // language codes. + nsAutoString language; + if (NS_SUCCEEDED(doc->GetContentLanguage(language))) { + nsDependentString langString(pseudoClass->mString); + language.StripWhitespace(); + PRInt32 begin = 0; + PRInt32 len = language.Length(); + while (begin < len) { + PRInt32 end = language.FindChar(PRUnichar(','), begin); + if (end == kNotFound) { + end = len; + } + if (DashMatchCompare(Substring(language, begin, end-begin), + langString, + PR_FALSE)) { + result = localTrue; + break; + } + begin = end + 1; + } + } + } + } + } } else if (IsEventPseudo(pseudoClass->mAtom)) { // check if the element is event-sensitive @@ -3783,36 +3901,17 @@ static PRBool SelectorMatches(RuleProcessorData &data, switch (attr->mFunction) { case NS_ATTR_FUNC_EQUALS: if (isCaseSensitive) { - result = PRBool(localTrue == value.Equals(attr->mValue)); + result = localTrue == value.Equals(attr->mValue); } else { - result = PRBool(localTrue == value.Equals(attr->mValue, nsCaseInsensitiveStringComparator())); + result = localTrue == value.Equals(attr->mValue, nsCaseInsensitiveStringComparator()); } break; case NS_ATTR_FUNC_INCLUDES: - result = PRBool(localTrue == ValueIncludes(value, attr->mValue, isCaseSensitive)); + result = localTrue == ValueIncludes(value, attr->mValue, isCaseSensitive); break; case NS_ATTR_FUNC_DASHMATCH: - { - PRUint32 selLen = attr->mValue.Length(); - PRUint32 valLen = value.Length(); - if (selLen > valLen) { - result = localFalse; - } else { - if (selLen != valLen && - value.CharAt(selLen) != PRUnichar('-')) { - // to match, the value must have a dash after the end of - // the selector's text (unless the selector and the value - // have the same text) - result = localFalse; - break; - } - if (isCaseSensitive) - result = PRBool(localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsDefaultStringComparator())); - else - result = PRBool(localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator())); - } - } + result = localTrue == DashMatchCompare(value, attr->mValue, isCaseSensitive); break; case NS_ATTR_FUNC_ENDSMATCH: { @@ -3822,9 +3921,9 @@ static PRBool SelectorMatches(RuleProcessorData &data, result = localFalse; } else { if (isCaseSensitive) - result = PRBool(localTrue == Substring(value, valLen - selLen, selLen).Equals(attr->mValue, nsDefaultStringComparator())); + result = localTrue == Substring(value, valLen - selLen, selLen).Equals(attr->mValue, nsDefaultStringComparator()); else - result = PRBool(localTrue == Substring(value, valLen - selLen, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator())); + result = localTrue == Substring(value, valLen - selLen, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator()); } } break; @@ -3836,14 +3935,14 @@ static PRBool SelectorMatches(RuleProcessorData &data, result = localFalse; } else { if (isCaseSensitive) - result = PRBool(localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsDefaultStringComparator())); + result = localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsDefaultStringComparator()); else - result = PRBool(localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator())); + result = localTrue == Substring(value, 0, selLen).Equals(attr->mValue, nsCaseInsensitiveStringComparator()); } } break; case NS_ATTR_FUNC_CONTAINSMATCH: - result = PRBool(localTrue == (FindInReadable(attr->mValue, value, nsCaseInsensitiveStringComparator()))); + result = localTrue == (FindInReadable(attr->mValue, value, nsCaseInsensitiveStringComparator())); break; } } @@ -4210,6 +4309,7 @@ PRBool WeightedRulesSizeEnumFunc( nsISupports *aRule, void *aData ) { nsICSSStyleRule* rule = (nsICSSStyleRule*)aRule; CascadeSizeEnumData *pData = (CascadeSizeEnumData *)aData; + NS_ASSERTION(rule && pData, "null arguments not supported"); if(! pData->uniqueItems->AddItem((void*)rule)){ @@ -4335,7 +4435,7 @@ CSSRuleProcessor::ClearRuleCascades(void) inline PRBool IsStateSelector(nsCSSSelector& aSelector) { - nsAtomList* pseudoClass = aSelector.mPseudoClassList; + nsAtomStringList* pseudoClass = aSelector.mPseudoClassList; while (pseudoClass) { if ((pseudoClass->mAtom == nsCSSAtoms::activePseudo) || (pseudoClass->mAtom == nsCSSAtoms::checkedPseudo) || diff --git a/mozilla/layout/style/nsICSSStyleRule.h b/mozilla/layout/style/nsICSSStyleRule.h index 7d9e4e1b93f..f19ed554166 100644 --- a/mozilla/layout/style/nsICSSStyleRule.h +++ b/mozilla/layout/style/nsICSSStyleRule.h @@ -42,8 +42,10 @@ //#include #include "nsICSSRule.h" #include "nsString.h" +#include "nsCOMPtr.h" #include "nsCSSProps.h" #include "nsCSSValue.h" +#include "nsIAtom.h" class nsISizeOfHandler; @@ -60,8 +62,21 @@ public: ~nsAtomList(void); PRBool Equals(const nsAtomList* aOther) const; - nsIAtom* mAtom; - nsAtomList* mNext; + nsCOMPtr mAtom; + nsAtomList* mNext; +}; + +struct nsAtomStringList { +public: + nsAtomStringList(nsIAtom* aAtom, const PRUnichar *aString = nsnull); + nsAtomStringList(const nsString& aAtomValue, const PRUnichar *aString = nsnull); + nsAtomStringList(const nsAtomStringList& aCopy); + ~nsAtomStringList(void); + PRBool Equals(const nsAtomStringList* aOther) const; + + nsCOMPtr mAtom; + PRUnichar* mString; + nsAtomStringList* mNext; }; #define NS_ATTR_FUNC_SET 0 // [attr] @@ -112,8 +127,8 @@ public: void SetTag(const nsString& aTag); void AddID(const nsString& aID); void AddClass(const nsString& aClass); - void AddPseudoClass(const nsString& aPseudoClass); - void AddPseudoClass(nsIAtom* aPseudoClass); + void AddPseudoClass(const nsString& aPseudoClass, const PRUnichar* aString = nsnull); + void AddPseudoClass(nsIAtom* aPseudoClass, const PRUnichar* aString = nsnull); void AddAttribute(PRInt32 aNameSpace, const nsString& aAttr); void AddAttribute(PRInt32 aNameSpace, const nsString& aAttr, PRUint8 aFunc, const nsString& aValue, PRBool aCaseSensitive); @@ -133,10 +148,11 @@ private: public: PRInt32 mNameSpace; - nsIAtom* mTag; + nsCOMPtr mTag; nsAtomList* mIDList; nsAtomList* mClassList; - nsAtomList* mPseudoClassList; + nsAtomStringList* mPseudoClassList; // atom for the pseudo, string for + // the argument to functional pseudos nsAttrSelector* mAttrList; PRUnichar mOperator; nsCSSSelector* mNegations; diff --git a/mozilla/layout/style/nsIStyleRuleProcessor.h b/mozilla/layout/style/nsIStyleRuleProcessor.h index c3e7cb4c1f4..78e612019b8 100644 --- a/mozilla/layout/style/nsIStyleRuleProcessor.h +++ b/mozilla/layout/style/nsIStyleRuleProcessor.h @@ -43,6 +43,7 @@ #include "nsISupports.h" #include "nsIPresContext.h" // for nsCompatability #include "nsILinkHandler.h" +#include "nsString.h" class nsISizeOfHandler; @@ -78,6 +79,8 @@ struct RuleProcessorData { aContext->FreeToShell(sizeof(RuleProcessorData), this); }; + const nsString* GetLang(); + nsIPresContext* mPresContext; nsIContent* mContent; nsIContent* mParentContent; // if content, content->GetParent() @@ -98,6 +101,10 @@ struct RuleProcessorData { PRInt32 mNameSpaceID; // if content, content->GetNameSapce() RuleProcessorData* mPreviousSiblingData; RuleProcessorData* mParentData; + +protected: + nsAutoString mLanguage; + PRBool mIsLanguageValid; }; struct ElementRuleProcessorData : public RuleProcessorData { diff --git a/mozilla/layout/style/nsStyleContext.cpp b/mozilla/layout/style/nsStyleContext.cpp index 3bc83916c92..bd760db159a 100644 --- a/mozilla/layout/style/nsStyleContext.cpp +++ b/mozilla/layout/style/nsStyleContext.cpp @@ -565,7 +565,7 @@ nsStyleContext::CalcStyleDifference(nsIStyleContext* aOther, PRInt32& aHint) // We begin by examining those style structs that are capable of causing the maximal // difference, a FRAMECHANGE. - // FRAMECHANGE Structs: Display, XUL, Content, UserInterface + // FRAMECHANGE Structs: Display, XUL, Content, UserInterface, Quotes if (aHint < maxHint) { const nsStyleDisplay* display = (const nsStyleDisplay*)PeekStyleData(eStyleStruct_Display); if (display) { @@ -630,12 +630,26 @@ nsStyleContext::CalcStyleDifference(nsIStyleContext* aOther, PRInt32& aHint) } #endif + // If the quotes implementation is ever going to change we might not need + // a framechange here and a reflow should be sufficient. See bug 35768. + if (aHint < maxHint) { + const nsStyleQuotes* quotes = (const nsStyleQuotes*)PeekStyleData(eStyleStruct_Quotes); + if (quotes) { + const nsStyleQuotes* otherQuotes = (const nsStyleQuotes*)aOther->GetStyleData(eStyleStruct_Quotes); + if (quotes != otherQuotes) { + hint = quotes->CalcDifference(*otherQuotes); + if (aHint < hint) + aHint = hint; + } + } + } + // At this point, we know that the worst kind of damage we could do is a reflow. maxHint = NS_STYLE_HINT_REFLOW; // The following structs cause (as their maximal difference) a reflow to occur. // REFLOW Structs: Font, Margin, Padding, Border, List, Position, Text, TextReset, - // Visibility, Quotes, Table, TableBorder + // Visibility, Table, TableBorder if (aHint < maxHint) { const nsStyleFont* font = (const nsStyleFont*)PeekStyleData(eStyleStruct_Font); if (font) { @@ -768,18 +782,6 @@ nsStyleContext::CalcStyleDifference(nsIStyleContext* aOther, PRInt32& aHint) } } - if (aHint < maxHint) { - const nsStyleQuotes* quotes = (const nsStyleQuotes*)PeekStyleData(eStyleStruct_Quotes); - if (quotes) { - const nsStyleQuotes* otherQuotes = (const nsStyleQuotes*)aOther->GetStyleData(eStyleStruct_Quotes); - if (quotes != otherQuotes) { - hint = quotes->CalcDifference(*otherQuotes); - if (aHint < hint) - aHint = hint; - } - } - } - // At this point, we know that the worst kind of damage we could do is a re-render // (i.e., a VISUAL change). maxHint = NS_STYLE_HINT_VISUAL; diff --git a/mozilla/layout/style/nsStyleStruct.cpp b/mozilla/layout/style/nsStyleStruct.cpp index f8ca3208f5d..c9660ee1377 100644 --- a/mozilla/layout/style/nsStyleStruct.cpp +++ b/mozilla/layout/style/nsStyleStruct.cpp @@ -1246,17 +1246,19 @@ nsStyleQuotes::nsStyleQuotes(const nsStyleQuotes& aSource) PRInt32 nsStyleQuotes::CalcDifference(const nsStyleQuotes& aOther) const { + // If the quotes implementation is ever going to change we might not need + // a framechange here and a reflow should be sufficient. See bug 35768. if (mQuotesCount == aOther.mQuotesCount) { PRUint32 ix = (mQuotesCount * 2); while (0 < ix--) { if (mQuotes[ix] != aOther.mQuotes[ix]) { - return NS_STYLE_HINT_REFLOW; + return NS_STYLE_HINT_FRAMECHANGE; } } return NS_STYLE_HINT_NONE; } - return NS_STYLE_HINT_REFLOW; + return NS_STYLE_HINT_FRAMECHANGE; } // -------------------- diff --git a/mozilla/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp b/mozilla/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp index 3b81b1b8ab3..47ee1c0efe7 100644 --- a/mozilla/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp +++ b/mozilla/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp @@ -3306,7 +3306,7 @@ nsTreeBodyFrame::PseudoMatches(nsIAtom* aTag, nsCSSSelector* aSelector, PRBool* // it is contained in our scratch array. If we have a miss, then // we aren't a match. If all items in the pseudoclass list are // present in the scratch array, then we have a match. - nsAtomList* curr = aSelector->mPseudoClassList; + nsAtomStringList* curr = aSelector->mPseudoClassList; while (curr) { PRInt32 index; mScratchArray->GetIndexOf(curr->mAtom, &index);