Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / resolver / FontBuilder.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
4  * Copyright (C) 2013 Google Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #include "config.h"
24 #include "core/css/resolver/FontBuilder.h"
25
26 #include "core/css/CSSCalculationValue.h"
27 #include "core/css/CSSToLengthConversionData.h"
28 #include "core/css/FontSize.h"
29 #include "core/frame/LocalFrame.h"
30 #include "core/frame/Settings.h"
31 #include "core/rendering/RenderTheme.h"
32 #include "core/rendering/RenderView.h"
33 #include "core/rendering/TextAutosizer.h"
34 #include "platform/fonts/FontDescription.h"
35 #include "platform/text/LocaleToScriptMapping.h"
36
37 namespace blink {
38
39 // FIXME: This scoping class is a short-term fix to minimize the changes in
40 // Font-constructing logic.
41 class FontDescriptionChangeScope {
42     STACK_ALLOCATED();
43 public:
44     FontDescriptionChangeScope(FontBuilder* fontBuilder)
45         : m_fontBuilder(fontBuilder)
46         , m_fontDescription(fontBuilder->m_style->fontDescription())
47     {
48     }
49
50     void reset() { m_fontDescription = FontDescription(); }
51     void set(const FontDescription& fontDescription) { m_fontDescription = fontDescription; }
52     FontDescription& fontDescription() { return m_fontDescription; }
53
54     ~FontDescriptionChangeScope()
55     {
56         m_fontBuilder->didChangeFontParameters(m_fontBuilder->m_style->setFontDescription(m_fontDescription));
57     }
58
59 private:
60     RawPtrWillBeMember<FontBuilder> m_fontBuilder;
61     FontDescription m_fontDescription;
62 };
63
64 FontBuilder::FontBuilder()
65     : m_document(nullptr)
66     , m_fontSizehasViewportUnits(false)
67     , m_style(0)
68     , m_fontDirty(false)
69 {
70 }
71
72 void FontBuilder::initForStyleResolve(const Document& document, RenderStyle* style)
73 {
74     ASSERT(document.frame());
75     m_document = &document;
76     m_style = style;
77     m_fontDirty = false;
78 }
79
80 inline static void setFontFamilyToStandard(FontDescription& fontDescription, const Document* document)
81 {
82     if (!document || !document->settings())
83         return;
84
85     fontDescription.setGenericFamily(FontDescription::StandardFamily);
86     const AtomicString& standardFontFamily = document->settings()->genericFontFamilySettings().standard();
87     if (standardFontFamily.isEmpty())
88         return;
89
90     fontDescription.firstFamily().setFamily(standardFontFamily);
91     // FIXME: Why is this needed here?
92     fontDescription.firstFamily().appendFamily(nullptr);
93 }
94
95 void FontBuilder::setInitial(float effectiveZoom)
96 {
97     ASSERT(m_document && m_document->settings());
98     if (!m_document || !m_document->settings())
99         return;
100
101     FontDescriptionChangeScope scope(this);
102
103     scope.reset();
104     setFontFamilyToStandard(scope.fontDescription(), m_document);
105     scope.fontDescription().setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
106     setSize(scope.fontDescription(), effectiveZoom, FontSize::fontSizeForKeyword(m_document, CSSValueMedium, NonFixedPitchFont));
107 }
108
109 void FontBuilder::inheritFrom(const FontDescription& fontDescription)
110 {
111     FontDescriptionChangeScope scope(this);
112
113     scope.set(fontDescription);
114 }
115
116 void FontBuilder::didChangeFontParameters(bool changed)
117 {
118     m_fontDirty |= changed;
119 }
120
121 void FontBuilder::fromSystemFont(CSSValueID valueId, float effectiveZoom)
122 {
123     FontDescriptionChangeScope scope(this);
124
125     FontDescription fontDescription;
126     RenderTheme::theme().systemFont(valueId, fontDescription);
127
128     // Double-check and see if the theme did anything. If not, don't bother updating the font.
129     if (!fontDescription.isAbsoluteSize())
130         return;
131
132     // Make sure the rendering mode and printer font settings are updated.
133     const Settings* settings = m_document->settings();
134     ASSERT(settings); // If we're doing style resolution, this document should always be in a frame and thus have settings
135     if (!settings)
136         return;
137
138     // Handle the zoom factor.
139     fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, effectiveZoom, fontDescription.specifiedSize()));
140     scope.set(fontDescription);
141 }
142
143 void FontBuilder::setFontFamilyInitial()
144 {
145     FontDescriptionChangeScope scope(this);
146
147     setFontFamilyToStandard(scope.fontDescription(), m_document);
148 }
149
150 void FontBuilder::setFontFamilyInherit(const FontDescription& parentFontDescription)
151 {
152     FontDescriptionChangeScope scope(this);
153
154     scope.fontDescription().setGenericFamily(parentFontDescription.genericFamily());
155     scope.fontDescription().setFamily(parentFontDescription.family());
156 }
157
158 // FIXME: I am not convinced FontBuilder needs to know anything about CSSValues.
159 void FontBuilder::setFontFamilyValue(CSSValue* value)
160 {
161     FontDescriptionChangeScope scope(this);
162
163     if (!value->isValueList())
164         return;
165
166     FontFamily& firstFamily = scope.fontDescription().firstFamily();
167     FontFamily* currFamily = 0;
168
169     // Before mapping in a new font-family property, we should reset the generic family.
170     FixedPitchFontType oldFixedPitchFontType = scope.fontDescription().fixedPitchFontType();
171     scope.fontDescription().setGenericFamily(FontDescription::NoFamily);
172
173     for (CSSValueListIterator i = value; i.hasMore(); i.advance()) {
174         CSSValue* item = i.value();
175         if (!item->isPrimitiveValue())
176             continue;
177         CSSPrimitiveValue* contentValue = toCSSPrimitiveValue(item);
178         AtomicString face;
179         Settings* settings = m_document->settings();
180         if (contentValue->isString()) {
181             face = AtomicString(contentValue->getStringValue());
182         } else if (settings) {
183             switch (contentValue->getValueID()) {
184             case CSSValueWebkitBody:
185                 face = settings->genericFontFamilySettings().standard();
186                 break;
187             case CSSValueSerif:
188                 face = FontFamilyNames::webkit_serif;
189                 scope.fontDescription().setGenericFamily(FontDescription::SerifFamily);
190                 break;
191             case CSSValueSansSerif:
192                 face = FontFamilyNames::webkit_sans_serif;
193                 scope.fontDescription().setGenericFamily(FontDescription::SansSerifFamily);
194                 break;
195             case CSSValueCursive:
196                 face = FontFamilyNames::webkit_cursive;
197                 scope.fontDescription().setGenericFamily(FontDescription::CursiveFamily);
198                 break;
199             case CSSValueFantasy:
200                 face = FontFamilyNames::webkit_fantasy;
201                 scope.fontDescription().setGenericFamily(FontDescription::FantasyFamily);
202                 break;
203             case CSSValueMonospace:
204                 face = FontFamilyNames::webkit_monospace;
205                 scope.fontDescription().setGenericFamily(FontDescription::MonospaceFamily);
206                 break;
207             case CSSValueWebkitPictograph:
208                 face = FontFamilyNames::webkit_pictograph;
209                 scope.fontDescription().setGenericFamily(FontDescription::PictographFamily);
210                 break;
211             default:
212                 break;
213             }
214         }
215
216         if (!face.isEmpty()) {
217             if (!currFamily) {
218                 // Filling in the first family.
219                 firstFamily.setFamily(face);
220                 firstFamily.appendFamily(nullptr); // Remove any inherited family-fallback list.
221                 currFamily = &firstFamily;
222             } else {
223                 RefPtr<SharedFontFamily> newFamily = SharedFontFamily::create();
224                 newFamily->setFamily(face);
225                 currFamily->appendFamily(newFamily);
226                 currFamily = newFamily.get();
227             }
228         }
229     }
230
231     // We can't call useFixedDefaultSize() until all new font families have been added
232     // If currFamily is non-zero then we set at least one family on this description.
233     if (!currFamily)
234         return;
235
236     if (scope.fontDescription().keywordSize() && scope.fontDescription().fixedPitchFontType() != oldFixedPitchFontType) {
237         scope.fontDescription().setSpecifiedSize(FontSize::fontSizeForKeyword(m_document,
238         static_cast<CSSValueID>(CSSValueXxSmall + scope.fontDescription().keywordSize() - 1), scope.fontDescription().fixedPitchFontType()));
239     }
240 }
241
242 void FontBuilder::setFontSizeInitial()
243 {
244     FontDescriptionChangeScope scope(this);
245
246     float size = FontSize::fontSizeForKeyword(m_document, CSSValueMedium, scope.fontDescription().fixedPitchFontType());
247
248     if (size < 0)
249         return;
250
251     scope.fontDescription().setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
252     scope.fontDescription().setSpecifiedSize(size);
253 }
254
255 void FontBuilder::setFontSizeInherit(const FontDescription& parentFontDescription)
256 {
257     FontDescriptionChangeScope scope(this);
258
259     float size = parentFontDescription.specifiedSize();
260
261     if (size < 0)
262         return;
263
264     scope.fontDescription().setKeywordSize(parentFontDescription.keywordSize());
265     scope.fontDescription().setSpecifiedSize(size);
266 }
267
268 // FIXME: Figure out where we fall in the size ranges (xx-small to xxx-large)
269 // and scale down/up to the next size level.
270 static float largerFontSize(float size)
271 {
272     return size * 1.2f;
273 }
274
275 static float smallerFontSize(float size)
276 {
277     return size / 1.2f;
278 }
279
280 // FIXME: Have to pass RenderStyles here for calc/computed values. This shouldn't be neecessary.
281 void FontBuilder::setFontSizeValue(CSSValue* value, RenderStyle* parentStyle, const RenderStyle* rootElementStyle)
282 {
283     if (!value->isPrimitiveValue())
284         return;
285
286     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
287
288     FontDescriptionChangeScope scope(this);
289
290     scope.fontDescription().setKeywordSize(0);
291     float parentSize = 0;
292     bool parentIsAbsoluteSize = false;
293     float size = 0;
294
295     // FIXME: Find out when parentStyle could be 0?
296     if (parentStyle) {
297         parentSize = parentStyle->fontDescription().specifiedSize();
298         parentIsAbsoluteSize = parentStyle->fontDescription().isAbsoluteSize();
299     }
300
301     if (CSSValueID valueID = primitiveValue->getValueID()) {
302         switch (valueID) {
303         case CSSValueXxSmall:
304         case CSSValueXSmall:
305         case CSSValueSmall:
306         case CSSValueMedium:
307         case CSSValueLarge:
308         case CSSValueXLarge:
309         case CSSValueXxLarge:
310         case CSSValueWebkitXxxLarge:
311             size = FontSize::fontSizeForKeyword(m_document, valueID, scope.fontDescription().fixedPitchFontType());
312             scope.fontDescription().setKeywordSize(valueID - CSSValueXxSmall + 1);
313             break;
314         case CSSValueLarger:
315             size = largerFontSize(parentSize);
316             break;
317         case CSSValueSmaller:
318             size = smallerFontSize(parentSize);
319             break;
320         default:
321             return;
322         }
323
324         scope.fontDescription().setIsAbsoluteSize(parentIsAbsoluteSize && (valueID == CSSValueLarger || valueID == CSSValueSmaller));
325     } else {
326         scope.fontDescription().setIsAbsoluteSize(parentIsAbsoluteSize || !(primitiveValue->isPercentage() || primitiveValue->isFontRelativeLength()));
327         if (primitiveValue->isPercentage()) {
328             size = (primitiveValue->getFloatValue() * parentSize) / 100.0f;
329         } else {
330             // If we have viewport units the conversion will mark the parent style as having viewport units.
331             bool parentHasViewportUnits = parentStyle->hasViewportUnits();
332             parentStyle->setHasViewportUnits(false);
333             CSSToLengthConversionData conversionData(parentStyle, rootElementStyle, m_document->renderView(), 1.0f, true);
334             if (primitiveValue->isLength())
335                 size = primitiveValue->computeLength<float>(conversionData);
336             else if (primitiveValue->isCalculatedPercentageWithLength())
337                 size = primitiveValue->cssCalcValue()->toCalcValue(conversionData)->evaluate(parentSize);
338             else
339                 ASSERT_NOT_REACHED();
340             m_fontSizehasViewportUnits = parentStyle->hasViewportUnits();
341             parentStyle->setHasViewportUnits(parentHasViewportUnits);
342         }
343     }
344
345     if (size < 0)
346         return;
347
348     // Overly large font sizes will cause crashes on some platforms (such as Windows).
349     // Cap font size here to make sure that doesn't happen.
350     size = std::min(maximumAllowedFontSize, size);
351
352
353     scope.fontDescription().setSpecifiedSize(size);
354 }
355
356 void FontBuilder::setWeight(FontWeight fontWeight)
357 {
358     FontDescriptionChangeScope scope(this);
359
360     scope.fontDescription().setWeight(fontWeight);
361 }
362
363 void FontBuilder::setStretch(FontStretch fontStretch)
364 {
365     FontDescriptionChangeScope scope(this);
366
367     scope.fontDescription().setStretch(fontStretch);
368 }
369
370 void FontBuilder::setScript(const String& locale)
371 {
372     FontDescriptionChangeScope scope(this);
373
374     scope.fontDescription().setLocale(locale);
375     scope.fontDescription().setScript(localeToScriptCodeForFontSelection(locale));
376 }
377
378 void FontBuilder::setStyle(FontStyle italic)
379 {
380     FontDescriptionChangeScope scope(this);
381
382     scope.fontDescription().setStyle(italic);
383 }
384
385 void FontBuilder::setVariant(FontVariant smallCaps)
386 {
387     FontDescriptionChangeScope scope(this);
388
389     scope.fontDescription().setVariant(smallCaps);
390 }
391
392 void FontBuilder::setVariantLigatures(const FontDescription::VariantLigatures& ligatures)
393 {
394     FontDescriptionChangeScope scope(this);
395
396     scope.fontDescription().setVariantLigatures(ligatures);
397 }
398
399 void FontBuilder::setTextRendering(TextRenderingMode textRenderingMode)
400 {
401     FontDescriptionChangeScope scope(this);
402
403     scope.fontDescription().setTextRendering(textRenderingMode);
404 }
405
406 void FontBuilder::setKerning(FontDescription::Kerning kerning)
407 {
408     FontDescriptionChangeScope scope(this);
409
410     scope.fontDescription().setKerning(kerning);
411 }
412
413 void FontBuilder::setFontSmoothing(FontSmoothingMode foontSmoothingMode)
414 {
415     FontDescriptionChangeScope scope(this);
416
417     scope.fontDescription().setFontSmoothing(foontSmoothingMode);
418 }
419
420 void FontBuilder::setFeatureSettings(PassRefPtr<FontFeatureSettings> settings)
421 {
422     FontDescriptionChangeScope scope(this);
423
424     scope.fontDescription().setFeatureSettings(settings);
425 }
426
427 void FontBuilder::setSize(FontDescription& fontDescription, float effectiveZoom, float size)
428 {
429     fontDescription.setSpecifiedSize(size);
430     fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, effectiveZoom, size));
431 }
432
433 float FontBuilder::getComputedSizeFromSpecifiedSize(FontDescription& fontDescription, float effectiveZoom, float specifiedSize)
434 {
435     float zoomFactor = effectiveZoom;
436     // FIXME: Why is this here!!!!?!
437     if (LocalFrame* frame = m_document->frame())
438         zoomFactor *= frame->textZoomFactor();
439
440     return FontSize::getComputedSizeFromSpecifiedSize(m_document, zoomFactor, fontDescription.isAbsoluteSize(), specifiedSize);
441 }
442
443 static void getFontAndGlyphOrientation(const RenderStyle* style, FontOrientation& fontOrientation, NonCJKGlyphOrientation& glyphOrientation)
444 {
445     if (style->isHorizontalWritingMode()) {
446         fontOrientation = Horizontal;
447         glyphOrientation = NonCJKGlyphOrientationVerticalRight;
448         return;
449     }
450
451     switch (style->textOrientation()) {
452     case TextOrientationVerticalRight:
453         fontOrientation = Vertical;
454         glyphOrientation = NonCJKGlyphOrientationVerticalRight;
455         return;
456     case TextOrientationUpright:
457         fontOrientation = Vertical;
458         glyphOrientation = NonCJKGlyphOrientationUpright;
459         return;
460     case TextOrientationSideways:
461         if (style->writingMode() == LeftToRightWritingMode) {
462             // FIXME: This should map to sideways-left, which is not supported yet.
463             fontOrientation = Vertical;
464             glyphOrientation = NonCJKGlyphOrientationVerticalRight;
465             return;
466         }
467         fontOrientation = Horizontal;
468         glyphOrientation = NonCJKGlyphOrientationVerticalRight;
469         return;
470     case TextOrientationSidewaysRight:
471         fontOrientation = Horizontal;
472         glyphOrientation = NonCJKGlyphOrientationVerticalRight;
473         return;
474     default:
475         ASSERT_NOT_REACHED();
476         fontOrientation = Horizontal;
477         glyphOrientation = NonCJKGlyphOrientationVerticalRight;
478         return;
479     }
480 }
481
482 void FontBuilder::checkForOrientationChange(RenderStyle* style)
483 {
484     FontOrientation fontOrientation;
485     NonCJKGlyphOrientation glyphOrientation;
486     getFontAndGlyphOrientation(style, fontOrientation, glyphOrientation);
487
488     FontDescriptionChangeScope scope(this);
489
490     if (scope.fontDescription().orientation() == fontOrientation && scope.fontDescription().nonCJKGlyphOrientation() == glyphOrientation)
491         return;
492
493     scope.fontDescription().setNonCJKGlyphOrientation(glyphOrientation);
494     scope.fontDescription().setOrientation(fontOrientation);
495 }
496
497 void FontBuilder::checkForGenericFamilyChange(RenderStyle* style, const RenderStyle* parentStyle)
498 {
499     FontDescriptionChangeScope scope(this);
500
501     if (scope.fontDescription().isAbsoluteSize() || !parentStyle)
502         return;
503
504     const FontDescription& parentFontDescription = parentStyle->fontDescription();
505     if (scope.fontDescription().fixedPitchFontType() == parentFontDescription.fixedPitchFontType())
506         return;
507
508     // For now, lump all families but monospace together.
509     if (scope.fontDescription().genericFamily() != FontDescription::MonospaceFamily
510         && parentFontDescription.genericFamily() != FontDescription::MonospaceFamily)
511         return;
512
513     // We know the parent is monospace or the child is monospace, and that font
514     // size was unspecified. We want to scale our font size as appropriate.
515     // If the font uses a keyword size, then we refetch from the table rather than
516     // multiplying by our scale factor.
517     float size;
518     if (scope.fontDescription().keywordSize()) {
519         size = FontSize::fontSizeForKeyword(m_document, static_cast<CSSValueID>(CSSValueXxSmall + scope.fontDescription().keywordSize() - 1), scope.fontDescription().fixedPitchFontType());
520     } else {
521         Settings* settings = m_document->settings();
522         float fixedScaleFactor = (settings && settings->defaultFixedFontSize() && settings->defaultFontSize())
523             ? static_cast<float>(settings->defaultFixedFontSize()) / settings->defaultFontSize()
524             : 1;
525         size = parentFontDescription.fixedPitchFontType() == FixedPitchFont ?
526             scope.fontDescription().specifiedSize() / fixedScaleFactor :
527             scope.fontDescription().specifiedSize() * fixedScaleFactor;
528     }
529
530     setSize(scope.fontDescription(), style->effectiveZoom(), size);
531 }
532
533 void FontBuilder::updateComputedSize(RenderStyle* style, const RenderStyle* parentStyle)
534 {
535     FontDescriptionChangeScope scope(this);
536
537     float computedSize = getComputedSizeFromSpecifiedSize(scope.fontDescription(), style->effectiveZoom(), scope.fontDescription().specifiedSize());
538     float multiplier = style->textAutosizingMultiplier();
539     if (multiplier > 1)
540         computedSize = TextAutosizer::computeAutosizedFontSize(computedSize, multiplier);
541
542     scope.fontDescription().setComputedSize(computedSize);
543 }
544
545 // FIXME: style param should come first
546 void FontBuilder::createFont(PassRefPtrWillBeRawPtr<FontSelector> fontSelector, const RenderStyle* parentStyle, RenderStyle* style)
547 {
548     if (!m_fontDirty)
549         return;
550
551     updateComputedSize(style, parentStyle);
552     checkForGenericFamilyChange(style, parentStyle);
553     checkForOrientationChange(style);
554     style->font().update(fontSelector);
555     m_fontDirty = false;
556 }
557
558 void FontBuilder::createFontForDocument(PassRefPtrWillBeRawPtr<FontSelector> fontSelector, RenderStyle* documentStyle)
559 {
560     FontDescription fontDescription = FontDescription();
561     fontDescription.setScript(localeToScriptCodeForFontSelection(documentStyle->locale()));
562
563     setFontFamilyToStandard(fontDescription, m_document);
564     fontDescription.setKeywordSize(CSSValueMedium - CSSValueXxSmall + 1);
565     int size = FontSize::fontSizeForKeyword(m_document, CSSValueMedium, NonFixedPitchFont);
566     fontDescription.setSpecifiedSize(size);
567     fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, documentStyle->effectiveZoom(), size));
568
569     FontOrientation fontOrientation;
570     NonCJKGlyphOrientation glyphOrientation;
571     getFontAndGlyphOrientation(documentStyle, fontOrientation, glyphOrientation);
572     fontDescription.setOrientation(fontOrientation);
573     fontDescription.setNonCJKGlyphOrientation(glyphOrientation);
574     documentStyle->setFontDescription(fontDescription);
575     documentStyle->font().update(fontSelector);
576 }
577
578 }