michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: * michael@0: * This Original Code has been modified by IBM Corporation. Modifications made by IBM michael@0: * described herein are Copyright (c) International Business Machines Corporation, 2000. michael@0: * Modifications to Mozilla code or documentation identified per MPL Section 3.3 michael@0: * michael@0: * Date Modified by Description of modification michael@0: * 04/20/2000 IBM Corp. OS/2 VisualAge build. michael@0: */ michael@0: michael@0: /* michael@0: * style sheet and style rule processor representing data from presentational michael@0: * HTML attributes michael@0: */ michael@0: michael@0: #include "nsHTMLStyleSheet.h" michael@0: #include "nsMappedAttributes.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsPresContext.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsRuleWalker.h" michael@0: #include "nsRuleData.h" michael@0: #include "nsError.h" michael@0: #include "nsRuleProcessorData.h" michael@0: #include "nsCSSRuleProcessor.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsHashKeys.h" michael@0: #include "RestyleManager.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsHTMLStyleSheet::HTMLColorRule, nsIStyleRule) michael@0: michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::HTMLColorRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Color)) { michael@0: nsCSSValue* color = aRuleData->ValueForColor(); michael@0: if (color->GetUnit() == eCSSUnit_Null && michael@0: aRuleData->mPresContext->UseDocumentColors()) michael@0: color->SetColorValue(mColor); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::HTMLColorRule::List(FILE* out, int32_t aIndent) const michael@0: { michael@0: for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); michael@0: fputs("[html color rule] {}\n", out); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(nsHTMLStyleSheet::GenericTableRule, nsIStyleRule) michael@0: michael@0: #ifdef DEBUG michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::GenericTableRule::List(FILE* out, int32_t aIndent) const michael@0: { michael@0: for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); michael@0: fputs("[generic table rule] {}\n", out); michael@0: } michael@0: #endif michael@0: michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::TableTHRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) { michael@0: nsCSSValue* textAlign = aRuleData->ValueForTextAlign(); michael@0: if (textAlign->GetUnit() == eCSSUnit_Null) { michael@0: textAlign->SetIntValue(NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT, michael@0: eCSSUnit_Enumerated); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::TableQuirkColorRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Color)) { michael@0: nsCSSValue* color = aRuleData->ValueForColor(); michael@0: // We do not check UseDocumentColors() here, because we want to michael@0: // use the body color no matter what. michael@0: if (color->GetUnit() == eCSSUnit_Null) michael@0: color->SetIntValue(NS_STYLE_COLOR_INHERIT_FROM_BODY, michael@0: eCSSUnit_Enumerated); michael@0: } michael@0: } michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(nsHTMLStyleSheet::LangRule, nsIStyleRule) michael@0: michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::LangRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) { michael@0: nsCSSValue* lang = aRuleData->ValueForLang(); michael@0: if (lang->GetUnit() == eCSSUnit_Null) { michael@0: lang->SetStringValue(mLang, eCSSUnit_Ident); michael@0: } michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::LangRule::List(FILE* out, int32_t aIndent) const michael@0: { michael@0: for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); michael@0: fputs("[lang rule] { language: \"", out); michael@0: fputs(NS_ConvertUTF16toUTF8(mLang).get(), out); michael@0: fputs("\" }\n", out); michael@0: } michael@0: #endif michael@0: michael@0: // ----------------------------------------------------------- michael@0: michael@0: struct MappedAttrTableEntry : public PLDHashEntryHdr { michael@0: nsMappedAttributes *mAttributes; michael@0: }; michael@0: michael@0: static PLDHashNumber michael@0: MappedAttrTable_HashKey(PLDHashTable *table, const void *key) michael@0: { michael@0: nsMappedAttributes *attributes = michael@0: static_cast(const_cast(key)); michael@0: michael@0: return attributes->HashValue(); michael@0: } michael@0: michael@0: static void michael@0: MappedAttrTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) michael@0: { michael@0: MappedAttrTableEntry *entry = static_cast(hdr); michael@0: michael@0: entry->mAttributes->DropStyleSheetReference(); michael@0: memset(entry, 0, sizeof(MappedAttrTableEntry)); michael@0: } michael@0: michael@0: static bool michael@0: MappedAttrTable_MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, michael@0: const void *key) michael@0: { michael@0: nsMappedAttributes *attributes = michael@0: static_cast(const_cast(key)); michael@0: const MappedAttrTableEntry *entry = michael@0: static_cast(hdr); michael@0: michael@0: return attributes->Equals(entry->mAttributes); michael@0: } michael@0: michael@0: static const PLDHashTableOps MappedAttrTable_Ops = { michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: MappedAttrTable_HashKey, michael@0: MappedAttrTable_MatchEntry, michael@0: PL_DHashMoveEntryStub, michael@0: MappedAttrTable_ClearEntry, michael@0: PL_DHashFinalizeStub, michael@0: nullptr michael@0: }; michael@0: michael@0: // ----------------------------------------------------------- michael@0: michael@0: struct LangRuleTableEntry : public PLDHashEntryHdr { michael@0: nsRefPtr mRule; michael@0: }; michael@0: michael@0: static PLDHashNumber michael@0: LangRuleTable_HashKey(PLDHashTable *table, const void *key) michael@0: { michael@0: const nsString *lang = static_cast(key); michael@0: return HashString(*lang); michael@0: } michael@0: michael@0: static void michael@0: LangRuleTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) michael@0: { michael@0: LangRuleTableEntry *entry = static_cast(hdr); michael@0: michael@0: entry->~LangRuleTableEntry(); michael@0: memset(entry, 0, sizeof(LangRuleTableEntry)); michael@0: } michael@0: michael@0: static bool michael@0: LangRuleTable_MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, michael@0: const void *key) michael@0: { michael@0: const nsString *lang = static_cast(key); michael@0: const LangRuleTableEntry *entry = static_cast(hdr); michael@0: michael@0: return entry->mRule->mLang == *lang; michael@0: } michael@0: michael@0: static bool michael@0: LangRuleTable_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: const void *key) michael@0: { michael@0: const nsString *lang = static_cast(key); michael@0: michael@0: LangRuleTableEntry *entry = new (hdr) LangRuleTableEntry(); michael@0: michael@0: // Create the unique rule for this language michael@0: entry->mRule = new nsHTMLStyleSheet::LangRule(*lang); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static const PLDHashTableOps LangRuleTable_Ops = { michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: LangRuleTable_HashKey, michael@0: LangRuleTable_MatchEntry, michael@0: PL_DHashMoveEntryStub, michael@0: LangRuleTable_ClearEntry, michael@0: PL_DHashFinalizeStub, michael@0: LangRuleTable_InitEntry michael@0: }; michael@0: michael@0: // ----------------------------------------------------------- michael@0: michael@0: nsHTMLStyleSheet::nsHTMLStyleSheet(nsIDocument* aDocument) michael@0: : mDocument(aDocument) michael@0: , mTableQuirkColorRule(new TableQuirkColorRule()) michael@0: , mTableTHRule(new TableTHRule()) michael@0: { michael@0: MOZ_ASSERT(aDocument); michael@0: mMappedAttrTable.ops = nullptr; michael@0: mLangRuleTable.ops = nullptr; michael@0: } michael@0: michael@0: nsHTMLStyleSheet::~nsHTMLStyleSheet() michael@0: { michael@0: if (mLangRuleTable.ops) michael@0: PL_DHashTableFinish(&mLangRuleTable); michael@0: if (mMappedAttrTable.ops) michael@0: PL_DHashTableFinish(&mMappedAttrTable); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsHTMLStyleSheet, nsIStyleRuleProcessor) michael@0: michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::RulesMatching(ElementRuleProcessorData* aData) michael@0: { michael@0: nsRuleWalker *ruleWalker = aData->mRuleWalker; michael@0: if (aData->mElement->IsHTML() && !ruleWalker->AuthorStyleDisabled()) { michael@0: nsIAtom* tag = aData->mElement->Tag(); michael@0: michael@0: // if we have anchor colors, check if this is an anchor with an href michael@0: if (tag == nsGkAtoms::a) { michael@0: if (mLinkRule || mVisitedRule || mActiveRule) { michael@0: EventStates state = michael@0: nsCSSRuleProcessor::GetContentStateForVisitedHandling( michael@0: aData->mElement, michael@0: aData->mTreeMatchContext, michael@0: aData->mTreeMatchContext.VisitedHandling(), michael@0: // If the node being matched is a link, michael@0: // it's the relevant link. michael@0: nsCSSRuleProcessor::IsLink(aData->mElement)); michael@0: if (mLinkRule && state.HasState(NS_EVENT_STATE_UNVISITED)) { michael@0: ruleWalker->Forward(mLinkRule); michael@0: aData->mTreeMatchContext.SetHaveRelevantLink(); michael@0: } michael@0: else if (mVisitedRule && state.HasState(NS_EVENT_STATE_VISITED)) { michael@0: ruleWalker->Forward(mVisitedRule); michael@0: aData->mTreeMatchContext.SetHaveRelevantLink(); michael@0: } michael@0: michael@0: // No need to add to the active rule if it's not a link michael@0: if (mActiveRule && nsCSSRuleProcessor::IsLink(aData->mElement) && michael@0: state.HasState(NS_EVENT_STATE_ACTIVE)) { michael@0: ruleWalker->Forward(mActiveRule); michael@0: } michael@0: } // end link/visited/active rules michael@0: } // end A tag michael@0: // add the rule to handle text-align for a michael@0: else if (tag == nsGkAtoms::th) { michael@0: ruleWalker->Forward(mTableTHRule); michael@0: } michael@0: else if (tag == nsGkAtoms::table) { michael@0: if (aData->mTreeMatchContext.mCompatMode == eCompatibility_NavQuirks) { michael@0: ruleWalker->Forward(mTableQuirkColorRule); michael@0: } michael@0: } michael@0: } // end html element michael@0: michael@0: // just get the style rules from the content. For SVG we do this even if michael@0: // author style is disabled, because SVG presentational hints aren't michael@0: // considered style. michael@0: if (!ruleWalker->AuthorStyleDisabled() || aData->mElement->IsSVG()) { michael@0: aData->mElement->WalkContentStyleRules(ruleWalker); michael@0: } michael@0: michael@0: // http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language michael@0: // says that the xml:lang attribute overrides HTML's lang attribute, michael@0: // so we need to do this after WalkContentStyleRules. michael@0: nsString lang; michael@0: if (aData->mElement->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang, lang)) { michael@0: ruleWalker->Forward(LangRuleFor(lang)); michael@0: } michael@0: } michael@0: michael@0: // Test if style is dependent on content state michael@0: /* virtual */ nsRestyleHint michael@0: nsHTMLStyleSheet::HasStateDependentStyle(StateRuleProcessorData* aData) michael@0: { michael@0: if (aData->mElement->IsHTML(nsGkAtoms::a) && michael@0: nsCSSRuleProcessor::IsLink(aData->mElement) && michael@0: ((mActiveRule && aData->mStateMask.HasState(NS_EVENT_STATE_ACTIVE)) || michael@0: (mLinkRule && aData->mStateMask.HasState(NS_EVENT_STATE_VISITED)) || michael@0: (mVisitedRule && aData->mStateMask.HasState(NS_EVENT_STATE_VISITED)))) { michael@0: return eRestyle_Self; michael@0: } michael@0: michael@0: return nsRestyleHint(0); michael@0: } michael@0: michael@0: /* virtual */ nsRestyleHint michael@0: nsHTMLStyleSheet::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) michael@0: { michael@0: return nsRestyleHint(0); michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsHTMLStyleSheet::HasDocumentStateDependentStyle(StateRuleProcessorData* aData) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: /* virtual */ nsRestyleHint michael@0: nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) michael@0: { michael@0: // Do nothing on before-change checks michael@0: if (!aData->mAttrHasChanged) { michael@0: return nsRestyleHint(0); michael@0: } michael@0: michael@0: // Note: no need to worry about whether some states changed with this michael@0: // attribute here, because we handle that under HasStateDependentStyle() as michael@0: // needed. michael@0: michael@0: // Result is true for |href| changes on HTML links if we have link rules. michael@0: Element *element = aData->mElement; michael@0: if (aData->mAttribute == nsGkAtoms::href && michael@0: (mLinkRule || mVisitedRule || mActiveRule) && michael@0: element->IsHTML(nsGkAtoms::a)) { michael@0: return eRestyle_Self; michael@0: } michael@0: michael@0: // Don't worry about the mDocumentColorRule since it only applies michael@0: // to descendants of body, when we're already reresolving. michael@0: michael@0: // Handle the content style rules. michael@0: if (element->IsAttributeMapped(aData->mAttribute)) { michael@0: // cellpadding on tables is special and requires reresolving all michael@0: // the cells in the table michael@0: if (aData->mAttribute == nsGkAtoms::cellpadding && michael@0: element->IsHTML(nsGkAtoms::table)) { michael@0: return eRestyle_Subtree; michael@0: } michael@0: return eRestyle_Self; michael@0: } michael@0: michael@0: return nsRestyleHint(0); michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsHTMLStyleSheet::MediumFeaturesChanged(nsPresContext* aPresContext) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: /* virtual */ size_t michael@0: nsHTMLStyleSheet::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return 0; // nsHTMLStyleSheets are charged to the DOM, not layout michael@0: } michael@0: michael@0: /* virtual */ size_t michael@0: nsHTMLStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return 0; // nsHTMLStyleSheets are charged to the DOM, not layout michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::RulesMatching(PseudoElementRuleProcessorData* aData) michael@0: { michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::RulesMatching(AnonBoxRuleProcessorData* aData) michael@0: { michael@0: } michael@0: michael@0: #ifdef MOZ_XUL michael@0: /* virtual */ void michael@0: nsHTMLStyleSheet::RulesMatching(XULTreeRuleProcessorData* aData) michael@0: { michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsHTMLStyleSheet::SetOwningDocument(nsIDocument* aDocument) michael@0: { michael@0: mDocument = aDocument; // not refcounted michael@0: } michael@0: michael@0: void michael@0: nsHTMLStyleSheet::Reset() michael@0: { michael@0: mLinkRule = nullptr; michael@0: mVisitedRule = nullptr; michael@0: mActiveRule = nullptr; michael@0: michael@0: if (mLangRuleTable.ops) { michael@0: PL_DHashTableFinish(&mLangRuleTable); michael@0: mLangRuleTable.ops = nullptr; michael@0: } michael@0: if (mMappedAttrTable.ops) { michael@0: PL_DHashTableFinish(&mMappedAttrTable); michael@0: mMappedAttrTable.ops = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLStyleSheet::ImplLinkColorSetter(nsRefPtr& aRule, nscolor aColor) michael@0: { michael@0: if (aRule && aRule->mColor == aColor) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: aRule = new HTMLColorRule(); michael@0: if (!aRule) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: aRule->mColor = aColor; michael@0: // Now make sure we restyle any links that might need it. This michael@0: // shouldn't happen often, so just rebuilding everything is ok. michael@0: if (mDocument && mDocument->GetShell()) { michael@0: Element* root = mDocument->GetRootElement(); michael@0: if (root) { michael@0: mDocument->GetShell()->GetPresContext()->RestyleManager()-> michael@0: PostRestyleEvent(root, eRestyle_Subtree, NS_STYLE_HINT_NONE); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLStyleSheet::SetLinkColor(nscolor aColor) michael@0: { michael@0: return ImplLinkColorSetter(mLinkRule, aColor); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsHTMLStyleSheet::SetActiveLinkColor(nscolor aColor) michael@0: { michael@0: return ImplLinkColorSetter(mActiveRule, aColor); michael@0: } michael@0: michael@0: nsresult michael@0: nsHTMLStyleSheet::SetVisitedLinkColor(nscolor aColor) michael@0: { michael@0: return ImplLinkColorSetter(mVisitedRule, aColor); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsHTMLStyleSheet::UniqueMappedAttributes(nsMappedAttributes* aMapped) michael@0: { michael@0: if (!mMappedAttrTable.ops) { michael@0: PL_DHashTableInit(&mMappedAttrTable, &MappedAttrTable_Ops, michael@0: nullptr, sizeof(MappedAttrTableEntry), 16); michael@0: } michael@0: MappedAttrTableEntry *entry = static_cast michael@0: (PL_DHashTableOperate(&mMappedAttrTable, aMapped, PL_DHASH_ADD)); michael@0: if (!entry) michael@0: return nullptr; michael@0: if (!entry->mAttributes) { michael@0: // We added a new entry to the hashtable, so we have a new unique set. michael@0: entry->mAttributes = aMapped; michael@0: } michael@0: nsRefPtr ret = entry->mAttributes; michael@0: return ret.forget(); michael@0: } michael@0: michael@0: void michael@0: nsHTMLStyleSheet::DropMappedAttributes(nsMappedAttributes* aMapped) michael@0: { michael@0: NS_ENSURE_TRUE_VOID(aMapped); michael@0: michael@0: NS_ASSERTION(mMappedAttrTable.ops, "table uninitialized"); michael@0: #ifdef DEBUG michael@0: uint32_t entryCount = mMappedAttrTable.entryCount - 1; michael@0: #endif michael@0: michael@0: PL_DHashTableOperate(&mMappedAttrTable, aMapped, PL_DHASH_REMOVE); michael@0: michael@0: NS_ASSERTION(entryCount == mMappedAttrTable.entryCount, "not removed"); michael@0: } michael@0: michael@0: nsIStyleRule* michael@0: nsHTMLStyleSheet::LangRuleFor(const nsString& aLanguage) michael@0: { michael@0: if (!mLangRuleTable.ops) { michael@0: PL_DHashTableInit(&mLangRuleTable, &LangRuleTable_Ops, michael@0: nullptr, sizeof(LangRuleTableEntry), 16); michael@0: } michael@0: LangRuleTableEntry *entry = static_cast michael@0: (PL_DHashTableOperate(&mLangRuleTable, &aLanguage, PL_DHASH_ADD)); michael@0: if (!entry) { michael@0: NS_ASSERTION(false, "out of memory"); michael@0: return nullptr; michael@0: } michael@0: return entry->mRule; michael@0: } michael@0: michael@0: static size_t michael@0: SizeOfAttributesEntryExcludingThis(PLDHashEntryHdr* aEntry, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aArg) michael@0: { michael@0: NS_PRECONDITION(aEntry, "The entry should not be null!"); michael@0: michael@0: MappedAttrTableEntry* entry = static_cast(aEntry); michael@0: NS_ASSERTION(entry->mAttributes, "entry->mAttributes should not be null!"); michael@0: return entry->mAttributes->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: size_t michael@0: nsHTMLStyleSheet::DOMSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: michael@0: if (mMappedAttrTable.ops) { michael@0: n += PL_DHashTableSizeOfExcludingThis(&mMappedAttrTable, michael@0: SizeOfAttributesEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: } michael@0: michael@0: // Measurement of the following members may be added later if DMD finds it is michael@0: // worthwhile: michael@0: // - mURL michael@0: // - mLinkRule michael@0: // - mVisitedRule michael@0: // - mActiveRule michael@0: // - mTableQuirkColorRule michael@0: // - mTableTHRule michael@0: // - mLangRuleTable michael@0: // michael@0: // The following members are not measured: michael@0: // - mDocument, because it's non-owning michael@0: michael@0: return n; michael@0: }