Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / layout / style / nsStyleUtil.cpp
blobae499a6420bb6931b1daa97f1c5942b792e5270d
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
38 #include <math.h>
39 #include "nsStyleUtil.h"
40 #include "nsCRT.h"
41 #include "nsStyleConsts.h"
43 #include "nsGkAtoms.h"
44 #include "nsIContent.h"
45 #include "nsIDocument.h"
46 #include "nsINameSpaceManager.h"
47 #include "nsIURI.h"
48 #include "nsNetUtil.h"
49 #include "nsReadableUtils.h"
50 #include "nsContentUtils.h"
51 #include "nsTextFormatter.h"
52 #include "nsCSSProps.h"
54 // XXX This is here because nsCachedStyleData is accessed outside of
55 // the content module; e.g., by nsCSSFrameConstructor.
56 #include "nsRuleNode.h"
58 nsCachedStyleData::StyleStructInfo
59 nsCachedStyleData::gInfo[] = {
61 #define STYLE_STRUCT_INHERITED(name, checkdata_cb, ctor_args) \
62 { offsetof(nsCachedStyleData, mInheritedData), \
63 offsetof(nsInheritedStyleData, m##name##Data), \
64 PR_FALSE },
65 #define STYLE_STRUCT_RESET(name, checkdata_cb, ctor_args) \
66 { offsetof(nsCachedStyleData, mResetData), \
67 offsetof(nsResetStyleData, m##name##Data), \
68 PR_TRUE },
70 #include "nsStyleStructList.h"
72 #undef STYLE_STRUCT_INHERITED
73 #undef STYLE_STRUCT_RESET
75 { 0, 0, 0 }
78 #define POSITIVE_SCALE_FACTOR 1.10 /* 10% */
79 #define NEGATIVE_SCALE_FACTOR .90 /* 10% */
82 //------------------------------------------------------------------------------
84 //------------------------------------------------------------------------------
87 * Return the scaling percentage given a font scaler
88 * Lifted from layutil.c
90 float nsStyleUtil::GetScalingFactor(PRInt32 aScaler)
92 double scale = 1.0;
93 double mult;
94 PRInt32 count;
96 if(aScaler < 0) {
97 count = -aScaler;
98 mult = NEGATIVE_SCALE_FACTOR;
100 else {
101 count = aScaler;
102 mult = POSITIVE_SCALE_FACTOR;
105 /* use the percentage scaling factor to the power of the pref */
106 while(count--) {
107 scale *= mult;
110 return (float)scale;
114 //------------------------------------------------------------------------------
115 // Font Algorithm Code
116 //------------------------------------------------------------------------------
118 nscoord
119 nsStyleUtil::CalcFontPointSize(PRInt32 aHTMLSize, PRInt32 aBasePointSize,
120 float aScalingFactor, nsPresContext* aPresContext,
121 nsFontSizeType aFontSizeType)
123 #define sFontSizeTableMin 9
124 #define sFontSizeTableMax 16
126 // This table seems to be the one used by MacIE5. We hope its adoption in Mozilla
127 // and eventually in WinIE5.5 will help to establish a standard rendering across
128 // platforms and browsers. For now, it is used only in Strict mode. More can be read
129 // in the document written by Todd Farhner at:
130 // http://style.verso.com/font_size_intervals/altintervals.html
132 static PRInt32 sStrictFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] =
134 { 9, 9, 9, 9, 11, 14, 18, 27},
135 { 9, 9, 9, 10, 12, 15, 20, 30},
136 { 9, 9, 10, 11, 13, 17, 22, 33},
137 { 9, 9, 10, 12, 14, 18, 24, 36},
138 { 9, 10, 12, 13, 16, 20, 26, 39},
139 { 9, 10, 12, 14, 17, 21, 28, 42},
140 { 9, 10, 13, 15, 18, 23, 30, 45},
141 { 9, 10, 13, 16, 18, 24, 32, 48}
143 // HTML 1 2 3 4 5 6 7
144 // CSS xxs xs s m l xl xxl
145 // |
146 // user pref
148 //------------------------------------------------------------
150 // This table gives us compatibility with WinNav4 for the default fonts only.
151 // In WinNav4, the default fonts were:
153 // Times/12pt == Times/16px at 96ppi
154 // Courier/10pt == Courier/13px at 96ppi
156 // The 2 lines below marked "anchored" have the exact pixel sizes used by
157 // WinNav4 for Times/12pt and Courier/10pt at 96ppi. As you can see, the
158 // HTML size 3 (user pref) for those 2 anchored lines is 13px and 16px.
160 // All values other than the anchored values were filled in by hand, never
161 // going below 9px, and maintaining a "diagonal" relationship. See for
162 // example the 13s -- they follow a diagonal line through the table.
164 static PRInt32 sQuirksFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] =
166 { 9, 9, 9, 9, 11, 14, 18, 28 },
167 { 9, 9, 9, 10, 12, 15, 20, 31 },
168 { 9, 9, 9, 11, 13, 17, 22, 34 },
169 { 9, 9, 10, 12, 14, 18, 24, 37 },
170 { 9, 9, 10, 13, 16, 20, 26, 40 }, // anchored (13)
171 { 9, 9, 11, 14, 17, 21, 28, 42 },
172 { 9, 10, 12, 15, 17, 23, 30, 45 },
173 { 9, 10, 13, 16, 18, 24, 32, 48 } // anchored (16)
175 // HTML 1 2 3 4 5 6 7
176 // CSS xxs xs s m l xl xxl
177 // |
178 // user pref
180 #if 0
182 // These are the exact pixel values used by WinIE5 at 96ppi.
184 { ?, 8, 11, 12, 13, 16, 21, 32 }, // smallest
185 { ?, 9, 12, 13, 16, 21, 27, 40 }, // smaller
186 { ?, 10, 13, 16, 18, 24, 32, 48 }, // medium
187 { ?, 13, 16, 19, 21, 27, 37, ?? }, // larger
188 { ?, 16, 19, 21, 24, 32, 43, ?? } // largest
190 // HTML 1 2 3 4 5 6 7
191 // CSS ? ? ? ? ? ? ? ?
193 // (CSS not tested yet.)
195 #endif
197 static PRInt32 sFontSizeFactors[8] = { 60,75,89,100,120,150,200,300 };
199 static PRInt32 sCSSColumns[7] = {0, 1, 2, 3, 4, 5, 6}; // xxs...xxl
200 static PRInt32 sHTMLColumns[7] = {1, 2, 3, 4, 5, 6, 7}; // 1...7
202 double dFontSize;
204 if (aFontSizeType == eFontSize_HTML) {
205 aHTMLSize--; // input as 1-7
208 if (aHTMLSize < 0)
209 aHTMLSize = 0;
210 else if (aHTMLSize > 6)
211 aHTMLSize = 6;
213 PRInt32* column;
214 switch (aFontSizeType)
216 case eFontSize_HTML: column = sHTMLColumns; break;
217 case eFontSize_CSS: column = sCSSColumns; break;
220 // Make special call specifically for fonts (needed PrintPreview)
221 PRInt32 fontSize = nsPresContext::AppUnitsToIntCSSPixels(aBasePointSize);
223 if ((fontSize >= sFontSizeTableMin) && (fontSize <= sFontSizeTableMax))
225 PRInt32 row = fontSize - sFontSizeTableMin;
227 if (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
228 dFontSize = nsPresContext::CSSPixelsToAppUnits(sQuirksFontSizeTable[row][column[aHTMLSize]]);
229 } else {
230 dFontSize = nsPresContext::CSSPixelsToAppUnits(sStrictFontSizeTable[row][column[aHTMLSize]]);
233 else
235 PRInt32 factor = sFontSizeFactors[column[aHTMLSize]];
236 dFontSize = (factor * aBasePointSize) / 100;
239 dFontSize *= aScalingFactor;
241 if (1.0 < dFontSize) {
242 return (nscoord)dFontSize;
244 return (nscoord)1;
248 //------------------------------------------------------------------------------
250 //------------------------------------------------------------------------------
252 nscoord nsStyleUtil::FindNextSmallerFontSize(nscoord aFontSize, PRInt32 aBasePointSize,
253 float aScalingFactor, nsPresContext* aPresContext,
254 nsFontSizeType aFontSizeType)
256 PRInt32 index;
257 PRInt32 indexMin;
258 PRInt32 indexMax;
259 float relativePosition;
260 nscoord smallerSize;
261 nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning
262 nscoord smallestIndexFontSize;
263 nscoord largestIndexFontSize;
264 nscoord smallerIndexFontSize;
265 nscoord largerIndexFontSize;
267 nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1);
269 if (aFontSizeType == eFontSize_HTML) {
270 indexMin = 1;
271 indexMax = 7;
272 } else {
273 indexMin = 0;
274 indexMax = 6;
277 smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
278 largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
279 if (aFontSize > smallestIndexFontSize) {
280 if (aFontSize < NSToCoordRound(float(largestIndexFontSize) * 1.5)) { // smaller will be in HTML table
281 // find largest index smaller than current
282 for (index = indexMax; index >= indexMin; index--) {
283 indexFontSize = CalcFontPointSize(index, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
284 if (indexFontSize < aFontSize)
285 break;
287 // set up points beyond table for interpolation purposes
288 if (indexFontSize == smallestIndexFontSize) {
289 smallerIndexFontSize = indexFontSize - onePx;
290 largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
291 } else if (indexFontSize == largestIndexFontSize) {
292 smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
293 largerIndexFontSize = NSToCoordRound(float(largestIndexFontSize) * 1.5);
294 } else {
295 smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
296 largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
298 // compute the relative position of the parent size between the two closest indexed sizes
299 relativePosition = float(aFontSize - indexFontSize) / float(largerIndexFontSize - indexFontSize);
300 // set the new size to have the same relative position between the next smallest two indexed sizes
301 smallerSize = smallerIndexFontSize + NSToCoordRound(relativePosition * (indexFontSize - smallerIndexFontSize));
303 else { // larger than HTML table, drop by 33%
304 smallerSize = NSToCoordRound(float(aFontSize) / 1.5);
307 else { // smaller than HTML table, drop by 1px
308 smallerSize = NS_MAX(aFontSize - onePx, onePx);
310 return smallerSize;
313 //------------------------------------------------------------------------------
315 //------------------------------------------------------------------------------
317 nscoord nsStyleUtil::FindNextLargerFontSize(nscoord aFontSize, PRInt32 aBasePointSize,
318 float aScalingFactor, nsPresContext* aPresContext,
319 nsFontSizeType aFontSizeType)
321 PRInt32 index;
322 PRInt32 indexMin;
323 PRInt32 indexMax;
324 float relativePosition;
325 nscoord largerSize;
326 nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning
327 nscoord smallestIndexFontSize;
328 nscoord largestIndexFontSize;
329 nscoord smallerIndexFontSize;
330 nscoord largerIndexFontSize;
332 nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1);
334 if (aFontSizeType == eFontSize_HTML) {
335 indexMin = 1;
336 indexMax = 7;
337 } else {
338 indexMin = 0;
339 indexMax = 6;
342 smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
343 largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
344 if (aFontSize > (smallestIndexFontSize - onePx)) {
345 if (aFontSize < largestIndexFontSize) { // larger will be in HTML table
346 // find smallest index larger than current
347 for (index = indexMin; index <= indexMax; index++) {
348 indexFontSize = CalcFontPointSize(index, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
349 if (indexFontSize > aFontSize)
350 break;
352 // set up points beyond table for interpolation purposes
353 if (indexFontSize == smallestIndexFontSize) {
354 smallerIndexFontSize = indexFontSize - onePx;
355 largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
356 } else if (indexFontSize == largestIndexFontSize) {
357 smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
358 largerIndexFontSize = NSToCoordRound(float(largestIndexFontSize) * 1.5);
359 } else {
360 smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
361 largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
363 // compute the relative position of the parent size between the two closest indexed sizes
364 relativePosition = float(aFontSize - smallerIndexFontSize) / float(indexFontSize - smallerIndexFontSize);
365 // set the new size to have the same relative position between the next largest two indexed sizes
366 largerSize = indexFontSize + NSToCoordRound(relativePosition * (largerIndexFontSize - indexFontSize));
368 else { // larger than HTML table, increase by 50%
369 largerSize = NSToCoordRound(float(aFontSize) * 1.5);
372 else { // smaller than HTML table, increase by 1px
373 largerSize = aFontSize + onePx;
375 return largerSize;
378 //------------------------------------------------------------------------------
380 //------------------------------------------------------------------------------
382 PRInt32
383 nsStyleUtil::ConstrainFontWeight(PRInt32 aWeight)
385 aWeight = ((aWeight < 100) ? 100 : ((aWeight > 900) ? 900 : aWeight));
386 PRInt32 base = ((aWeight / 100) * 100);
387 PRInt32 step = (aWeight % 100);
388 PRBool negativeStep = PRBool(50 < step);
389 PRInt32 maxStep;
390 if (negativeStep) {
391 step = 100 - step;
392 maxStep = (base / 100);
393 base += 100;
395 else {
396 maxStep = ((900 - base) / 100);
398 if (maxStep < step) {
399 step = maxStep;
401 return (base + ((negativeStep) ? -step : step));
404 // Compare two language strings
405 PRBool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue,
406 const nsAString& aSelectorValue,
407 const nsStringComparator& aComparator)
409 PRBool result;
410 PRUint32 selectorLen = aSelectorValue.Length();
411 PRUint32 attributeLen = aAttributeValue.Length();
412 if (selectorLen > attributeLen) {
413 result = PR_FALSE;
415 else {
416 nsAString::const_iterator iter;
417 if (selectorLen != attributeLen &&
418 *aAttributeValue.BeginReading(iter).advance(selectorLen) !=
419 PRUnichar('-')) {
420 // to match, the aAttributeValue must have a dash after the end of
421 // the aSelectorValue's text (unless the aSelectorValue and the
422 // aAttributeValue have the same text)
423 result = PR_FALSE;
425 else {
426 result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator);
429 return result;
432 void nsStyleUtil::AppendEscapedCSSString(const nsString& aString,
433 nsAString& aReturn)
435 aReturn.Append(PRUnichar('"'));
437 const nsString::char_type* in = aString.get();
438 const nsString::char_type* const end = in + aString.Length();
439 for (; in != end; in++)
441 if (*in < 0x20)
443 // Escape all characters below 0x20 numerically.
446 This is the buffer into which snprintf should write. As the hex. value is,
447 for numbers below 0x20, max. 2 characters long, we don't need more than 5
448 characters ("\XX "+NUL).
450 PRUnichar buf[5];
451 nsTextFormatter::snprintf(buf, NS_ARRAY_LENGTH(buf), NS_LITERAL_STRING("\\%hX ").get(), *in);
452 aReturn.Append(buf);
454 } else switch (*in) {
455 // Special characters which should be escaped: Quotes and backslash
456 case '\\':
457 case '\"':
458 case '\'':
459 aReturn.Append(PRUnichar('\\'));
460 // And now, after the eventual escaping character, the actual one.
461 default:
462 aReturn.Append(PRUnichar(*in));
466 aReturn.Append(PRUnichar('"'));
469 /* static */ void
470 nsStyleUtil::AppendEscapedCSSIdent(const nsString& aIdent, nsAString& aReturn)
472 // The relevant parts of the CSS grammar are:
473 // ident [-]?{nmstart}{nmchar}*
474 // nmstart [_a-z]|{nonascii}|{escape}
475 // nmchar [_a-z0-9-]|{nonascii}|{escape}
476 // nonascii [^\0-\177]
477 // escape {unicode}|\\[^\n\r\f0-9a-f]
478 // unicode \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
479 // from http://www.w3.org/TR/CSS21/syndata.html#tokenization
481 const nsString::char_type* in = aIdent.get();
482 const nsString::char_type* const end = in + aIdent.Length();
484 // Deal with the leading dash separately so we don't need to
485 // unnecessarily escape digits.
486 if (in != end && *in == '-') {
487 aReturn.Append(PRUnichar('-'));
488 ++in;
491 PRBool first = PR_TRUE;
492 for (; in != end; ++in, first = PR_FALSE)
494 if (*in < 0x20 || (first && '0' <= *in && *in <= '9'))
496 // Escape all characters below 0x20, and digits at the start
497 // (including after a dash), numerically. If we didn't escape
498 // digits numerically, they'd get interpreted as a numeric escape
499 // for the wrong character.
502 This is the buffer into which snprintf should write. As the hex.
503 value is, for numbers below 0x7F, max. 2 characters long, we
504 don't need more than 5 characters ("\XX "+NUL).
506 PRUnichar buf[5];
507 nsTextFormatter::snprintf(buf, NS_ARRAY_LENGTH(buf),
508 NS_LITERAL_STRING("\\%hX ").get(), *in);
509 aReturn.Append(buf);
510 } else {
511 PRUnichar ch = *in;
512 if (!((ch == PRUnichar('_')) ||
513 (PRUnichar('A') <= ch && ch <= PRUnichar('Z')) ||
514 (PRUnichar('a') <= ch && ch <= PRUnichar('z')) ||
515 PRUnichar(0x80) <= ch ||
516 (!first && ch == PRUnichar('-')) ||
517 (PRUnichar('0') <= ch && ch <= PRUnichar('9')))) {
518 // Character needs to be escaped
519 aReturn.Append(PRUnichar('\\'));
521 aReturn.Append(ch);
526 /* static */ void
527 nsStyleUtil::AppendBitmaskCSSValue(nsCSSProperty aProperty,
528 PRInt32 aMaskedValue,
529 PRInt32 aFirstMask,
530 PRInt32 aLastMask,
531 nsAString& aResult)
533 for (PRInt32 mask = aFirstMask; mask <= aLastMask; mask <<= 1) {
534 if (mask & aMaskedValue) {
535 AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, mask),
536 aResult);
537 aMaskedValue &= ~mask;
538 if (aMaskedValue) { // more left
539 aResult.Append(PRUnichar(' '));
543 NS_ABORT_IF_FALSE(aMaskedValue == 0, "unexpected bit remaining in bitfield");
546 /* static */ float
547 nsStyleUtil::ColorComponentToFloat(PRUint8 aAlpha)
549 // Alpha values are expressed as decimals, so we should convert
550 // back, using as few decimal places as possible for
551 // round-tripping.
552 // First try two decimal places:
553 float rounded = NS_roundf(float(aAlpha) * 100.0f / 255.0f) / 100.0f;
554 if (FloatToColorComponent(rounded) != aAlpha) {
555 // Use three decimal places.
556 rounded = NS_roundf(float(aAlpha) * 1000.0f / 255.0f) / 1000.0f;
558 return rounded;
561 /* static */ PRBool
562 nsStyleUtil::IsSignificantChild(nsIContent* aChild, PRBool aTextIsSignificant,
563 PRBool aWhitespaceIsSignificant)
565 NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant,
566 "Nonsensical arguments");
568 PRBool isText = aChild->IsNodeOfType(nsINode::eTEXT);
570 if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
571 !aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
572 return PR_TRUE;
575 return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
576 (aWhitespaceIsSignificant ||
577 !aChild->TextIsOnlyWhitespace());