CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / layout / style / nsHTMLStyleSheet.cpp
blob5528ae8e968f1ad7a955821bc19b95c9fb22afbe
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK *****
39 * This Original Code has been modified by IBM Corporation. Modifications made by IBM
40 * described herein are Copyright (c) International Business Machines Corporation, 2000.
41 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
43 * Date Modified by Description of modification
44 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
48 * style sheet and style rule processor representing data from presentational
49 * HTML attributes
52 #include "nsHTMLStyleSheet.h"
53 #include "nsINameSpaceManager.h"
54 #include "nsIAtom.h"
55 #include "nsIURL.h"
56 #include "nsMappedAttributes.h"
57 #include "nsILink.h"
58 #include "nsIFrame.h"
59 #include "nsStyleContext.h"
60 #include "nsGkAtoms.h"
61 #include "nsPresContext.h"
62 #include "nsIEventStateManager.h"
63 #include "nsIDocument.h"
64 #include "nsIPresShell.h"
65 #include "nsStyleConsts.h"
66 #include "nsIHTMLDocument.h"
67 #include "nsIDOMHTMLElement.h"
68 #include "nsCSSAnonBoxes.h"
69 #include "nsRuleWalker.h"
70 #include "nsRuleData.h"
71 #include "nsContentErrors.h"
72 #include "nsRuleProcessorData.h"
73 #include "mozilla/dom/Element.h"
74 #include "nsCSSFrameConstructor.h"
76 using namespace mozilla::dom;
78 NS_IMPL_ISUPPORTS1(nsHTMLStyleSheet::HTMLColorRule, nsIStyleRule)
80 /* virtual */ void
81 nsHTMLStyleSheet::HTMLColorRule::MapRuleInfoInto(nsRuleData* aRuleData)
83 if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Color)) {
84 if (aRuleData->mColorData->mColor.GetUnit() == eCSSUnit_Null &&
85 aRuleData->mPresContext->UseDocumentColors())
86 aRuleData->mColorData->mColor.SetColorValue(mColor);
90 #ifdef DEBUG
91 /* virtual */ void
92 nsHTMLStyleSheet::HTMLColorRule::List(FILE* out, PRInt32 aIndent) const
95 #endif
98 NS_IMPL_ISUPPORTS1(nsHTMLStyleSheet::GenericTableRule, nsIStyleRule)
100 /* virtual */ void
101 nsHTMLStyleSheet::GenericTableRule::MapRuleInfoInto(nsRuleData* aRuleData)
103 // Nothing to do.
106 #ifdef DEBUG
107 /* virtual */ void
108 nsHTMLStyleSheet::GenericTableRule::List(FILE* out, PRInt32 aIndent) const
111 #endif
113 /* virtual */ void
114 nsHTMLStyleSheet::TableTHRule::MapRuleInfoInto(nsRuleData* aRuleData)
116 if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
117 if (aRuleData->mTextData->mTextAlign.GetUnit() == eCSSUnit_Null) {
118 aRuleData->mTextData->mTextAlign.
119 SetIntValue(NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT,
120 eCSSUnit_Enumerated);
125 // -----------------------------------------------------------
127 struct MappedAttrTableEntry : public PLDHashEntryHdr {
128 nsMappedAttributes *mAttributes;
131 static PLDHashNumber
132 MappedAttrTable_HashKey(PLDHashTable *table, const void *key)
134 nsMappedAttributes *attributes =
135 static_cast<nsMappedAttributes*>(const_cast<void*>(key));
137 return attributes->HashValue();
140 static void
141 MappedAttrTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
143 MappedAttrTableEntry *entry = static_cast<MappedAttrTableEntry*>(hdr);
145 entry->mAttributes->DropStyleSheetReference();
146 memset(entry, 0, sizeof(MappedAttrTableEntry));
149 static PRBool
150 MappedAttrTable_MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
151 const void *key)
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 PLDHashTableOps MappedAttrTable_Ops = {
162 PL_DHashAllocTable,
163 PL_DHashFreeTable,
164 MappedAttrTable_HashKey,
165 MappedAttrTable_MatchEntry,
166 PL_DHashMoveEntryStub,
167 MappedAttrTable_ClearEntry,
168 PL_DHashFinalizeStub,
169 NULL
172 // -----------------------------------------------------------
174 nsHTMLStyleSheet::nsHTMLStyleSheet(void)
175 : mDocument(nsnull)
177 mMappedAttrTable.ops = nsnull;
180 nsresult
181 nsHTMLStyleSheet::Init()
183 mTableTHRule = new TableTHRule();
184 if (!mTableTHRule)
185 return NS_ERROR_OUT_OF_MEMORY;
186 return NS_OK;
189 nsHTMLStyleSheet::~nsHTMLStyleSheet()
191 if (mMappedAttrTable.ops)
192 PL_DHashTableFinish(&mMappedAttrTable);
195 NS_IMPL_ISUPPORTS2(nsHTMLStyleSheet, nsIStyleSheet, nsIStyleRuleProcessor)
197 static nsresult GetBodyColor(nsPresContext* aPresContext, nscolor* aColor)
199 nsIPresShell *shell = aPresContext->PresShell();
200 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(shell->GetDocument());
201 if (!htmlDoc)
202 return NS_ERROR_FAILURE;
203 nsIContent* bodyContent = htmlDoc->GetBodyContentExternal();
204 if (!bodyContent)
205 return NS_ERROR_FAILURE;
206 nsIFrame *bodyFrame = bodyContent->GetPrimaryFrame();
207 if (!bodyFrame)
208 return NS_ERROR_FAILURE;
209 *aColor = bodyFrame->GetStyleColor()->mColor;
210 return NS_OK;
213 /* virtual */ void
214 nsHTMLStyleSheet::RulesMatching(ElementRuleProcessorData* aData)
216 nsRuleWalker *ruleWalker = aData->mRuleWalker;
217 if (aData->mIsHTMLContent) {
218 nsIAtom* tag = aData->mContentTag;
220 // if we have anchor colors, check if this is an anchor with an href
221 if (tag == nsGkAtoms::a) {
222 if (mLinkRule || mVisitedRule || mActiveRule) {
223 nsEventStates state = aData->GetContentStateForVisitedHandling(
224 ruleWalker->VisitedHandling(),
225 // If the node being matched is a link,
226 // it's the relevant link.
227 aData->IsLink());
228 if (mLinkRule && state.HasState(NS_EVENT_STATE_UNVISITED)) {
229 ruleWalker->Forward(mLinkRule);
230 ruleWalker->SetHaveRelevantLink();
232 else if (mVisitedRule && state.HasState(NS_EVENT_STATE_VISITED)) {
233 ruleWalker->Forward(mVisitedRule);
234 ruleWalker->SetHaveRelevantLink();
237 // No need to add to the active rule if it's not a link
238 if (mActiveRule && aData->IsLink() &&
239 state.HasState(NS_EVENT_STATE_ACTIVE)) {
240 ruleWalker->Forward(mActiveRule);
242 } // end link/visited/active rules
243 } // end A tag
244 // add the rule to handle text-align for a <th>
245 else if (tag == nsGkAtoms::th) {
246 ruleWalker->Forward(mTableTHRule);
248 else if (tag == nsGkAtoms::table) {
249 if (aData->mCompatMode == eCompatibility_NavQuirks) {
250 nscolor bodyColor;
251 nsresult rv =
252 GetBodyColor(ruleWalker->CurrentNode()->GetPresContext(),
253 &bodyColor);
254 if (NS_SUCCEEDED(rv) &&
255 (!mDocumentColorRule || bodyColor != mDocumentColorRule->mColor)) {
256 mDocumentColorRule = new HTMLColorRule();
257 if (mDocumentColorRule) {
258 mDocumentColorRule->mColor = bodyColor;
261 if (mDocumentColorRule)
262 ruleWalker->Forward(mDocumentColorRule);
265 } // end html element
267 // just get the style rules from the content
268 aData->mElement->WalkContentStyleRules(ruleWalker);
271 // Test if style is dependent on content state
272 /* virtual */ nsRestyleHint
273 nsHTMLStyleSheet::HasStateDependentStyle(StateRuleProcessorData* aData)
275 if (aData->mIsHTMLContent &&
276 aData->mContentTag == nsGkAtoms::a &&
277 aData->IsLink() &&
278 ((mActiveRule && aData->mStateMask.HasState(NS_EVENT_STATE_ACTIVE)) ||
279 (mLinkRule && aData->mStateMask.HasState(NS_EVENT_STATE_VISITED)) ||
280 (mVisitedRule && aData->mStateMask.HasState(NS_EVENT_STATE_VISITED)))) {
281 return eRestyle_Self;
284 return nsRestyleHint(0);
287 /* virtual */ PRBool
288 nsHTMLStyleSheet::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
290 return PR_FALSE;
293 /* virtual */ nsRestyleHint
294 nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
296 // Do nothing on before-change checks
297 if (!aData->mAttrHasChanged) {
298 return nsRestyleHint(0);
301 // Note: no need to worry about whether some states changed with this
302 // attribute here, because we handle that under HasStateDependentStyle() as
303 // needed.
305 // Result is true for |href| changes on HTML links if we have link rules.
306 Element *element = aData->mElement;
307 if (aData->mAttribute == nsGkAtoms::href &&
308 (mLinkRule || mVisitedRule || mActiveRule) &&
309 element->IsHTML() &&
310 aData->mContentTag == nsGkAtoms::a) {
311 return eRestyle_Self;
314 // Don't worry about the mDocumentColorRule since it only applies
315 // to descendants of body, when we're already reresolving.
317 // Handle the content style rules.
318 if (element->IsAttributeMapped(aData->mAttribute)) {
319 // cellpadding on tables is special and requires reresolving all
320 // the cells in the table
321 if (aData->mAttribute == nsGkAtoms::cellpadding &&
322 element->IsHTML() &&
323 aData->mContentTag == nsGkAtoms::table) {
324 return eRestyle_Subtree;
326 return eRestyle_Self;
329 return nsRestyleHint(0);
332 /* virtual */ PRBool
333 nsHTMLStyleSheet::MediumFeaturesChanged(nsPresContext* aPresContext)
335 return PR_FALSE;
339 /* virtual */ void
340 nsHTMLStyleSheet::RulesMatching(PseudoElementRuleProcessorData* aData)
344 /* virtual */ void
345 nsHTMLStyleSheet::RulesMatching(AnonBoxRuleProcessorData* aData)
349 #ifdef MOZ_XUL
350 /* virtual */ void
351 nsHTMLStyleSheet::RulesMatching(XULTreeRuleProcessorData* aData)
354 #endif
356 // nsIStyleSheet api
357 /* virtual */ nsIURI*
358 nsHTMLStyleSheet::GetSheetURI() const
360 return mURL;
363 /* virtual */ nsIURI*
364 nsHTMLStyleSheet::GetBaseURI() const
366 return mURL;
369 /* virtual */ void
370 nsHTMLStyleSheet::GetTitle(nsString& aTitle) const
372 aTitle.Truncate();
375 /* virtual */ void
376 nsHTMLStyleSheet::GetType(nsString& aType) const
378 aType.AssignLiteral("text/html");
381 /* virtual */ PRBool
382 nsHTMLStyleSheet::HasRules() const
384 return PR_TRUE; // We have rules at all reasonable times
387 /* virtual */ PRBool
388 nsHTMLStyleSheet::IsApplicable() const
390 return PR_TRUE;
393 /* virtual */ void
394 nsHTMLStyleSheet::SetEnabled(PRBool aEnabled)
395 { // these can't be disabled
398 /* virtual */ PRBool
399 nsHTMLStyleSheet::IsComplete() const
401 return PR_TRUE;
404 /* virtual */ void
405 nsHTMLStyleSheet::SetComplete()
409 /* virtual */ nsIStyleSheet*
410 nsHTMLStyleSheet::GetParentSheet() const
412 return nsnull;
415 /* virtual */ nsIDocument*
416 nsHTMLStyleSheet::GetOwningDocument() const
418 return mDocument;
421 /* virtual */ void
422 nsHTMLStyleSheet::SetOwningDocument(nsIDocument* aDocument)
424 mDocument = aDocument; // not refcounted
427 nsresult
428 nsHTMLStyleSheet::Init(nsIURI* aURL, nsIDocument* aDocument)
430 NS_PRECONDITION(aURL && aDocument, "null ptr");
431 if (! aURL || ! aDocument)
432 return NS_ERROR_NULL_POINTER;
434 if (mURL || mDocument)
435 return NS_ERROR_ALREADY_INITIALIZED;
437 mDocument = aDocument; // not refcounted!
438 mURL = aURL;
439 return NS_OK;
442 void
443 nsHTMLStyleSheet::Reset(nsIURI* aURL)
445 mURL = aURL;
447 mLinkRule = nsnull;
448 mVisitedRule = nsnull;
449 mActiveRule = nsnull;
450 mDocumentColorRule = nsnull;
452 if (mMappedAttrTable.ops) {
453 PL_DHashTableFinish(&mMappedAttrTable);
454 mMappedAttrTable.ops = nsnull;
458 nsresult
459 nsHTMLStyleSheet::ImplLinkColorSetter(nsRefPtr<HTMLColorRule>& aRule, nscolor aColor)
461 if (aRule && aRule->mColor == aColor) {
462 return NS_OK;
465 aRule = new HTMLColorRule();
466 if (!aRule)
467 return NS_ERROR_OUT_OF_MEMORY;
469 aRule->mColor = aColor;
470 // Now make sure we restyle any links that might need it. This
471 // shouldn't happen often, so just rebuilding everything is ok.
472 if (mDocument && mDocument->GetShell()) {
473 Element* root = mDocument->GetRootElement();
474 if (root) {
475 mDocument->GetShell()->FrameConstructor()->
476 PostRestyleEvent(root, eRestyle_Subtree, NS_STYLE_HINT_NONE);
479 return NS_OK;
482 nsresult
483 nsHTMLStyleSheet::SetLinkColor(nscolor aColor)
485 return ImplLinkColorSetter(mLinkRule, aColor);
489 nsresult
490 nsHTMLStyleSheet::SetActiveLinkColor(nscolor aColor)
492 return ImplLinkColorSetter(mActiveRule, aColor);
495 nsresult
496 nsHTMLStyleSheet::SetVisitedLinkColor(nscolor aColor)
498 return ImplLinkColorSetter(mVisitedRule, aColor);
501 already_AddRefed<nsMappedAttributes>
502 nsHTMLStyleSheet::UniqueMappedAttributes(nsMappedAttributes* aMapped)
504 if (!mMappedAttrTable.ops) {
505 PRBool res = PL_DHashTableInit(&mMappedAttrTable, &MappedAttrTable_Ops,
506 nsnull, sizeof(MappedAttrTableEntry), 16);
507 if (!res) {
508 mMappedAttrTable.ops = nsnull;
509 return nsnull;
512 MappedAttrTableEntry *entry = static_cast<MappedAttrTableEntry*>
513 (PL_DHashTableOperate(&mMappedAttrTable, aMapped, PL_DHASH_ADD));
514 if (!entry)
515 return nsnull;
516 if (!entry->mAttributes) {
517 // We added a new entry to the hashtable, so we have a new unique set.
518 entry->mAttributes = aMapped;
520 NS_ADDREF(entry->mAttributes); // for caller
521 return entry->mAttributes;
524 void
525 nsHTMLStyleSheet::DropMappedAttributes(nsMappedAttributes* aMapped)
527 NS_ENSURE_TRUE(aMapped, /**/);
529 NS_ASSERTION(mMappedAttrTable.ops, "table uninitialized");
530 #ifdef DEBUG
531 PRUint32 entryCount = mMappedAttrTable.entryCount - 1;
532 #endif
534 PL_DHashTableOperate(&mMappedAttrTable, aMapped, PL_DHASH_REMOVE);
536 NS_ASSERTION(entryCount == mMappedAttrTable.entryCount, "not removed");
539 #ifdef DEBUG
540 /* virtual */ void
541 nsHTMLStyleSheet::List(FILE* out, PRInt32 aIndent) const
543 // Indent
544 for (PRInt32 index = aIndent; --index >= 0; ) fputs(" ", out);
546 fputs("HTML Style Sheet: ", out);
547 nsCAutoString urlSpec;
548 mURL->GetSpec(urlSpec);
549 if (!urlSpec.IsEmpty()) {
550 fputs(urlSpec.get(), out);
552 fputs("\n", out);
554 #endif
556 // XXX For convenience and backwards compatibility
557 nsresult
558 NS_NewHTMLStyleSheet(nsHTMLStyleSheet** aInstancePtrResult, nsIURI* aURL,
559 nsIDocument* aDocument)
561 nsresult rv;
562 nsHTMLStyleSheet* sheet;
563 if (NS_FAILED(rv = NS_NewHTMLStyleSheet(&sheet)))
564 return rv;
566 if (NS_FAILED(rv = sheet->Init(aURL, aDocument))) {
567 NS_RELEASE(sheet);
568 return rv;
571 *aInstancePtrResult = sheet;
572 return NS_OK;
576 nsresult
577 NS_NewHTMLStyleSheet(nsHTMLStyleSheet** aInstancePtrResult)
579 NS_ASSERTION(aInstancePtrResult, "null out param");
581 nsHTMLStyleSheet *it = new nsHTMLStyleSheet();
582 if (!it) {
583 *aInstancePtrResult = nsnull;
584 return NS_ERROR_OUT_OF_MEMORY;
587 NS_ADDREF(it);
588 nsresult rv = it->Init();
589 if (NS_FAILED(rv))
590 NS_RELEASE(it);
592 *aInstancePtrResult = it; // NS_ADDREF above, or set to null by NS_RELEASE
593 return rv;