Bug 454376 add -lCrun -lCstd for Solaris OS_LIBS, r=bsmedberg
[wine-gecko.git] / layout / style / nsStyleUtil.cpp
blobe1738c5feca0d8f878c5782e90a2f7a82e7afaa8
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 "nsILinkHandler.h"
45 #include "nsILink.h"
46 #include "nsIContent.h"
47 #include "nsIDocument.h"
48 #include "nsINameSpaceManager.h"
49 #include "nsIURI.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), \
65 PR_FALSE },
66 #define STYLE_STRUCT_RESET(name, checkdata_cb, ctor_args) \
67 { offsetof(nsCachedStyleData, mResetData), \
68 offsetof(nsResetStyleData, m##name##Data), \
69 PR_TRUE },
71 #include "nsStyleStructList.h"
73 #undef STYLE_STRUCT_INHERITED
74 #undef STYLE_STRUCT_RESET
76 { 0, 0, 0 }
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)
93 double scale = 1.0;
94 double mult;
95 PRInt32 count;
97 if(aScaler < 0) {
98 count = -aScaler;
99 mult = NEGATIVE_SCALE_FACTOR;
101 else {
102 count = aScaler;
103 mult = POSITIVE_SCALE_FACTOR;
106 /* use the percentage scaling factor to the power of the pref */
107 while(count--) {
108 scale *= mult;
111 return (float)scale;
115 //------------------------------------------------------------------------------
116 // Font Algorithm Code
117 //------------------------------------------------------------------------------
119 nscoord
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
146 // |
147 // user pref
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
178 // |
179 // user pref
181 #if 0
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.)
196 #endif
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
203 double dFontSize;
205 if (aFontSizeType == eFontSize_HTML) {
206 aHTMLSize--; // input as 1-7
209 if (aHTMLSize < 0)
210 aHTMLSize = 0;
211 else if (aHTMLSize > 6)
212 aHTMLSize = 6;
214 PRInt32* column;
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]]);
230 } else {
231 dFontSize = nsPresContext::CSSPixelsToAppUnits(sStrictFontSizeTable[row][column[aHTMLSize]]);
234 else
236 PRInt32 factor = sFontSizeFactors[column[aHTMLSize]];
237 dFontSize = (factor * aBasePointSize) / 100;
240 dFontSize *= aScalingFactor;
242 if (1.0 < dFontSize) {
243 return (nscoord)dFontSize;
245 return (nscoord)1;
249 //------------------------------------------------------------------------------
251 //------------------------------------------------------------------------------
253 nscoord nsStyleUtil::FindNextSmallerFontSize(nscoord aFontSize, PRInt32 aBasePointSize,
254 float aScalingFactor, nsPresContext* aPresContext,
255 nsFontSizeType aFontSizeType)
257 PRInt32 index;
258 PRInt32 indexMin;
259 PRInt32 indexMax;
260 float relativePosition;
261 nscoord smallerSize;
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) {
271 indexMin = 1;
272 indexMax = 7;
273 } else {
274 indexMin = 0;
275 indexMax = 6;
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)
286 break;
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);
295 } else {
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);
311 return smallerSize;
314 //------------------------------------------------------------------------------
316 //------------------------------------------------------------------------------
318 nscoord nsStyleUtil::FindNextLargerFontSize(nscoord aFontSize, PRInt32 aBasePointSize,
319 float aScalingFactor, nsPresContext* aPresContext,
320 nsFontSizeType aFontSizeType)
322 PRInt32 index;
323 PRInt32 indexMin;
324 PRInt32 indexMax;
325 float relativePosition;
326 nscoord largerSize;
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) {
336 indexMin = 1;
337 indexMax = 7;
338 } else {
339 indexMin = 0;
340 indexMax = 6;
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)
351 break;
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);
360 } else {
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;
376 return largerSize;
379 //------------------------------------------------------------------------------
381 //------------------------------------------------------------------------------
383 PRInt32
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);
390 PRInt32 maxStep;
391 if (negativeStep) {
392 step = 100 - step;
393 maxStep = (base / 100);
394 base += 100;
396 else {
397 maxStep = ((900 - base) / 100);
399 if (maxStep < step) {
400 step = maxStep;
402 return (base + ((negativeStep) ? -step : step));
405 static nsLinkState
406 GetLinkStateFromURI(nsIURI* aURI, nsIContent* aContent,
407 nsILinkHandler* aLinkHandler)
409 NS_PRECONDITION(aURI, "Must have URI");
410 nsLinkState state;
411 if (NS_LIKELY(aLinkHandler)) {
412 aLinkHandler->GetLinkState(aURI, state);
414 else {
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);
420 if (handler) {
421 handler->GetLinkState(aURI, state);
422 } else {
423 // no link handler? then all links are unvisited
424 state = eLinkState_Unvisited;
428 return state;
431 /*static*/
432 PRBool nsStyleUtil::IsHTMLLink(nsIContent *aContent, nsIAtom *aTag,
433 nsILinkHandler *aLinkHandler,
434 PRBool aForStyling,
435 nsLinkState *aState)
437 NS_ASSERTION(aContent && aState, "null arg in IsHTMLLink");
439 // check for:
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.
452 if (link) {
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.
459 // bug=23209
461 nsCOMPtr<nsIURI> hrefURI;
462 link->GetHrefURI(getter_AddRefs(hrefURI));
464 if (hrefURI) {
465 linkState = GetLinkStateFromURI(hrefURI, aContent, aLinkHandler);
466 } else {
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) {
476 *aState = linkState;
477 result = PR_TRUE;
482 return result;
485 /*static*/
486 PRBool nsStyleUtil::IsLink(nsIContent *aContent,
487 nsILinkHandler *aLinkHandler,
488 PRBool aForStyling,
489 nsLinkState *aState)
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);
507 rv = PR_TRUE;
510 return rv;
513 // Compare two language strings
514 PRBool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue,
515 const nsAString& aSelectorValue,
516 const nsStringComparator& aComparator)
518 PRBool result;
519 PRUint32 selectorLen = aSelectorValue.Length();
520 PRUint32 attributeLen = aAttributeValue.Length();
521 if (selectorLen > attributeLen) {
522 result = PR_FALSE;
524 else {
525 nsAString::const_iterator iter;
526 if (selectorLen != attributeLen &&
527 *aAttributeValue.BeginReading(iter).advance(selectorLen) !=
528 PRUnichar('-')) {
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)
532 result = PR_FALSE;
534 else {
535 result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator);
538 return result;
541 void nsStyleUtil::EscapeCSSString(const nsString& aString, nsAString& aReturn)
543 aReturn.Truncate();
545 const nsString::char_type* in = aString.get();
546 const nsString::char_type* const end = in + aString.Length();
547 for (; in != end; in++)
549 if (*in < 0x20)
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).
558 PRUnichar buf[5];
559 nsTextFormatter::snprintf(buf, NS_ARRAY_LENGTH(buf), NS_LITERAL_STRING("\\%hX ").get(), *in);
560 aReturn.Append(buf);
562 } else switch (*in) {
563 // Special characters which should be escaped: Quotes and backslash
564 case '\\':
565 case '\"':
566 case '\'':
567 aReturn.Append(PRUnichar('\\'));
568 // And now, after the eventual escaping character, the actual one.
569 default:
570 aReturn.Append(PRUnichar(*in));
575 /* static */ float
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
580 // round-tripping.
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;
587 return rounded;
590 /* static */ PRBool
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)) {
601 return PR_TRUE;
604 return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
605 (aWhitespaceIsSignificant ||
606 !aChild->TextIsOnlyWhitespace());