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