diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index f5f959a4f67e..f7bae660207d 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1676,6 +1676,7 @@ PresShell::Init(nsIDocument* aDocument, nsCOMPtr os = do_GetService("@mozilla.org/observer-service;1", &result); if (os) { + os->AddObserver(this, NS_LINK_VISITED_EVENT_TOPIC, PR_FALSE); os->AddObserver(this, "agent-sheet-added", PR_FALSE); os->AddObserver(this, "user-sheet-added", PR_FALSE); os->AddObserver(this, "agent-sheet-removed", PR_FALSE); @@ -1743,6 +1744,7 @@ PresShell::Destroy() nsCOMPtr os = do_GetService("@mozilla.org/observer-service;1"); if (os) { + os->RemoveObserver(this, NS_LINK_VISITED_EVENT_TOPIC); os->RemoveObserver(this, "agent-sheet-added"); os->RemoveObserver(this, "user-sheet-added"); os->RemoveObserver(this, "agent-sheet-removed"); @@ -7585,6 +7587,14 @@ PresShell::Observe(nsISupports* aSubject, } #endif + if (!nsCRT::strcmp(aTopic, NS_LINK_VISITED_EVENT_TOPIC)) { + nsCOMPtr uri = do_QueryInterface(aSubject); + if (uri && mDocument) { + mDocument->NotifyURIVisitednessChanged(uri); + } + return NS_OK; + } + if (!nsCRT::strcmp(aTopic, "agent-sheet-added") && mStyleSet) { AddAgentSheet(aSubject); return NS_OK; diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index 854d4520794d..3c2da8e678a1 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -48,8 +48,7 @@ * order. The first argument to CSS_PSEUDO_CLASS is the C++ * identifier of the atom. The second argument is the string value of * the atom. CSS_STATE_PSEUDO_CLASS also takes the name of the state - * bits that the class corresponds to. Only one of the bits needs to - * match for the pseudo-class to match. If CSS_STATE_PSEUDO_CLASS is + * bit that the class corresponds to. If CSS_STATE_PSEUDO_CLASS is * not defined, it'll be automatically defined to CSS_PSEUDO_CLASS. */ @@ -74,11 +73,9 @@ CSS_PSEUDO_CLASS(notPseudo, ":not") CSS_PSEUDO_CLASS(mozBoundElement, ":-moz-bound-element") CSS_PSEUDO_CLASS(root, ":root") -CSS_STATE_PSEUDO_CLASS(link, ":link", NS_EVENT_STATE_UNVISITED) -// what matches :link or :visited -CSS_STATE_PSEUDO_CLASS(mozAnyLink, ":-moz-any-link", - NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED) -CSS_STATE_PSEUDO_CLASS(visited, ":visited", NS_EVENT_STATE_VISITED) +CSS_PSEUDO_CLASS(link, ":link") +CSS_PSEUDO_CLASS(mozAnyLink, ":-moz-any-link") // what matches :link or :visited +CSS_PSEUDO_CLASS(visited, ":visited") CSS_STATE_PSEUDO_CLASS(active, ":active", NS_EVENT_STATE_ACTIVE) CSS_STATE_PSEUDO_CLASS(checked, ":checked", NS_EVENT_STATE_CHECKED) diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 9b021bd75669..588b0578fca9 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -910,7 +910,8 @@ RuleProcessorData::RuleProcessorData(nsPresContext* aPresContext, mPreviousSiblingData(nsnull), mParentData(nsnull), mLanguage(nsnull), - mGotContentState(PR_FALSE) + mGotContentState(PR_FALSE), + mGotLinkInfo(PR_FALSE) { MOZ_COUNT_CTOR(RuleProcessorData); @@ -958,6 +959,9 @@ RuleProcessorData::RuleProcessorData(nsPresContext* aPresContext, mIsHTMLContent = (mNameSpaceID == kNameSpaceID_XHTML); mIsHTML = mIsHTMLContent && aContent->IsInHTMLDocument(); + // No need to initialize mIsLink or mLinkState; the IsLink() accessor will + // handle that. + // No need to initialize mContentState; the ContentState() accessor will handle // that. } @@ -1034,13 +1038,6 @@ RuleProcessorData::ContentState() } else { mContentState = mContent->IntrinsicState(); } - - // If we are not supposed to mark visited links as such, be sure to flip the - // bits appropriately. - if (!gSupportVisitedPseudo && (mContentState & NS_EVENT_STATE_VISITED)) { - mContentState = (mContentState & ~PRUint32(NS_EVENT_STATE_VISITED)) | - NS_EVENT_STATE_UNVISITED; - } } return mContentState; } @@ -1048,8 +1045,37 @@ RuleProcessorData::ContentState() PRBool RuleProcessorData::IsLink() { - PRUint32 state = ContentState(); - return (state & (NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) != 0; + if (!mGotLinkInfo) { + mGotLinkInfo = PR_TRUE; + mLinkState = eLinkState_Unknown; + mIsLink = PR_FALSE; + // 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) + nsILinkHandler* linkHandler = + mPresContext ? mPresContext->GetLinkHandler() : nsnull; + if (mIsHTMLContent && mHasAttributes) { + // check if it is an HTML Link + if (nsStyleUtil::IsHTMLLink(mContent, linkHandler, &mLinkState)) { + mIsLink = PR_TRUE; + } + } + + // 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 href) + if(!mIsLink && + mHasAttributes && + !(mIsHTMLContent || mContent->IsXUL()) && + nsStyleUtil::IsLink(mContent, linkHandler, &mLinkState)) { + mIsLink = PR_TRUE; + } + + if (mLinkState == eLinkState_Visited && !gSupportVisitedPseudo) { + mLinkState = eLinkState_Unvisited; + } + } + return mIsLink; } PRInt32 @@ -1611,6 +1637,31 @@ langMatches(RuleProcessorData& data, PRBool setNodeFlags, return PR_FALSE; } +static PRBool NS_FASTCALL +mozAnyLinkMatches(RuleProcessorData& data, PRBool setNodeFlags, + nsPseudoClassList* pseudoClass) +{ + NS_PRECONDITION(pseudoClass->mAtom == nsCSSPseudoClasses::mozAnyLink, + "Unexpected atom"); + return data.IsLink(); +} + +static PRBool NS_FASTCALL +linkMatches(RuleProcessorData& data, PRBool setNodeFlags, + nsPseudoClassList* pseudoClass) +{ + NS_NOTREACHED("Shouldn't be called"); + return PR_FALSE; +} + +static PRBool NS_FASTCALL +visitedMatches(RuleProcessorData& data, PRBool setNodeFlags, + nsPseudoClassList* pseudoClass) +{ + NS_NOTREACHED("Shouldn't be called"); + return PR_FALSE; +} + static PRBool NS_FASTCALL mozIsHTMLMatches(RuleProcessorData& data, PRBool setNodeFlags, nsPseudoClassList* pseudoClass) @@ -1684,13 +1735,13 @@ notPseudoMatches(RuleProcessorData& data, PRBool setNodeFlags, typedef PRBool (NS_FASTCALL * PseudoClassMatcher)(RuleProcessorData&, PRBool setNodeFlags, nsPseudoClassList* pseudoClass); -// Only one of mFunc or mBits will be set; the other will be null or 0 +// Only one of mFunc or mBit will be set; the other will be null or 0 // respectively. We could use a union, but then we'd still need to // differentiate somehow, eiher with another member in the struct or // with a boolean coming from _sowewhere_. struct PseudoClassInfo { PseudoClassMatcher mFunc; - PRInt32 mBits; + PRInt32 mBit; }; static const PseudoClassInfo sPseudoClassInfo[] = { @@ -1814,15 +1865,33 @@ static PRBool SelectorMatches(RuleProcessorData &data, // test for pseudo class match for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList; pseudoClass; pseudoClass = pseudoClass->mNext) { + // XXXbz special-case for :link and :visited, which are neither + // fish nor fowl + if (pseudoClass->mAtom == nsCSSPseudoClasses::link || + pseudoClass->mAtom == nsCSSPseudoClasses::visited) { + if (!data.IsLink()) { + return PR_FALSE; + } + + if (aStateMask & NS_EVENT_STATE_VISITED) { + if (aDependence) + *aDependence = PR_TRUE; + } else if ((eLinkState_Visited == data.LinkState()) == + (nsCSSPseudoClasses::link == pseudoClass->mAtom)) { + // Visited but :link or unvisited but :visited + return PR_FALSE; + } + continue; + } const PseudoClassInfo& info = sPseudoClassInfo[pseudoClass->mType]; if (info.mFunc) { if (!(*info.mFunc)(data, setNodeFlags, pseudoClass)) { return PR_FALSE; } } else { - PRInt32 statesToCheck = info.mBits; - NS_ABORT_IF_FALSE(statesToCheck != 0, "How did that happen?"); - if ((statesToCheck & (NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE)) && + PRInt32 stateToCheck = info.mBit; + NS_ABORT_IF_FALSE(stateToCheck != 0, "How did that happen?"); + if ((stateToCheck & (NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE)) && data.mCompatMode == eCompatibility_NavQuirks && // global selector (but don't check .class): !aSelector->HasTagSelector() && !aSelector->mIDList && @@ -1839,11 +1908,11 @@ static PRBool SelectorMatches(RuleProcessorData &data, // selectors ":hover" and ":active". return PR_FALSE; } else { - if (aStateMask & statesToCheck) { + if (aStateMask & stateToCheck) { if (aDependence) *aDependence = PR_TRUE; } else { - if (!(data.ContentState() & statesToCheck)) { + if (!(data.ContentState() & stateToCheck)) { return PR_FALSE; } } @@ -2255,6 +2324,28 @@ nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData // Don't do our special handling of certain attributes if the attr // hasn't changed yet. if (aData->mAttrHasChanged) { + // Since we always have :-moz-any-link (and almost always have :link + // and :visited rules from prefs), rather than hacking AddRule below + // to add |href| to the hash, we'll just handle it here. + if (aData->mAttribute == nsGkAtoms::href && + aData->mIsHTMLContent && + (aData->mContentTag == nsGkAtoms::a || + aData->mContentTag == nsGkAtoms::area || + aData->mContentTag == nsGkAtoms::link)) { + data.change = nsReStyleHint(data.change | eReStyle_Self); + } + // XXX What about XLinks? +#ifdef MOZ_SVG + // XXX should really check the attribute namespace is XLink + if (aData->mAttribute == nsGkAtoms::href && + aData->mNameSpaceID == kNameSpaceID_SVG && + aData->mContentTag == nsGkAtoms::a) { + data.change = nsReStyleHint(data.change | eReStyle_Self); + } +#endif + // XXXbz now that :link and :visited are also states, do we need a + // similar optimization in HasStateDependentStyle? + // check for the localedir, lwtheme and lwthemetextcolor attribute on root XUL elements if ((aData->mAttribute == nsGkAtoms::localedir || aData->mAttribute == nsGkAtoms::lwtheme || @@ -2371,7 +2462,11 @@ PRBool IsStateSelector(nsCSSSelector& aSelector) if (pseudoClass->mType >= nsCSSPseudoClasses::ePseudoClass_Count) { continue; } - if (sPseudoClassInfo[pseudoClass->mType].mBits) { + // XXXbz special-case for now for :link/:visited, since they're + // sorta-states-but-not-really right now. + if (sPseudoClassInfo[pseudoClass->mType].mBit || + pseudoClass->mAtom == nsCSSPseudoClasses::link || + pseudoClass->mAtom == nsCSSPseudoClasses::visited) { return PR_TRUE; } } diff --git a/layout/style/nsHTMLStyleSheet.cpp b/layout/style/nsHTMLStyleSheet.cpp index 77490689a12e..f9063c16ea99 100644 --- a/layout/style/nsHTMLStyleSheet.cpp +++ b/layout/style/nsHTMLStyleSheet.cpp @@ -238,18 +238,23 @@ nsHTMLStyleSheet::RulesMatching(ElementRuleProcessorData* aData) // if we have anchor colors, check if this is an anchor with an href if (tag == nsGkAtoms::a) { if (mLinkRule || mVisitedRule || mActiveRule) { - PRUint32 state = aData->ContentState(); - if (mLinkRule && (state & NS_EVENT_STATE_UNVISITED)) { - ruleWalker->Forward(mLinkRule); - } - else if (mVisitedRule && (state & NS_EVENT_STATE_VISITED)) { - ruleWalker->Forward(mVisitedRule); - } + if (aData->IsLink()) { + switch (aData->LinkState()) { + case eLinkState_Unvisited: + if (mLinkRule) + ruleWalker->Forward(mLinkRule); + break; + case eLinkState_Visited: + if (mVisitedRule) + ruleWalker->Forward(mVisitedRule); + break; + default: + break; + } - // No need to add to the active rule if it's not a link - if (mActiveRule && aData->IsLink() && - (state & NS_EVENT_STATE_ACTIVE)) { - ruleWalker->Forward(mActiveRule); + // No need to add to the active rule if it's not a link + if (mActiveRule && (aData->ContentState() & NS_EVENT_STATE_ACTIVE)) + ruleWalker->Forward(mActiveRule); } } // end link/visited/active rules } // end A tag diff --git a/layout/style/nsRuleProcessorData.h b/layout/style/nsRuleProcessorData.h index 25caa745f8c8..26e21b8c529b 100644 --- a/layout/style/nsRuleProcessorData.h +++ b/layout/style/nsRuleProcessorData.h @@ -109,6 +109,10 @@ public: const nsString* GetLang(); PRUint32 ContentState(); PRBool IsLink(); + nsLinkState LinkState() { + NS_ASSERTION(mGotLinkInfo && mIsLink, "Why am I being called?"); + return mLinkState; + } // Returns a 1-based index of the child in its parent. If the child // is not in its parent's child list (i.e., it is anonymous content), @@ -153,7 +157,11 @@ private: // mContentState, mLinkState, mIsLink are initialized lazily. PRInt32 mContentState; // eventStateMgr->GetContentState() or // mContent->IntrinsicState() if we have no ESM + nsLinkState mLinkState; // if a link, this is the state, otherwise unknown + PRPackedBool mIsLink; // nsStyleUtil::IsHTMLLink or nsStyleUtil::IsLink PRPackedBool mGotContentState; + PRPackedBool mGotLinkInfo; // Whether we've gotten the right values + // for mLinkState and mIsLink. }; struct ElementRuleProcessorData : public RuleProcessorData {