1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 * This Original Code has been modified by IBM Corporation. Modifications made by IBM
8 * described herein are Copyright (c) International Business Machines Corporation, 2000.
9 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
11 * Date Modified by Description of modification
12 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
16 * style sheet and style rule processor representing data from presentational
20 #include "nsHTMLStyleSheet.h"
21 #include "nsMappedAttributes.h"
22 #include "nsGkAtoms.h"
23 #include "nsPresContext.h"
24 #include "mozilla/EventStates.h"
25 #include "nsIDocument.h"
26 #include "nsIPresShell.h"
27 #include "nsStyleConsts.h"
28 #include "nsRuleWalker.h"
29 #include "nsRuleData.h"
31 #include "nsRuleProcessorData.h"
32 #include "nsCSSRuleProcessor.h"
33 #include "mozilla/MemoryReporting.h"
34 #include "mozilla/dom/Element.h"
35 #include "nsHashKeys.h"
36 #include "RestyleManager.h"
38 using namespace mozilla
;
39 using namespace mozilla::dom
;
41 NS_IMPL_ISUPPORTS(nsHTMLStyleSheet::HTMLColorRule
, nsIStyleRule
)
44 nsHTMLStyleSheet::HTMLColorRule::MapRuleInfoInto(nsRuleData
* aRuleData
)
46 if (aRuleData
->mSIDs
& NS_STYLE_INHERIT_BIT(Color
)) {
47 nsCSSValue
* color
= aRuleData
->ValueForColor();
48 if (color
->GetUnit() == eCSSUnit_Null
&&
49 aRuleData
->mPresContext
->UseDocumentColors())
50 color
->SetColorValue(mColor
);
56 nsHTMLStyleSheet::HTMLColorRule::List(FILE* out
, int32_t aIndent
) const
58 for (int32_t index
= aIndent
; --index
>= 0; ) fputs(" ", out
);
59 fputs("[html color rule] {}\n", out
);
64 NS_IMPL_ISUPPORTS(nsHTMLStyleSheet::GenericTableRule
, nsIStyleRule
)
68 nsHTMLStyleSheet::GenericTableRule::List(FILE* out
, int32_t aIndent
) const
70 for (int32_t index
= aIndent
; --index
>= 0; ) fputs(" ", out
);
71 fputs("[generic table rule] {}\n", out
);
76 nsHTMLStyleSheet::TableTHRule::MapRuleInfoInto(nsRuleData
* aRuleData
)
78 if (aRuleData
->mSIDs
& NS_STYLE_INHERIT_BIT(Text
)) {
79 nsCSSValue
* textAlign
= aRuleData
->ValueForTextAlign();
80 if (textAlign
->GetUnit() == eCSSUnit_Null
) {
81 textAlign
->SetIntValue(NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT
,
88 nsHTMLStyleSheet::TableQuirkColorRule::MapRuleInfoInto(nsRuleData
* aRuleData
)
90 if (aRuleData
->mSIDs
& NS_STYLE_INHERIT_BIT(Color
)) {
91 nsCSSValue
* color
= aRuleData
->ValueForColor();
92 // We do not check UseDocumentColors() here, because we want to
93 // use the body color no matter what.
94 if (color
->GetUnit() == eCSSUnit_Null
)
95 color
->SetIntValue(NS_STYLE_COLOR_INHERIT_FROM_BODY
,
101 NS_IMPL_ISUPPORTS(nsHTMLStyleSheet::LangRule
, nsIStyleRule
)
104 nsHTMLStyleSheet::LangRule::MapRuleInfoInto(nsRuleData
* aRuleData
)
106 if (aRuleData
->mSIDs
& NS_STYLE_INHERIT_BIT(Font
)) {
107 nsCSSValue
* lang
= aRuleData
->ValueForLang();
108 if (lang
->GetUnit() == eCSSUnit_Null
) {
109 lang
->SetStringValue(mLang
, eCSSUnit_Ident
);
116 nsHTMLStyleSheet::LangRule::List(FILE* out
, int32_t aIndent
) const
118 for (int32_t index
= aIndent
; --index
>= 0; ) fputs(" ", out
);
119 fputs("[lang rule] { language: \"", out
);
120 fputs(NS_ConvertUTF16toUTF8(mLang
).get(), out
);
121 fputs("\" }\n", out
);
125 // -----------------------------------------------------------
127 struct MappedAttrTableEntry
: public PLDHashEntryHdr
{
128 nsMappedAttributes
*mAttributes
;
132 MappedAttrTable_HashKey(PLDHashTable
*table
, const void *key
)
134 nsMappedAttributes
*attributes
=
135 static_cast<nsMappedAttributes
*>(const_cast<void*>(key
));
137 return attributes
->HashValue();
141 MappedAttrTable_ClearEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
)
143 MappedAttrTableEntry
*entry
= static_cast<MappedAttrTableEntry
*>(hdr
);
145 entry
->mAttributes
->DropStyleSheetReference();
146 memset(entry
, 0, sizeof(MappedAttrTableEntry
));
150 MappedAttrTable_MatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
153 nsMappedAttributes
*attributes
=
154 static_cast<nsMappedAttributes
*>(const_cast<void*>(key
));
155 const MappedAttrTableEntry
*entry
=
156 static_cast<const MappedAttrTableEntry
*>(hdr
);
158 return attributes
->Equals(entry
->mAttributes
);
161 static const PLDHashTableOps MappedAttrTable_Ops
= {
164 MappedAttrTable_HashKey
,
165 MappedAttrTable_MatchEntry
,
166 PL_DHashMoveEntryStub
,
167 MappedAttrTable_ClearEntry
,
168 PL_DHashFinalizeStub
,
172 // -----------------------------------------------------------
174 struct LangRuleTableEntry
: public PLDHashEntryHdr
{
175 nsRefPtr
<nsHTMLStyleSheet::LangRule
> mRule
;
179 LangRuleTable_HashKey(PLDHashTable
*table
, const void *key
)
181 const nsString
*lang
= static_cast<const nsString
*>(key
);
182 return HashString(*lang
);
186 LangRuleTable_ClearEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
)
188 LangRuleTableEntry
*entry
= static_cast<LangRuleTableEntry
*>(hdr
);
190 entry
->~LangRuleTableEntry();
191 memset(entry
, 0, sizeof(LangRuleTableEntry
));
195 LangRuleTable_MatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
198 const nsString
*lang
= static_cast<const nsString
*>(key
);
199 const LangRuleTableEntry
*entry
= static_cast<const LangRuleTableEntry
*>(hdr
);
201 return entry
->mRule
->mLang
== *lang
;
205 LangRuleTable_InitEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
208 const nsString
*lang
= static_cast<const nsString
*>(key
);
210 LangRuleTableEntry
*entry
= new (hdr
) LangRuleTableEntry();
212 // Create the unique rule for this language
213 entry
->mRule
= new nsHTMLStyleSheet::LangRule(*lang
);
218 static const PLDHashTableOps LangRuleTable_Ops
= {
221 LangRuleTable_HashKey
,
222 LangRuleTable_MatchEntry
,
223 PL_DHashMoveEntryStub
,
224 LangRuleTable_ClearEntry
,
225 PL_DHashFinalizeStub
,
226 LangRuleTable_InitEntry
229 // -----------------------------------------------------------
231 nsHTMLStyleSheet::nsHTMLStyleSheet(nsIDocument
* aDocument
)
232 : mDocument(aDocument
)
233 , mTableQuirkColorRule(new TableQuirkColorRule())
234 , mTableTHRule(new TableTHRule())
236 MOZ_ASSERT(aDocument
);
237 mMappedAttrTable
.ops
= nullptr;
238 mLangRuleTable
.ops
= nullptr;
241 nsHTMLStyleSheet::~nsHTMLStyleSheet()
243 if (mLangRuleTable
.ops
)
244 PL_DHashTableFinish(&mLangRuleTable
);
245 if (mMappedAttrTable
.ops
)
246 PL_DHashTableFinish(&mMappedAttrTable
);
249 NS_IMPL_ISUPPORTS(nsHTMLStyleSheet
, nsIStyleRuleProcessor
)
252 nsHTMLStyleSheet::RulesMatching(ElementRuleProcessorData
* aData
)
254 nsRuleWalker
*ruleWalker
= aData
->mRuleWalker
;
255 if (aData
->mElement
->IsHTML() && !ruleWalker
->AuthorStyleDisabled()) {
256 nsIAtom
* tag
= aData
->mElement
->Tag();
258 // if we have anchor colors, check if this is an anchor with an href
259 if (tag
== nsGkAtoms::a
) {
260 if (mLinkRule
|| mVisitedRule
|| mActiveRule
) {
262 nsCSSRuleProcessor::GetContentStateForVisitedHandling(
264 aData
->mTreeMatchContext
,
265 aData
->mTreeMatchContext
.VisitedHandling(),
266 // If the node being matched is a link,
267 // it's the relevant link.
268 nsCSSRuleProcessor::IsLink(aData
->mElement
));
269 if (mLinkRule
&& state
.HasState(NS_EVENT_STATE_UNVISITED
)) {
270 ruleWalker
->Forward(mLinkRule
);
271 aData
->mTreeMatchContext
.SetHaveRelevantLink();
273 else if (mVisitedRule
&& state
.HasState(NS_EVENT_STATE_VISITED
)) {
274 ruleWalker
->Forward(mVisitedRule
);
275 aData
->mTreeMatchContext
.SetHaveRelevantLink();
278 // No need to add to the active rule if it's not a link
279 if (mActiveRule
&& nsCSSRuleProcessor::IsLink(aData
->mElement
) &&
280 state
.HasState(NS_EVENT_STATE_ACTIVE
)) {
281 ruleWalker
->Forward(mActiveRule
);
283 } // end link/visited/active rules
285 // add the rule to handle text-align for a <th>
286 else if (tag
== nsGkAtoms::th
) {
287 ruleWalker
->Forward(mTableTHRule
);
289 else if (tag
== nsGkAtoms::table
) {
290 if (aData
->mTreeMatchContext
.mCompatMode
== eCompatibility_NavQuirks
) {
291 ruleWalker
->Forward(mTableQuirkColorRule
);
294 } // end html element
296 // just get the style rules from the content. For SVG we do this even if
297 // author style is disabled, because SVG presentational hints aren't
299 if (!ruleWalker
->AuthorStyleDisabled() || aData
->mElement
->IsSVG()) {
300 aData
->mElement
->WalkContentStyleRules(ruleWalker
);
303 // http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language
304 // says that the xml:lang attribute overrides HTML's lang attribute,
305 // so we need to do this after WalkContentStyleRules.
307 if (aData
->mElement
->GetAttr(kNameSpaceID_XML
, nsGkAtoms::lang
, lang
)) {
308 ruleWalker
->Forward(LangRuleFor(lang
));
312 // Test if style is dependent on content state
313 /* virtual */ nsRestyleHint
314 nsHTMLStyleSheet::HasStateDependentStyle(StateRuleProcessorData
* aData
)
316 if (aData
->mElement
->IsHTML(nsGkAtoms::a
) &&
317 nsCSSRuleProcessor::IsLink(aData
->mElement
) &&
318 ((mActiveRule
&& aData
->mStateMask
.HasState(NS_EVENT_STATE_ACTIVE
)) ||
319 (mLinkRule
&& aData
->mStateMask
.HasState(NS_EVENT_STATE_VISITED
)) ||
320 (mVisitedRule
&& aData
->mStateMask
.HasState(NS_EVENT_STATE_VISITED
)))) {
321 return eRestyle_Self
;
324 return nsRestyleHint(0);
327 /* virtual */ nsRestyleHint
328 nsHTMLStyleSheet::HasStateDependentStyle(PseudoElementStateRuleProcessorData
* aData
)
330 return nsRestyleHint(0);
334 nsHTMLStyleSheet::HasDocumentStateDependentStyle(StateRuleProcessorData
* aData
)
339 /* virtual */ nsRestyleHint
340 nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData
* aData
)
342 // Do nothing on before-change checks
343 if (!aData
->mAttrHasChanged
) {
344 return nsRestyleHint(0);
347 // Note: no need to worry about whether some states changed with this
348 // attribute here, because we handle that under HasStateDependentStyle() as
351 // Result is true for |href| changes on HTML links if we have link rules.
352 Element
*element
= aData
->mElement
;
353 if (aData
->mAttribute
== nsGkAtoms::href
&&
354 (mLinkRule
|| mVisitedRule
|| mActiveRule
) &&
355 element
->IsHTML(nsGkAtoms::a
)) {
356 return eRestyle_Self
;
359 // Don't worry about the mDocumentColorRule since it only applies
360 // to descendants of body, when we're already reresolving.
362 // Handle the content style rules.
363 if (element
->IsAttributeMapped(aData
->mAttribute
)) {
364 // cellpadding on tables is special and requires reresolving all
365 // the cells in the table
366 if (aData
->mAttribute
== nsGkAtoms::cellpadding
&&
367 element
->IsHTML(nsGkAtoms::table
)) {
368 return eRestyle_Subtree
;
370 return eRestyle_Self
;
373 return nsRestyleHint(0);
377 nsHTMLStyleSheet::MediumFeaturesChanged(nsPresContext
* aPresContext
)
383 nsHTMLStyleSheet::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const
385 return 0; // nsHTMLStyleSheets are charged to the DOM, not layout
389 nsHTMLStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const
391 return 0; // nsHTMLStyleSheets are charged to the DOM, not layout
395 nsHTMLStyleSheet::RulesMatching(PseudoElementRuleProcessorData
* aData
)
400 nsHTMLStyleSheet::RulesMatching(AnonBoxRuleProcessorData
* aData
)
406 nsHTMLStyleSheet::RulesMatching(XULTreeRuleProcessorData
* aData
)
412 nsHTMLStyleSheet::SetOwningDocument(nsIDocument
* aDocument
)
414 mDocument
= aDocument
; // not refcounted
418 nsHTMLStyleSheet::Reset()
421 mVisitedRule
= nullptr;
422 mActiveRule
= nullptr;
424 if (mLangRuleTable
.ops
) {
425 PL_DHashTableFinish(&mLangRuleTable
);
426 mLangRuleTable
.ops
= nullptr;
428 if (mMappedAttrTable
.ops
) {
429 PL_DHashTableFinish(&mMappedAttrTable
);
430 mMappedAttrTable
.ops
= nullptr;
435 nsHTMLStyleSheet::ImplLinkColorSetter(nsRefPtr
<HTMLColorRule
>& aRule
, nscolor aColor
)
437 if (aRule
&& aRule
->mColor
== aColor
) {
441 aRule
= new HTMLColorRule();
443 return NS_ERROR_OUT_OF_MEMORY
;
445 aRule
->mColor
= aColor
;
446 // Now make sure we restyle any links that might need it. This
447 // shouldn't happen often, so just rebuilding everything is ok.
448 if (mDocument
&& mDocument
->GetShell()) {
449 Element
* root
= mDocument
->GetRootElement();
451 mDocument
->GetShell()->GetPresContext()->RestyleManager()->
452 PostRestyleEvent(root
, eRestyle_Subtree
, NS_STYLE_HINT_NONE
);
459 nsHTMLStyleSheet::SetLinkColor(nscolor aColor
)
461 return ImplLinkColorSetter(mLinkRule
, aColor
);
466 nsHTMLStyleSheet::SetActiveLinkColor(nscolor aColor
)
468 return ImplLinkColorSetter(mActiveRule
, aColor
);
472 nsHTMLStyleSheet::SetVisitedLinkColor(nscolor aColor
)
474 return ImplLinkColorSetter(mVisitedRule
, aColor
);
477 already_AddRefed
<nsMappedAttributes
>
478 nsHTMLStyleSheet::UniqueMappedAttributes(nsMappedAttributes
* aMapped
)
480 if (!mMappedAttrTable
.ops
) {
481 PL_DHashTableInit(&mMappedAttrTable
, &MappedAttrTable_Ops
,
482 nullptr, sizeof(MappedAttrTableEntry
));
484 MappedAttrTableEntry
*entry
= static_cast<MappedAttrTableEntry
*>
485 (PL_DHashTableOperate(&mMappedAttrTable
, aMapped
, PL_DHASH_ADD
));
488 if (!entry
->mAttributes
) {
489 // We added a new entry to the hashtable, so we have a new unique set.
490 entry
->mAttributes
= aMapped
;
492 nsRefPtr
<nsMappedAttributes
> ret
= entry
->mAttributes
;
497 nsHTMLStyleSheet::DropMappedAttributes(nsMappedAttributes
* aMapped
)
499 NS_ENSURE_TRUE_VOID(aMapped
);
501 NS_ASSERTION(mMappedAttrTable
.ops
, "table uninitialized");
503 uint32_t entryCount
= mMappedAttrTable
.EntryCount() - 1;
506 PL_DHashTableOperate(&mMappedAttrTable
, aMapped
, PL_DHASH_REMOVE
);
508 NS_ASSERTION(entryCount
== mMappedAttrTable
.EntryCount(), "not removed");
512 nsHTMLStyleSheet::LangRuleFor(const nsString
& aLanguage
)
514 if (!mLangRuleTable
.ops
) {
515 PL_DHashTableInit(&mLangRuleTable
, &LangRuleTable_Ops
,
516 nullptr, sizeof(LangRuleTableEntry
));
518 LangRuleTableEntry
*entry
= static_cast<LangRuleTableEntry
*>
519 (PL_DHashTableOperate(&mLangRuleTable
, &aLanguage
, PL_DHASH_ADD
));
521 NS_ASSERTION(false, "out of memory");
528 SizeOfAttributesEntryExcludingThis(PLDHashEntryHdr
* aEntry
,
529 MallocSizeOf aMallocSizeOf
,
532 NS_PRECONDITION(aEntry
, "The entry should not be null!");
534 MappedAttrTableEntry
* entry
= static_cast<MappedAttrTableEntry
*>(aEntry
);
535 NS_ASSERTION(entry
->mAttributes
, "entry->mAttributes should not be null!");
536 return entry
->mAttributes
->SizeOfIncludingThis(aMallocSizeOf
);
540 nsHTMLStyleSheet::DOMSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const
542 size_t n
= aMallocSizeOf(this);
544 if (mMappedAttrTable
.ops
) {
545 n
+= PL_DHashTableSizeOfExcludingThis(&mMappedAttrTable
,
546 SizeOfAttributesEntryExcludingThis
,
550 // Measurement of the following members may be added later if DMD finds it is
556 // - mTableQuirkColorRule
560 // The following members are not measured:
561 // - mDocument, because it's non-owning