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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : *
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.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include <math.h>
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "nsStyleUtil.h"
43 : #include "nsCRT.h"
44 : #include "nsStyleConsts.h"
45 :
46 : #include "nsGkAtoms.h"
47 : #include "nsIContent.h"
48 : #include "nsIDocument.h"
49 : #include "nsINameSpaceManager.h"
50 : #include "nsIURI.h"
51 : #include "nsNetUtil.h"
52 : #include "nsReadableUtils.h"
53 : #include "nsContentUtils.h"
54 : #include "nsTextFormatter.h"
55 : #include "nsCSSProps.h"
56 :
57 : using namespace mozilla;
58 :
59 : //------------------------------------------------------------------------------
60 : // Font Algorithm Code
61 : //------------------------------------------------------------------------------
62 :
63 : nscoord
64 0 : nsStyleUtil::CalcFontPointSize(PRInt32 aHTMLSize, PRInt32 aBasePointSize,
65 : nsPresContext* aPresContext,
66 : nsFontSizeType aFontSizeType)
67 : {
68 : #define sFontSizeTableMin 9
69 : #define sFontSizeTableMax 16
70 :
71 : // This table seems to be the one used by MacIE5. We hope its adoption in Mozilla
72 : // and eventually in WinIE5.5 will help to establish a standard rendering across
73 : // platforms and browsers. For now, it is used only in Strict mode. More can be read
74 : // in the document written by Todd Farhner at:
75 : // http://style.verso.com/font_size_intervals/altintervals.html
76 : //
77 : static PRInt32 sStrictFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] =
78 : {
79 : { 9, 9, 9, 9, 11, 14, 18, 27},
80 : { 9, 9, 9, 10, 12, 15, 20, 30},
81 : { 9, 9, 10, 11, 13, 17, 22, 33},
82 : { 9, 9, 10, 12, 14, 18, 24, 36},
83 : { 9, 10, 12, 13, 16, 20, 26, 39},
84 : { 9, 10, 12, 14, 17, 21, 28, 42},
85 : { 9, 10, 13, 15, 18, 23, 30, 45},
86 : { 9, 10, 13, 16, 18, 24, 32, 48}
87 : };
88 : // HTML 1 2 3 4 5 6 7
89 : // CSS xxs xs s m l xl xxl
90 : // |
91 : // user pref
92 : //
93 : //------------------------------------------------------------
94 : //
95 : // This table gives us compatibility with WinNav4 for the default fonts only.
96 : // In WinNav4, the default fonts were:
97 : //
98 : // Times/12pt == Times/16px at 96ppi
99 : // Courier/10pt == Courier/13px at 96ppi
100 : //
101 : // The 2 lines below marked "anchored" have the exact pixel sizes used by
102 : // WinNav4 for Times/12pt and Courier/10pt at 96ppi. As you can see, the
103 : // HTML size 3 (user pref) for those 2 anchored lines is 13px and 16px.
104 : //
105 : // All values other than the anchored values were filled in by hand, never
106 : // going below 9px, and maintaining a "diagonal" relationship. See for
107 : // example the 13s -- they follow a diagonal line through the table.
108 : //
109 : static PRInt32 sQuirksFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] =
110 : {
111 : { 9, 9, 9, 9, 11, 14, 18, 28 },
112 : { 9, 9, 9, 10, 12, 15, 20, 31 },
113 : { 9, 9, 9, 11, 13, 17, 22, 34 },
114 : { 9, 9, 10, 12, 14, 18, 24, 37 },
115 : { 9, 9, 10, 13, 16, 20, 26, 40 }, // anchored (13)
116 : { 9, 9, 11, 14, 17, 21, 28, 42 },
117 : { 9, 10, 12, 15, 17, 23, 30, 45 },
118 : { 9, 10, 13, 16, 18, 24, 32, 48 } // anchored (16)
119 : };
120 : // HTML 1 2 3 4 5 6 7
121 : // CSS xxs xs s m l xl xxl
122 : // |
123 : // user pref
124 :
125 : #if 0
126 : //
127 : // These are the exact pixel values used by WinIE5 at 96ppi.
128 : //
129 : { ?, 8, 11, 12, 13, 16, 21, 32 }, // smallest
130 : { ?, 9, 12, 13, 16, 21, 27, 40 }, // smaller
131 : { ?, 10, 13, 16, 18, 24, 32, 48 }, // medium
132 : { ?, 13, 16, 19, 21, 27, 37, ?? }, // larger
133 : { ?, 16, 19, 21, 24, 32, 43, ?? } // largest
134 : //
135 : // HTML 1 2 3 4 5 6 7
136 : // CSS ? ? ? ? ? ? ? ?
137 : //
138 : // (CSS not tested yet.)
139 : //
140 : #endif
141 :
142 : static PRInt32 sFontSizeFactors[8] = { 60,75,89,100,120,150,200,300 };
143 :
144 : static PRInt32 sCSSColumns[7] = {0, 1, 2, 3, 4, 5, 6}; // xxs...xxl
145 : static PRInt32 sHTMLColumns[7] = {1, 2, 3, 4, 5, 6, 7}; // 1...7
146 :
147 : double dFontSize;
148 :
149 0 : if (aFontSizeType == eFontSize_HTML) {
150 0 : aHTMLSize--; // input as 1-7
151 : }
152 :
153 0 : if (aHTMLSize < 0)
154 0 : aHTMLSize = 0;
155 0 : else if (aHTMLSize > 6)
156 0 : aHTMLSize = 6;
157 :
158 : PRInt32* column;
159 0 : switch (aFontSizeType)
160 : {
161 0 : case eFontSize_HTML: column = sHTMLColumns; break;
162 0 : case eFontSize_CSS: column = sCSSColumns; break;
163 : }
164 :
165 : // Make special call specifically for fonts (needed PrintPreview)
166 0 : PRInt32 fontSize = nsPresContext::AppUnitsToIntCSSPixels(aBasePointSize);
167 :
168 0 : if ((fontSize >= sFontSizeTableMin) && (fontSize <= sFontSizeTableMax))
169 : {
170 0 : PRInt32 row = fontSize - sFontSizeTableMin;
171 :
172 0 : if (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
173 0 : dFontSize = nsPresContext::CSSPixelsToAppUnits(sQuirksFontSizeTable[row][column[aHTMLSize]]);
174 : } else {
175 0 : dFontSize = nsPresContext::CSSPixelsToAppUnits(sStrictFontSizeTable[row][column[aHTMLSize]]);
176 0 : }
177 : }
178 : else
179 : {
180 0 : PRInt32 factor = sFontSizeFactors[column[aHTMLSize]];
181 0 : dFontSize = (factor * aBasePointSize) / 100;
182 : }
183 :
184 :
185 0 : if (1.0 < dFontSize) {
186 0 : return (nscoord)dFontSize;
187 : }
188 0 : return (nscoord)1;
189 : }
190 :
191 :
192 : //------------------------------------------------------------------------------
193 : //
194 : //------------------------------------------------------------------------------
195 :
196 0 : nscoord nsStyleUtil::FindNextSmallerFontSize(nscoord aFontSize, PRInt32 aBasePointSize,
197 : nsPresContext* aPresContext,
198 : nsFontSizeType aFontSizeType)
199 : {
200 : PRInt32 index;
201 : PRInt32 indexMin;
202 : PRInt32 indexMax;
203 : float relativePosition;
204 : nscoord smallerSize;
205 0 : nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning
206 : nscoord smallestIndexFontSize;
207 : nscoord largestIndexFontSize;
208 : nscoord smallerIndexFontSize;
209 : nscoord largerIndexFontSize;
210 :
211 0 : nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1);
212 :
213 0 : if (aFontSizeType == eFontSize_HTML) {
214 0 : indexMin = 1;
215 0 : indexMax = 7;
216 : } else {
217 0 : indexMin = 0;
218 0 : indexMax = 6;
219 : }
220 :
221 0 : smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aPresContext, aFontSizeType);
222 0 : largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aPresContext, aFontSizeType);
223 0 : if (aFontSize > smallestIndexFontSize) {
224 0 : if (aFontSize < NSToCoordRound(float(largestIndexFontSize) * 1.5)) { // smaller will be in HTML table
225 : // find largest index smaller than current
226 0 : for (index = indexMax; index >= indexMin; index--) {
227 0 : indexFontSize = CalcFontPointSize(index, aBasePointSize, aPresContext, aFontSizeType);
228 0 : if (indexFontSize < aFontSize)
229 0 : break;
230 : }
231 : // set up points beyond table for interpolation purposes
232 0 : if (indexFontSize == smallestIndexFontSize) {
233 0 : smallerIndexFontSize = indexFontSize - onePx;
234 0 : largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType);
235 0 : } else if (indexFontSize == largestIndexFontSize) {
236 0 : smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType);
237 0 : largerIndexFontSize = NSToCoordRound(float(largestIndexFontSize) * 1.5);
238 : } else {
239 0 : smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType);
240 0 : largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType);
241 : }
242 : // compute the relative position of the parent size between the two closest indexed sizes
243 0 : relativePosition = float(aFontSize - indexFontSize) / float(largerIndexFontSize - indexFontSize);
244 : // set the new size to have the same relative position between the next smallest two indexed sizes
245 0 : smallerSize = smallerIndexFontSize + NSToCoordRound(relativePosition * (indexFontSize - smallerIndexFontSize));
246 : }
247 : else { // larger than HTML table, drop by 33%
248 0 : smallerSize = NSToCoordRound(float(aFontSize) / 1.5);
249 : }
250 : }
251 : else { // smaller than HTML table, drop by 1px
252 0 : smallerSize = NS_MAX(aFontSize - onePx, onePx);
253 : }
254 0 : return smallerSize;
255 : }
256 :
257 : //------------------------------------------------------------------------------
258 : //
259 : //------------------------------------------------------------------------------
260 :
261 0 : nscoord nsStyleUtil::FindNextLargerFontSize(nscoord aFontSize, PRInt32 aBasePointSize,
262 : nsPresContext* aPresContext,
263 : nsFontSizeType aFontSizeType)
264 : {
265 : PRInt32 index;
266 : PRInt32 indexMin;
267 : PRInt32 indexMax;
268 : float relativePosition;
269 : nscoord adjustment;
270 : nscoord largerSize;
271 0 : nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning
272 : nscoord smallestIndexFontSize;
273 : nscoord largestIndexFontSize;
274 : nscoord smallerIndexFontSize;
275 : nscoord largerIndexFontSize;
276 :
277 0 : nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1);
278 :
279 0 : if (aFontSizeType == eFontSize_HTML) {
280 0 : indexMin = 1;
281 0 : indexMax = 7;
282 : } else {
283 0 : indexMin = 0;
284 0 : indexMax = 6;
285 : }
286 :
287 0 : smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aPresContext, aFontSizeType);
288 0 : largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aPresContext, aFontSizeType);
289 0 : if (aFontSize > (smallestIndexFontSize - onePx)) {
290 0 : if (aFontSize < largestIndexFontSize) { // larger will be in HTML table
291 : // find smallest index larger than current
292 0 : for (index = indexMin; index <= indexMax; index++) {
293 0 : indexFontSize = CalcFontPointSize(index, aBasePointSize, aPresContext, aFontSizeType);
294 0 : if (indexFontSize > aFontSize)
295 0 : break;
296 : }
297 : // set up points beyond table for interpolation purposes
298 0 : if (indexFontSize == smallestIndexFontSize) {
299 0 : smallerIndexFontSize = indexFontSize - onePx;
300 0 : largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType);
301 0 : } else if (indexFontSize == largestIndexFontSize) {
302 0 : smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType);
303 0 : largerIndexFontSize = NSCoordSaturatingMultiply(largestIndexFontSize, 1.5);
304 : } else {
305 0 : smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType);
306 0 : largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType);
307 : }
308 : // compute the relative position of the parent size between the two closest indexed sizes
309 0 : relativePosition = float(aFontSize - smallerIndexFontSize) / float(indexFontSize - smallerIndexFontSize);
310 : // set the new size to have the same relative position between the next largest two indexed sizes
311 0 : adjustment = NSCoordSaturatingNonnegativeMultiply(largerIndexFontSize - indexFontSize, relativePosition);
312 0 : largerSize = NSCoordSaturatingAdd(indexFontSize, adjustment);
313 : }
314 : else { // larger than HTML table, increase by 50%
315 0 : largerSize = NSCoordSaturatingMultiply(aFontSize, 1.5);
316 : }
317 : }
318 : else { // smaller than HTML table, increase by 1px
319 0 : largerSize = NSCoordSaturatingAdd(aFontSize, onePx);
320 : }
321 0 : return largerSize;
322 : }
323 :
324 : //------------------------------------------------------------------------------
325 : //
326 : //------------------------------------------------------------------------------
327 :
328 : PRInt32
329 0 : nsStyleUtil::ConstrainFontWeight(PRInt32 aWeight)
330 : {
331 0 : aWeight = ((aWeight < 100) ? 100 : ((aWeight > 900) ? 900 : aWeight));
332 0 : PRInt32 base = ((aWeight / 100) * 100);
333 0 : PRInt32 step = (aWeight % 100);
334 0 : bool negativeStep = bool(50 < step);
335 : PRInt32 maxStep;
336 0 : if (negativeStep) {
337 0 : step = 100 - step;
338 0 : maxStep = (base / 100);
339 0 : base += 100;
340 : }
341 : else {
342 0 : maxStep = ((900 - base) / 100);
343 : }
344 0 : if (maxStep < step) {
345 0 : step = maxStep;
346 : }
347 0 : return (base + ((negativeStep) ? -step : step));
348 : }
349 :
350 : // Compare two language strings
351 0 : bool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue,
352 : const nsAString& aSelectorValue,
353 : const nsStringComparator& aComparator)
354 : {
355 : bool result;
356 0 : PRUint32 selectorLen = aSelectorValue.Length();
357 0 : PRUint32 attributeLen = aAttributeValue.Length();
358 0 : if (selectorLen > attributeLen) {
359 0 : result = false;
360 : }
361 : else {
362 0 : nsAString::const_iterator iter;
363 0 : if (selectorLen != attributeLen &&
364 0 : *aAttributeValue.BeginReading(iter).advance(selectorLen) !=
365 : PRUnichar('-')) {
366 : // to match, the aAttributeValue must have a dash after the end of
367 : // the aSelectorValue's text (unless the aSelectorValue and the
368 : // aAttributeValue have the same text)
369 0 : result = false;
370 : }
371 : else {
372 0 : result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator);
373 : }
374 : }
375 0 : return result;
376 : }
377 :
378 0 : void nsStyleUtil::AppendEscapedCSSString(const nsString& aString,
379 : nsAString& aReturn)
380 : {
381 0 : aReturn.Append(PRUnichar('"'));
382 :
383 0 : const nsString::char_type* in = aString.get();
384 0 : const nsString::char_type* const end = in + aString.Length();
385 0 : for (; in != end; in++)
386 : {
387 0 : if (*in < 0x20)
388 : {
389 : // Escape all characters below 0x20 numerically.
390 :
391 : /*
392 : This is the buffer into which snprintf should write. As the hex. value is,
393 : for numbers below 0x20, max. 2 characters long, we don't need more than 5
394 : characters ("\XX "+NUL).
395 : */
396 : PRUnichar buf[5];
397 0 : nsTextFormatter::snprintf(buf, ArrayLength(buf), NS_LITERAL_STRING("\\%hX ").get(), *in);
398 0 : aReturn.Append(buf);
399 :
400 0 : } else switch (*in) {
401 : // Special characters which should be escaped: Quotes and backslash
402 : case '\\':
403 : case '\"':
404 : case '\'':
405 0 : aReturn.Append(PRUnichar('\\'));
406 : // And now, after the eventual escaping character, the actual one.
407 : default:
408 0 : aReturn.Append(PRUnichar(*in));
409 : }
410 : }
411 :
412 0 : aReturn.Append(PRUnichar('"'));
413 0 : }
414 :
415 : /* static */ void
416 0 : nsStyleUtil::AppendEscapedCSSIdent(const nsString& aIdent, nsAString& aReturn)
417 : {
418 : // The relevant parts of the CSS grammar are:
419 : // ident [-]?{nmstart}{nmchar}*
420 : // nmstart [_a-z]|{nonascii}|{escape}
421 : // nmchar [_a-z0-9-]|{nonascii}|{escape}
422 : // nonascii [^\0-\177]
423 : // escape {unicode}|\\[^\n\r\f0-9a-f]
424 : // unicode \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
425 : // from http://www.w3.org/TR/CSS21/syndata.html#tokenization
426 :
427 0 : const nsString::char_type* in = aIdent.get();
428 0 : const nsString::char_type* const end = in + aIdent.Length();
429 :
430 : // Deal with the leading dash separately so we don't need to
431 : // unnecessarily escape digits.
432 0 : if (in != end && *in == '-') {
433 0 : aReturn.Append(PRUnichar('-'));
434 0 : ++in;
435 : }
436 :
437 0 : bool first = true;
438 0 : for (; in != end; ++in, first = false)
439 : {
440 0 : if (*in < 0x20 || (first && '0' <= *in && *in <= '9'))
441 : {
442 : // Escape all characters below 0x20, and digits at the start
443 : // (including after a dash), numerically. If we didn't escape
444 : // digits numerically, they'd get interpreted as a numeric escape
445 : // for the wrong character.
446 :
447 : /*
448 : This is the buffer into which snprintf should write. As the hex.
449 : value is, for numbers below 0x7F, max. 2 characters long, we
450 : don't need more than 5 characters ("\XX "+NUL).
451 : */
452 : PRUnichar buf[5];
453 : nsTextFormatter::snprintf(buf, ArrayLength(buf),
454 0 : NS_LITERAL_STRING("\\%hX ").get(), *in);
455 0 : aReturn.Append(buf);
456 : } else {
457 0 : PRUnichar ch = *in;
458 0 : if (!((ch == PRUnichar('_')) ||
459 : (PRUnichar('A') <= ch && ch <= PRUnichar('Z')) ||
460 : (PRUnichar('a') <= ch && ch <= PRUnichar('z')) ||
461 : PRUnichar(0x80) <= ch ||
462 : (!first && ch == PRUnichar('-')) ||
463 0 : (PRUnichar('0') <= ch && ch <= PRUnichar('9')))) {
464 : // Character needs to be escaped
465 0 : aReturn.Append(PRUnichar('\\'));
466 : }
467 0 : aReturn.Append(ch);
468 : }
469 : }
470 0 : }
471 :
472 : /* static */ void
473 0 : nsStyleUtil::AppendBitmaskCSSValue(nsCSSProperty aProperty,
474 : PRInt32 aMaskedValue,
475 : PRInt32 aFirstMask,
476 : PRInt32 aLastMask,
477 : nsAString& aResult)
478 : {
479 0 : for (PRInt32 mask = aFirstMask; mask <= aLastMask; mask <<= 1) {
480 0 : if (mask & aMaskedValue) {
481 0 : AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, mask),
482 0 : aResult);
483 0 : aMaskedValue &= ~mask;
484 0 : if (aMaskedValue) { // more left
485 0 : aResult.Append(PRUnichar(' '));
486 : }
487 : }
488 : }
489 0 : NS_ABORT_IF_FALSE(aMaskedValue == 0, "unexpected bit remaining in bitfield");
490 0 : }
491 :
492 : /* static */ float
493 0 : nsStyleUtil::ColorComponentToFloat(PRUint8 aAlpha)
494 : {
495 : // Alpha values are expressed as decimals, so we should convert
496 : // back, using as few decimal places as possible for
497 : // round-tripping.
498 : // First try two decimal places:
499 0 : float rounded = NS_roundf(float(aAlpha) * 100.0f / 255.0f) / 100.0f;
500 0 : if (FloatToColorComponent(rounded) != aAlpha) {
501 : // Use three decimal places.
502 0 : rounded = NS_roundf(float(aAlpha) * 1000.0f / 255.0f) / 1000.0f;
503 : }
504 0 : return rounded;
505 : }
506 :
507 : /* static */ bool
508 0 : nsStyleUtil::IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
509 : bool aWhitespaceIsSignificant)
510 : {
511 0 : NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant,
512 : "Nonsensical arguments");
513 :
514 0 : bool isText = aChild->IsNodeOfType(nsINode::eTEXT);
515 :
516 0 : if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
517 0 : !aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
518 0 : return true;
519 : }
520 :
521 0 : return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
522 : (aWhitespaceIsSignificant ||
523 0 : !aChild->TextIsOnlyWhitespace());
524 : }
525 :
|