1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 #include "nsStyleUtil.h"
41 #include "nsStyleConsts.h"
43 #include "nsGkAtoms.h"
44 #include "nsILinkHandler.h"
46 #include "nsIContent.h"
47 #include "nsIDocument.h"
48 #include "nsINameSpaceManager.h"
50 #include "nsNetUtil.h"
51 #include "nsReadableUtils.h"
52 #include "nsContentUtils.h"
53 #include "nsTextFormatter.h"
55 // XXX This is here because nsCachedStyleData is accessed outside of
56 // the content module; e.g., by nsCSSFrameConstructor.
57 #include "nsRuleNode.h"
59 nsCachedStyleData::StyleStructInfo
60 nsCachedStyleData::gInfo
[] = {
62 #define STYLE_STRUCT_INHERITED(name, checkdata_cb, ctor_args) \
63 { offsetof(nsCachedStyleData, mInheritedData), \
64 offsetof(nsInheritedStyleData, m##name##Data), \
66 #define STYLE_STRUCT_RESET(name, checkdata_cb, ctor_args) \
67 { offsetof(nsCachedStyleData, mResetData), \
68 offsetof(nsResetStyleData, m##name##Data), \
71 #include "nsStyleStructList.h"
73 #undef STYLE_STRUCT_INHERITED
74 #undef STYLE_STRUCT_RESET
79 #define POSITIVE_SCALE_FACTOR 1.10 /* 10% */
80 #define NEGATIVE_SCALE_FACTOR .90 /* 10% */
83 //------------------------------------------------------------------------------
85 //------------------------------------------------------------------------------
88 * Return the scaling percentage given a font scaler
89 * Lifted from layutil.c
91 float nsStyleUtil::GetScalingFactor(PRInt32 aScaler
)
99 mult
= NEGATIVE_SCALE_FACTOR
;
103 mult
= POSITIVE_SCALE_FACTOR
;
106 /* use the percentage scaling factor to the power of the pref */
115 //------------------------------------------------------------------------------
116 // Font Algorithm Code
117 //------------------------------------------------------------------------------
120 nsStyleUtil::CalcFontPointSize(PRInt32 aHTMLSize
, PRInt32 aBasePointSize
,
121 float aScalingFactor
, nsPresContext
* aPresContext
,
122 nsFontSizeType aFontSizeType
)
124 #define sFontSizeTableMin 9
125 #define sFontSizeTableMax 16
127 // This table seems to be the one used by MacIE5. We hope its adoption in Mozilla
128 // and eventually in WinIE5.5 will help to establish a standard rendering across
129 // platforms and browsers. For now, it is used only in Strict mode. More can be read
130 // in the document written by Todd Farhner at:
131 // http://style.verso.com/font_size_intervals/altintervals.html
133 static PRInt32 sStrictFontSizeTable
[sFontSizeTableMax
- sFontSizeTableMin
+ 1][8] =
135 { 9, 9, 9, 9, 11, 14, 18, 27},
136 { 9, 9, 9, 10, 12, 15, 20, 30},
137 { 9, 9, 10, 11, 13, 17, 22, 33},
138 { 9, 9, 10, 12, 14, 18, 24, 36},
139 { 9, 10, 12, 13, 16, 20, 26, 39},
140 { 9, 10, 12, 14, 17, 21, 28, 42},
141 { 9, 10, 13, 15, 18, 23, 30, 45},
142 { 9, 10, 13, 16, 18, 24, 32, 48}
144 // HTML 1 2 3 4 5 6 7
145 // CSS xxs xs s m l xl xxl
149 //------------------------------------------------------------
151 // This table gives us compatibility with WinNav4 for the default fonts only.
152 // In WinNav4, the default fonts were:
154 // Times/12pt == Times/16px at 96ppi
155 // Courier/10pt == Courier/13px at 96ppi
157 // The 2 lines below marked "anchored" have the exact pixel sizes used by
158 // WinNav4 for Times/12pt and Courier/10pt at 96ppi. As you can see, the
159 // HTML size 3 (user pref) for those 2 anchored lines is 13px and 16px.
161 // All values other than the anchored values were filled in by hand, never
162 // going below 9px, and maintaining a "diagonal" relationship. See for
163 // example the 13s -- they follow a diagonal line through the table.
165 static PRInt32 sQuirksFontSizeTable
[sFontSizeTableMax
- sFontSizeTableMin
+ 1][8] =
167 { 9, 9, 9, 9, 11, 14, 18, 28 },
168 { 9, 9, 9, 10, 12, 15, 20, 31 },
169 { 9, 9, 9, 11, 13, 17, 22, 34 },
170 { 9, 9, 10, 12, 14, 18, 24, 37 },
171 { 9, 9, 10, 13, 16, 20, 26, 40 }, // anchored (13)
172 { 9, 9, 11, 14, 17, 21, 28, 42 },
173 { 9, 10, 12, 15, 17, 23, 30, 45 },
174 { 9, 10, 13, 16, 18, 24, 32, 48 } // anchored (16)
176 // HTML 1 2 3 4 5 6 7
177 // CSS xxs xs s m l xl xxl
183 // These are the exact pixel values used by WinIE5 at 96ppi.
185 { ?, 8, 11, 12, 13, 16, 21, 32 }, // smallest
186 { ?, 9, 12, 13, 16, 21, 27, 40 }, // smaller
187 { ?, 10, 13, 16, 18, 24, 32, 48 }, // medium
188 { ?, 13, 16, 19, 21, 27, 37, ?? }, // larger
189 { ?, 16, 19, 21, 24, 32, 43, ?? } // largest
191 // HTML 1 2 3 4 5 6 7
192 // CSS ? ? ? ? ? ? ? ?
194 // (CSS not tested yet.)
198 static PRInt32 sFontSizeFactors
[8] = { 60,75,89,100,120,150,200,300 };
200 static PRInt32 sCSSColumns
[7] = {0, 1, 2, 3, 4, 5, 6}; // xxs...xxl
201 static PRInt32 sHTMLColumns
[7] = {1, 2, 3, 4, 5, 6, 7}; // 1...7
205 if (aFontSizeType
== eFontSize_HTML
) {
206 aHTMLSize
--; // input as 1-7
211 else if (aHTMLSize
> 6)
215 switch (aFontSizeType
)
217 case eFontSize_HTML
: column
= sHTMLColumns
; break;
218 case eFontSize_CSS
: column
= sCSSColumns
; break;
221 // Make special call specifically for fonts (needed PrintPreview)
222 PRInt32 fontSize
= nsPresContext::AppUnitsToIntCSSPixels(aBasePointSize
);
224 if ((fontSize
>= sFontSizeTableMin
) && (fontSize
<= sFontSizeTableMax
))
226 PRInt32 row
= fontSize
- sFontSizeTableMin
;
228 if (aPresContext
->CompatibilityMode() == eCompatibility_NavQuirks
) {
229 dFontSize
= nsPresContext::CSSPixelsToAppUnits(sQuirksFontSizeTable
[row
][column
[aHTMLSize
]]);
231 dFontSize
= nsPresContext::CSSPixelsToAppUnits(sStrictFontSizeTable
[row
][column
[aHTMLSize
]]);
236 PRInt32 factor
= sFontSizeFactors
[column
[aHTMLSize
]];
237 dFontSize
= (factor
* aBasePointSize
) / 100;
240 dFontSize
*= aScalingFactor
;
242 if (1.0 < dFontSize
) {
243 return (nscoord
)dFontSize
;
249 //------------------------------------------------------------------------------
251 //------------------------------------------------------------------------------
253 nscoord
nsStyleUtil::FindNextSmallerFontSize(nscoord aFontSize
, PRInt32 aBasePointSize
,
254 float aScalingFactor
, nsPresContext
* aPresContext
,
255 nsFontSizeType aFontSizeType
)
260 float relativePosition
;
262 nscoord indexFontSize
= aFontSize
; // XXX initialize to quell a spurious gcc3.2 warning
263 nscoord smallestIndexFontSize
;
264 nscoord largestIndexFontSize
;
265 nscoord smallerIndexFontSize
;
266 nscoord largerIndexFontSize
;
268 nscoord onePx
= nsPresContext::CSSPixelsToAppUnits(1);
270 if (aFontSizeType
== eFontSize_HTML
) {
278 smallestIndexFontSize
= CalcFontPointSize(indexMin
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
279 largestIndexFontSize
= CalcFontPointSize(indexMax
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
280 if (aFontSize
> smallestIndexFontSize
) {
281 if (aFontSize
< NSToCoordRound(float(largestIndexFontSize
) * 1.5)) { // smaller will be in HTML table
282 // find largest index smaller than current
283 for (index
= indexMax
; index
>= indexMin
; index
--) {
284 indexFontSize
= CalcFontPointSize(index
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
285 if (indexFontSize
< aFontSize
)
288 // set up points beyond table for interpolation purposes
289 if (indexFontSize
== smallestIndexFontSize
) {
290 smallerIndexFontSize
= indexFontSize
- onePx
;
291 largerIndexFontSize
= CalcFontPointSize(index
+1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
292 } else if (indexFontSize
== largestIndexFontSize
) {
293 smallerIndexFontSize
= CalcFontPointSize(index
-1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
294 largerIndexFontSize
= NSToCoordRound(float(largestIndexFontSize
) * 1.5);
296 smallerIndexFontSize
= CalcFontPointSize(index
-1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
297 largerIndexFontSize
= CalcFontPointSize(index
+1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
299 // compute the relative position of the parent size between the two closest indexed sizes
300 relativePosition
= float(aFontSize
- indexFontSize
) / float(largerIndexFontSize
- indexFontSize
);
301 // set the new size to have the same relative position between the next smallest two indexed sizes
302 smallerSize
= smallerIndexFontSize
+ NSToCoordRound(relativePosition
* (indexFontSize
- smallerIndexFontSize
));
304 else { // larger than HTML table, drop by 33%
305 smallerSize
= NSToCoordRound(float(aFontSize
) / 1.5);
308 else { // smaller than HTML table, drop by 1px
309 smallerSize
= PR_MAX(aFontSize
- onePx
, onePx
);
314 //------------------------------------------------------------------------------
316 //------------------------------------------------------------------------------
318 nscoord
nsStyleUtil::FindNextLargerFontSize(nscoord aFontSize
, PRInt32 aBasePointSize
,
319 float aScalingFactor
, nsPresContext
* aPresContext
,
320 nsFontSizeType aFontSizeType
)
325 float relativePosition
;
327 nscoord indexFontSize
= aFontSize
; // XXX initialize to quell a spurious gcc3.2 warning
328 nscoord smallestIndexFontSize
;
329 nscoord largestIndexFontSize
;
330 nscoord smallerIndexFontSize
;
331 nscoord largerIndexFontSize
;
333 nscoord onePx
= nsPresContext::CSSPixelsToAppUnits(1);
335 if (aFontSizeType
== eFontSize_HTML
) {
343 smallestIndexFontSize
= CalcFontPointSize(indexMin
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
344 largestIndexFontSize
= CalcFontPointSize(indexMax
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
345 if (aFontSize
> (smallestIndexFontSize
- onePx
)) {
346 if (aFontSize
< largestIndexFontSize
) { // larger will be in HTML table
347 // find smallest index larger than current
348 for (index
= indexMin
; index
<= indexMax
; index
++) {
349 indexFontSize
= CalcFontPointSize(index
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
350 if (indexFontSize
> aFontSize
)
353 // set up points beyond table for interpolation purposes
354 if (indexFontSize
== smallestIndexFontSize
) {
355 smallerIndexFontSize
= indexFontSize
- onePx
;
356 largerIndexFontSize
= CalcFontPointSize(index
+1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
357 } else if (indexFontSize
== largestIndexFontSize
) {
358 smallerIndexFontSize
= CalcFontPointSize(index
-1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
359 largerIndexFontSize
= NSToCoordRound(float(largestIndexFontSize
) * 1.5);
361 smallerIndexFontSize
= CalcFontPointSize(index
-1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
362 largerIndexFontSize
= CalcFontPointSize(index
+1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
364 // compute the relative position of the parent size between the two closest indexed sizes
365 relativePosition
= float(aFontSize
- smallerIndexFontSize
) / float(indexFontSize
- smallerIndexFontSize
);
366 // set the new size to have the same relative position between the next largest two indexed sizes
367 largerSize
= indexFontSize
+ NSToCoordRound(relativePosition
* (largerIndexFontSize
- indexFontSize
));
369 else { // larger than HTML table, increase by 50%
370 largerSize
= NSToCoordRound(float(aFontSize
) * 1.5);
373 else { // smaller than HTML table, increase by 1px
374 largerSize
= aFontSize
+ onePx
;
379 //------------------------------------------------------------------------------
381 //------------------------------------------------------------------------------
384 nsStyleUtil::ConstrainFontWeight(PRInt32 aWeight
)
386 aWeight
= ((aWeight
< 100) ? 100 : ((aWeight
> 900) ? 900 : aWeight
));
387 PRInt32 base
= ((aWeight
/ 100) * 100);
388 PRInt32 step
= (aWeight
% 100);
389 PRBool negativeStep
= PRBool(50 < step
);
393 maxStep
= (base
/ 100);
397 maxStep
= ((900 - base
) / 100);
399 if (maxStep
< step
) {
402 return (base
+ ((negativeStep
) ? -step
: step
));
406 GetLinkStateFromURI(nsIURI
* aURI
, nsIContent
* aContent
,
407 nsILinkHandler
* aLinkHandler
)
409 NS_PRECONDITION(aURI
, "Must have URI");
411 if (NS_LIKELY(aLinkHandler
)) {
412 aLinkHandler
->GetLinkState(aURI
, state
);
415 // no link handler? Try to get one off the content
416 NS_ASSERTION(aContent
->GetOwnerDoc(), "Shouldn't happen");
417 nsCOMPtr
<nsISupports
> supp
=
418 aContent
->GetOwnerDoc()->GetContainer();
419 nsCOMPtr
<nsILinkHandler
> handler
= do_QueryInterface(supp
);
421 handler
->GetLinkState(aURI
, state
);
423 // no link handler? then all links are unvisited
424 state
= eLinkState_Unvisited
;
432 PRBool
nsStyleUtil::IsHTMLLink(nsIContent
*aContent
, nsIAtom
*aTag
,
433 nsILinkHandler
*aLinkHandler
,
437 NS_ASSERTION(aContent
&& aState
, "null arg in IsHTMLLink");
440 // - HTML ANCHOR with valid HREF
441 // - HTML LINK with valid HREF
442 // - HTML AREA with valid HREF
444 PRBool result
= PR_FALSE
;
446 if ((aTag
== nsGkAtoms::a
) ||
447 (aTag
== nsGkAtoms::link
) ||
448 (aTag
== nsGkAtoms::area
)) {
450 nsCOMPtr
<nsILink
> link( do_QueryInterface(aContent
) );
451 // In XML documents, this can be null.
453 nsLinkState linkState
;
454 link
->GetLinkState(linkState
);
455 if (linkState
== eLinkState_Unknown
) {
456 // if it is an anchor, area or link then check the href attribute
457 // make sure this anchor has a link even if we are not testing state
458 // if there is no link, then this anchor is not really a linkpseudo.
461 nsCOMPtr
<nsIURI
> hrefURI
;
462 link
->GetHrefURI(getter_AddRefs(hrefURI
));
465 linkState
= GetLinkStateFromURI(hrefURI
, aContent
, aLinkHandler
);
467 linkState
= eLinkState_NotLink
;
469 if (linkState
!= eLinkState_NotLink
&& aForStyling
&&
470 aContent
->IsInDoc()) {
471 aContent
->GetCurrentDoc()->AddStyleRelevantLink(aContent
, hrefURI
);
473 link
->SetLinkState(linkState
);
475 if (linkState
!= eLinkState_NotLink
) {
486 PRBool
nsStyleUtil::IsLink(nsIContent
*aContent
,
487 nsILinkHandler
*aLinkHandler
,
491 // XXX PERF This function will cause serious performance problems on
492 // pages with lots of XLinks. We should be caching the visited
493 // state of the XLinks. Where???
495 NS_ASSERTION(aContent
&& aState
, "invalid call to IsLink with null content");
497 PRBool rv
= PR_FALSE
;
499 if (aContent
&& aState
) {
500 nsCOMPtr
<nsIURI
> absURI
;
501 if (aContent
->IsLink(getter_AddRefs(absURI
))) {
502 *aState
= GetLinkStateFromURI(absURI
, aContent
, aLinkHandler
);
503 if (aForStyling
&& aContent
->IsInDoc()) {
504 aContent
->GetCurrentDoc()->AddStyleRelevantLink(aContent
, absURI
);
513 // Compare two language strings
514 PRBool
nsStyleUtil::DashMatchCompare(const nsAString
& aAttributeValue
,
515 const nsAString
& aSelectorValue
,
516 const nsStringComparator
& aComparator
)
519 PRUint32 selectorLen
= aSelectorValue
.Length();
520 PRUint32 attributeLen
= aAttributeValue
.Length();
521 if (selectorLen
> attributeLen
) {
525 nsAString::const_iterator iter
;
526 if (selectorLen
!= attributeLen
&&
527 *aAttributeValue
.BeginReading(iter
).advance(selectorLen
) !=
529 // to match, the aAttributeValue must have a dash after the end of
530 // the aSelectorValue's text (unless the aSelectorValue and the
531 // aAttributeValue have the same text)
535 result
= StringBeginsWith(aAttributeValue
, aSelectorValue
, aComparator
);
541 void nsStyleUtil::EscapeCSSString(const nsString
& aString
, nsAString
& aReturn
)
545 const nsString::char_type
* in
= aString
.get();
546 const nsString::char_type
* const end
= in
+ aString
.Length();
547 for (; in
!= end
; in
++)
551 // Escape all characters below 0x20 numerically.
554 This is the buffer into which snprintf should write. As the hex. value is,
555 for numbers below 0x20, max. 2 characters long, we don't need more than 5
556 characters ("\XX "+NUL).
559 nsTextFormatter::snprintf(buf
, NS_ARRAY_LENGTH(buf
), NS_LITERAL_STRING("\\%hX ").get(), *in
);
562 } else switch (*in
) {
563 // Special characters which should be escaped: Quotes and backslash
567 aReturn
.Append(PRUnichar('\\'));
568 // And now, after the eventual escaping character, the actual one.
570 aReturn
.Append(PRUnichar(*in
));
576 nsStyleUtil::ColorComponentToFloat(PRUint8 aAlpha
)
578 // Alpha values are expressed as decimals, so we should convert
579 // back, using as few decimal places as possible for
581 // First try two decimal places:
582 float rounded
= NS_roundf(float(aAlpha
) * 100.0f
/ 255.0f
) / 100.0f
;
583 if (FloatToColorComponent(rounded
) != aAlpha
) {
584 // Use three decimal places.
585 rounded
= NS_roundf(float(aAlpha
) * 1000.0f
/ 255.0f
) / 1000.0f
;
591 nsStyleUtil::IsSignificantChild(nsIContent
* aChild
, PRBool aTextIsSignificant
,
592 PRBool aWhitespaceIsSignificant
)
594 NS_ASSERTION(!aWhitespaceIsSignificant
|| aTextIsSignificant
,
595 "Nonsensical arguments");
597 PRBool isText
= aChild
->IsNodeOfType(nsINode::eTEXT
);
599 if (!isText
&& !aChild
->IsNodeOfType(nsINode::eCOMMENT
) &&
600 !aChild
->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION
)) {
604 return aTextIsSignificant
&& isText
&& aChild
->TextLength() != 0 &&
605 (aWhitespaceIsSignificant
||
606 !aChild
->TextIsOnlyWhitespace());