Move first-letter renderer mutation code out of RenderBlock and into RenderTreeUpdater
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Aug 2017 13:15:20 +0000 (13:15 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Aug 2017 13:15:20 +0000 (13:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175627

Reviewed by Andreas Kling.

Render tree should not mutate itself. We already fixed this for first-letter, supporting code
can now move to RenderTreeUpdater too.

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
* rendering/RenderBlock.cpp:
(WebCore::styleForFirstLetter): Deleted.
(WebCore::isPunctuationForFirstLetter): Deleted.
(WebCore::shouldSkipForFirstLetter): Deleted.
(WebCore::RenderBlock::updateFirstLetterStyle): Deleted.
(WebCore::RenderBlock::createFirstLetterRenderer): Deleted.
(WebCore::RenderBlock::updateFirstLetter): Deleted.
* rendering/RenderBlock.h:
* rendering/RenderRubyRun.cpp:
(WebCore::RenderRubyRun::updateFirstLetter): Deleted.
* rendering/RenderRubyRun.h:
* rendering/RenderTable.cpp:
(WebCore::RenderTable::updateFirstLetter): Deleted.
* rendering/RenderTable.h:

    Virtual overrides just disabled first letter for some RenderBlock subclasses. This is now achieved via
    supportsFirstLetter test in the first letter updater.

* rendering/TextAutoSizing.cpp:
(WebCore::TextAutoSizingValue::adjustTextNodeSizes):
* rendering/svg/RenderSVGText.cpp:
(WebCore::RenderSVGText::updateFirstLetter): Deleted.
* rendering/svg/RenderSVGText.h:
* style/RenderTreeUpdater.cpp:
(WebCore::RenderTreeUpdater::popParent):
* style/RenderTreeUpdater.h:
* style/RenderTreeUpdaterFirstLetter.cpp: Added.
(WebCore::styleForFirstLetter):
(WebCore::isPunctuationForFirstLetter):
(WebCore::shouldSkipForFirstLetter):
(WebCore::updateFirstLetterStyle):
(WebCore::createFirstLetterRenderer):
(WebCore::supportsFirstLetter):
(WebCore::RenderTreeUpdater::FirstLetter::update):
* style/RenderTreeUpdaterFirstLetter.h: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@220795 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/rendering/RenderBlock.cpp
Source/WebCore/rendering/RenderBlock.h
Source/WebCore/rendering/RenderRubyRun.cpp
Source/WebCore/rendering/RenderRubyRun.h
Source/WebCore/rendering/RenderTable.cpp
Source/WebCore/rendering/RenderTable.h
Source/WebCore/rendering/TextAutoSizing.cpp
Source/WebCore/rendering/svg/RenderSVGText.cpp
Source/WebCore/rendering/svg/RenderSVGText.h
Source/WebCore/style/RenderTreeUpdater.cpp
Source/WebCore/style/RenderTreeUpdater.h
Source/WebCore/style/RenderTreeUpdaterFirstLetter.cpp [new file with mode: 0644]
Source/WebCore/style/RenderTreeUpdaterFirstLetter.h [new file with mode: 0644]

index d968180..067dd7d 100644 (file)
@@ -2796,6 +2796,7 @@ set(WebCore_SOURCES
     style/InlineTextBoxStyle.cpp
     style/RenderTreePosition.cpp
     style/RenderTreeUpdater.cpp
+    style/RenderTreeUpdaterFirstLetter.cpp
     style/StyleChange.cpp
     style/StyleFontSizeFunctions.cpp
     style/StyleInvalidator.cpp
index cb372f3..88c7a04 100644 (file)
@@ -1,3 +1,51 @@
+2017-08-16  Antti Koivisto  <antti@apple.com>
+
+        Move first-letter renderer mutation code out of RenderBlock and into RenderTreeUpdater
+        https://bugs.webkit.org/show_bug.cgi?id=175627
+
+        Reviewed by Andreas Kling.
+
+        Render tree should not mutate itself. We already fixed this for first-letter, supporting code
+        can now move to RenderTreeUpdater too.
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * rendering/RenderBlock.cpp:
+        (WebCore::styleForFirstLetter): Deleted.
+        (WebCore::isPunctuationForFirstLetter): Deleted.
+        (WebCore::shouldSkipForFirstLetter): Deleted.
+        (WebCore::RenderBlock::updateFirstLetterStyle): Deleted.
+        (WebCore::RenderBlock::createFirstLetterRenderer): Deleted.
+        (WebCore::RenderBlock::updateFirstLetter): Deleted.
+        * rendering/RenderBlock.h:
+        * rendering/RenderRubyRun.cpp:
+        (WebCore::RenderRubyRun::updateFirstLetter): Deleted.
+        * rendering/RenderRubyRun.h:
+        * rendering/RenderTable.cpp:
+        (WebCore::RenderTable::updateFirstLetter): Deleted.
+        * rendering/RenderTable.h:
+
+            Virtual overrides just disabled first letter for some RenderBlock subclasses. This is now achieved via
+            supportsFirstLetter test in the first letter updater.
+
+        * rendering/TextAutoSizing.cpp:
+        (WebCore::TextAutoSizingValue::adjustTextNodeSizes):
+        * rendering/svg/RenderSVGText.cpp:
+        (WebCore::RenderSVGText::updateFirstLetter): Deleted.
+        * rendering/svg/RenderSVGText.h:
+        * style/RenderTreeUpdater.cpp:
+        (WebCore::RenderTreeUpdater::popParent):
+        * style/RenderTreeUpdater.h:
+        * style/RenderTreeUpdaterFirstLetter.cpp: Added.
+        (WebCore::styleForFirstLetter):
+        (WebCore::isPunctuationForFirstLetter):
+        (WebCore::shouldSkipForFirstLetter):
+        (WebCore::updateFirstLetterStyle):
+        (WebCore::createFirstLetterRenderer):
+        (WebCore::supportsFirstLetter):
+        (WebCore::RenderTreeUpdater::FirstLetter::update):
+        * style/RenderTreeUpdaterFirstLetter.h: Added.
+
 2017-08-16  Xabier Rodriguez Calvar  <calvaris@igalia.com>
 
         [GStreamer][EME] Rework handling key systems and UUIDs
index 49bc5d0..af2aef0 100644 (file)
                E47E276516036ED200EE2AFB /* ExtensionStyleSheets.h in Headers */ = {isa = PBXBuildFile; fileRef = E47E276416036ED200EE2AFB /* ExtensionStyleSheets.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E47E276816036EDC00EE2AFB /* ExtensionStyleSheets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E47E276716036EDC00EE2AFB /* ExtensionStyleSheets.cpp */; };
                E48137B91DB3B526005C59BF /* StyleValidity.h in Headers */ = {isa = PBXBuildFile; fileRef = E48137B81DB3B526005C59BF /* StyleValidity.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E48284081F44594C00863AC3 /* RenderTreeUpdaterFirstLetter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E48284061F44594B00863AC3 /* RenderTreeUpdaterFirstLetter.cpp */; };
                E48944A2180B57D800F165D8 /* SimpleLineLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E48944A0180B57D800F165D8 /* SimpleLineLayout.cpp */; };
                E48944A3180B57D800F165D8 /* SimpleLineLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = E48944A1180B57D800F165D8 /* SimpleLineLayout.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E4916FF7195DF6A0005AB349 /* LayerFlushThrottleState.h in Headers */ = {isa = PBXBuildFile; fileRef = E4916FF6195DF6A0005AB349 /* LayerFlushThrottleState.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9B098BDE1F3D673D002DD562 /* JSDataTransferItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDataTransferItem.h; sourceTree = "<group>"; };
                9B098BDF1F3D673D002DD562 /* JSDataTransferItemList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDataTransferItemList.cpp; sourceTree = "<group>"; };
                9B098BE01F3D673D002DD562 /* JSDataTransferItemList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDataTransferItemList.h; sourceTree = "<group>"; };
-               9B098BE61F3D6AF6002DD562 /* JSStringCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSStringCallback.cpp; path = JSStringCallback.cpp; sourceTree = "<group>"; };
-               9B098BE71F3D6AF6002DD562 /* JSStringCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSStringCallback.h; path = JSStringCallback.h; sourceTree = "<group>"; };
+               9B098BE61F3D6AF6002DD562 /* JSStringCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSStringCallback.cpp; sourceTree = "<group>"; };
+               9B098BE71F3D6AF6002DD562 /* JSStringCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSStringCallback.h; sourceTree = "<group>"; };
                9B0FE8731D9E02DF004A8ACB /* DocumentOrShadowRoot.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DocumentOrShadowRoot.idl; sourceTree = "<group>"; };
                9B13257B1F3D2ABA00DAAB69 /* DataTransferItemList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DataTransferItemList.cpp; sourceTree = "<group>"; };
                9B19B67E1B964E5200348745 /* ShadowRoot.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ShadowRoot.idl; sourceTree = "<group>"; };
                E47E276416036ED200EE2AFB /* ExtensionStyleSheets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExtensionStyleSheets.h; sourceTree = "<group>"; };
                E47E276716036EDC00EE2AFB /* ExtensionStyleSheets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExtensionStyleSheets.cpp; sourceTree = "<group>"; };
                E48137B81DB3B526005C59BF /* StyleValidity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleValidity.h; sourceTree = "<group>"; };
+               E48284061F44594B00863AC3 /* RenderTreeUpdaterFirstLetter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = RenderTreeUpdaterFirstLetter.cpp; sourceTree = "<group>"; };
+               E48284091F44595600863AC3 /* RenderTreeUpdaterFirstLetter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RenderTreeUpdaterFirstLetter.h; sourceTree = "<group>"; };
                E48944A0180B57D800F165D8 /* SimpleLineLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleLineLayout.cpp; sourceTree = "<group>"; };
                E48944A1180B57D800F165D8 /* SimpleLineLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleLineLayout.h; sourceTree = "<group>"; };
                E4916FF6195DF6A0005AB349 /* LayerFlushThrottleState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LayerFlushThrottleState.h; sourceTree = "<group>"; };
                                5824ABA91AE849C8009074B7 /* RenderTreePosition.h */,
                                E461802A1C8A06D90026C02C /* RenderTreeUpdater.cpp */,
                                E46180281C8A06CD0026C02C /* RenderTreeUpdater.h */,
+                               E48284061F44594B00863AC3 /* RenderTreeUpdaterFirstLetter.cpp */,
+                               E48284091F44595600863AC3 /* RenderTreeUpdaterFirstLetter.h */,
                                E401E0A51C3C0CF700F34D10 /* StyleChange.cpp */,
                                E401E0A31C3C0B8300F34D10 /* StyleChange.h */,
                                E4D58EB617B4ED8900CBDCA8 /* StyleFontSizeFunctions.cpp */,
                                516F7F6D1C31E39A00F111DC /* ServerOpenDBRequest.h in Headers */,
                                2D93AEE319DF5641002A86C3 /* ServicesOverlayController.h in Headers */,
                                51F1755D1F3EBC8300C74950 /* ServiceWorker.h in Headers */,
-                               5182C23F1F313A090059BA7C /* ServiceWorker.h in Headers */,
                                51F1755F1F3EBC8300C74950 /* ServiceWorkerContainer.h in Headers */,
-                               5182C2411F313A090059BA7C /* ServiceWorkerContainer.h in Headers */,
                                51F175611F3EBC8300C74950 /* ServiceWorkerGlobalScope.h in Headers */,
-                               5182C2431F313A090059BA7C /* ServiceWorkerGlobalScope.h in Headers */,
                                51F175631F3EBC8300C74950 /* ServiceWorkerJob.h in Headers */,
-                               511CA67E1F3905A60019E074 /* ServiceWorkerJob.h in Headers */,
                                51F175641F3EBC8300C74950 /* ServiceWorkerJobClient.h in Headers */,
-                               511CA6801F39331F0019E074 /* ServiceWorkerJobClient.h in Headers */,
-                               511CA67A1F3904B10019E074 /* ServiceWorkerProvider.h in Headers */,
                                51F175661F3EBC8300C74950 /* ServiceWorkerProvider.h in Headers */,
                                51F175681F3EBC8300C74950 /* ServiceWorkerRegistration.h in Headers */,
-                               5182C2451F313A090059BA7C /* ServiceWorkerRegistration.h in Headers */,
                                51F175691F3EBC8300C74950 /* ServiceWorkerRegistrationOptions.h in Headers */,
                                51F1756B1F3EBC8300C74950 /* ServiceWorkerRegistrationParameters.h in Headers */,
                                51F1756C1F3EBC8300C74950 /* ServiceWorkerUpdateViaCache.h in Headers */,
-                               51F174FF1F35899700C74950 /* ServiceWorkerUpdateViaCache.h in Headers */,
                                756B2CE118B7101600FECFAA /* SessionID.h in Headers */,
                                93309E10099E64920056E581 /* SetNodeAttributeCommand.h in Headers */,
                                B8DBDB4C130B0F8A00F5CDB1 /* SetSelectionCommand.h in Headers */,
                                E125F83D182411E700D84CD9 /* JSCryptoOperationData.cpp in Sources */,
                                7C9ACABF1F3CF1AF00F3AA09 /* JSCryptoRsaHashedKeyAlgorithm.cpp in Sources */,
                                7C9ACAC11F3CF1AF00F3AA09 /* JSCryptoRsaKeyAlgorithm.cpp in Sources */,
+                               E48284081F44594C00863AC3 /* RenderTreeUpdaterFirstLetter.cpp in Sources */,
                                BC46C1FC0C0DDC8F0020CFC3 /* JSCSSFontFaceRule.cpp in Sources */,
                                BC46C1FE0C0DDC8F0020CFC3 /* JSCSSImportRule.cpp in Sources */,
                                316FE0710E6CCBEE00BF6088 /* JSCSSKeyframeRule.cpp in Sources */,
                                516F7F6E1C31E39C00F111DC /* ServerOpenDBRequest.cpp in Sources */,
                                2D93AEE419DF5641002A86C3 /* ServicesOverlayController.mm in Sources */,
                                51F1755C1F3EBC8300C74950 /* ServiceWorker.cpp in Sources */,
-                               5182C23E1F313A090059BA7C /* ServiceWorker.cpp in Sources */,
                                51F1755E1F3EBC8300C74950 /* ServiceWorkerContainer.cpp in Sources */,
-                               5182C2401F313A090059BA7C /* ServiceWorkerContainer.cpp in Sources */,
-                               5182C2421F313A090059BA7C /* ServiceWorkerGlobalScope.cpp in Sources */,
                                51F175601F3EBC8300C74950 /* ServiceWorkerGlobalScope.cpp in Sources */,
                                51F175621F3EBC8300C74950 /* ServiceWorkerJob.cpp in Sources */,
-                               511CA67D1F3905A60019E074 /* ServiceWorkerJob.cpp in Sources */,
-                               511CA6791F3904B10019E074 /* ServiceWorkerProvider.cpp in Sources */,
                                51F175651F3EBC8300C74950 /* ServiceWorkerProvider.cpp in Sources */,
                                51F175671F3EBC8300C74950 /* ServiceWorkerRegistration.cpp in Sources */,
-                               5182C2441F313A090059BA7C /* ServiceWorkerRegistration.cpp in Sources */,
                                51F1756A1F3EBC8300C74950 /* ServiceWorkerRegistrationParameters.cpp in Sources */,
-                               511CA6831F3A3CD90019E074 /* ServiceWorkerRegistrationParameters.cpp in Sources */,
                                511F7D441EB1C39100E47B83 /* SessionID.cpp in Sources */,
                                93309E0F099E64920056E581 /* SetNodeAttributeCommand.cpp in Sources */,
                                B8DBDB4B130B0F8A00F5CDB1 /* SetSelectionCommand.cpp in Sources */,
index b674014..e63b146 100644 (file)
@@ -3085,70 +3085,6 @@ RenderBlock* RenderBlock::firstLineBlock() const
     return firstLineBlock;
 }
 
-static RenderStyle styleForFirstLetter(const RenderElement& firstLetterBlock, const RenderObject& firstLetterContainer)
-{
-    auto* containerFirstLetterStyle = firstLetterBlock.getCachedPseudoStyle(FIRST_LETTER, &firstLetterContainer.firstLineStyle());
-    // FIXME: There appears to be some path where we have a first letter renderer without first letter style.
-    ASSERT(containerFirstLetterStyle);
-    auto firstLetterStyle = RenderStyle::clone(containerFirstLetterStyle ? *containerFirstLetterStyle : firstLetterContainer.firstLineStyle());
-
-    // If we have an initial letter drop that is >= 1, then we need to force floating to be on.
-    if (firstLetterStyle.initialLetterDrop() >= 1 && !firstLetterStyle.isFloating())
-        firstLetterStyle.setFloating(firstLetterStyle.isLeftToRightDirection() ? LeftFloat : RightFloat);
-
-    // We have to compute the correct font-size for the first-letter if it has an initial letter height set.
-    auto* paragraph = firstLetterContainer.isRenderBlockFlow() ? &firstLetterContainer : firstLetterContainer.containingBlock();
-    if (firstLetterStyle.initialLetterHeight() >= 1 && firstLetterStyle.fontMetrics().hasCapHeight() && paragraph->style().fontMetrics().hasCapHeight()) {
-        // FIXME: For ideographic baselines, we want to go from line edge to line edge. This is equivalent to (N-1)*line-height + the font height.
-        // We don't yet support ideographic baselines.
-        // For an N-line first-letter and for alphabetic baselines, the cap-height of the first letter needs to equal (N-1)*line-height of paragraph lines + cap-height of the paragraph
-        // Mathematically we can't rely on font-size, since font().height() doesn't necessarily match. For reliability, the best approach is simply to
-        // compare the final measured cap-heights of the two fonts in order to get to the closest possible value.
-        firstLetterStyle.setLineBoxContain(LineBoxContainInitialLetter);
-        int lineHeight = paragraph->style().computedLineHeight();
-        
-        // Set the font to be one line too big and then ratchet back to get to a precise fit. We can't just set the desired font size based off font height metrics
-        // because many fonts bake ascent into the font metrics. Therefore we have to look at actual measured cap height values in order to know when we have a good fit.
-        auto newFontDescription = firstLetterStyle.fontDescription();
-        float capRatio = firstLetterStyle.fontMetrics().floatCapHeight() / firstLetterStyle.computedFontPixelSize();
-        float startingFontSize = ((firstLetterStyle.initialLetterHeight() - 1) * lineHeight + paragraph->style().fontMetrics().capHeight()) / capRatio;
-        newFontDescription.setSpecifiedSize(startingFontSize);
-        newFontDescription.setComputedSize(startingFontSize);
-        firstLetterStyle.setFontDescription(newFontDescription);
-        firstLetterStyle.fontCascade().update(firstLetterStyle.fontCascade().fontSelector());
-        
-        int desiredCapHeight = (firstLetterStyle.initialLetterHeight() - 1) * lineHeight + paragraph->style().fontMetrics().capHeight();
-        int actualCapHeight = firstLetterStyle.fontMetrics().capHeight();
-        while (actualCapHeight > desiredCapHeight) {
-            auto newFontDescription = firstLetterStyle.fontDescription();
-            newFontDescription.setSpecifiedSize(newFontDescription.specifiedSize() - 1);
-            newFontDescription.setComputedSize(newFontDescription.computedSize() -1);
-            firstLetterStyle.setFontDescription(newFontDescription);
-            firstLetterStyle.fontCascade().update(firstLetterStyle.fontCascade().fontSelector());
-            actualCapHeight = firstLetterStyle.fontMetrics().capHeight();
-        }
-    }
-    
-    // Force inline display (except for floating first-letters).
-    firstLetterStyle.setDisplay(firstLetterStyle.isFloating() ? BLOCK : INLINE);
-    // CSS2 says first-letter can't be positioned.
-    firstLetterStyle.setPosition(StaticPosition);
-    return firstLetterStyle;
-}
-
-// CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter
-// "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe),
-// "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included"
-static inline bool isPunctuationForFirstLetter(UChar c)
-{
-    return U_GET_GC_MASK(c) & (U_GC_PS_MASK | U_GC_PE_MASK | U_GC_PI_MASK | U_GC_PF_MASK | U_GC_PO_MASK);
-}
-
-static inline bool shouldSkipForFirstLetter(UChar c)
-{
-    return isSpaceOrNewline(c) || c == noBreakSpace || isPunctuationForFirstLetter(c);
-}
-
 static inline RenderBlock* findFirstLetterBlock(RenderBlock* start)
 {
     RenderBlock* firstLetterBlock = start;
@@ -3169,117 +3105,6 @@ static inline RenderBlock* findFirstLetterBlock(RenderBlock* start)
     return nullptr;
 }
 
-void RenderBlock::updateFirstLetterStyle(RenderElement* firstLetterBlock, RenderObject* currentChild)
-{
-    RenderElement* firstLetter = currentChild->parent();
-    RenderElement* firstLetterContainer = firstLetter->parent();
-    auto pseudoStyle = styleForFirstLetter(*firstLetterBlock, *firstLetterContainer);
-    ASSERT(firstLetter->isFloating() || firstLetter->isInline());
-
-    if (Style::determineChange(firstLetter->style(), pseudoStyle) == Style::Detach) {
-        // The first-letter renderer needs to be replaced. Create a new renderer of the right type.
-        RenderBoxModelObject* newFirstLetter;
-        if (pseudoStyle.display() == INLINE)
-            newFirstLetter = new RenderInline(document(), WTFMove(pseudoStyle));
-        else
-            newFirstLetter = new RenderBlockFlow(document(), WTFMove(pseudoStyle));
-        newFirstLetter->initializeStyle();
-
-        // Move the first letter into the new renderer.
-        LayoutStateDisabler layoutStateDisabler(view());
-        while (RenderObject* child = firstLetter->firstChild()) {
-            if (is<RenderText>(*child))
-                downcast<RenderText>(*child).removeAndDestroyTextBoxes();
-            firstLetter->removeChild(*child);
-            newFirstLetter->addChild(child, nullptr);
-        }
-
-        RenderObject* nextSibling = firstLetter->nextSibling();
-        if (RenderTextFragment* remainingText = downcast<RenderBoxModelObject>(*firstLetter).firstLetterRemainingText()) {
-            ASSERT(remainingText->isAnonymous() || remainingText->textNode()->renderer() == remainingText);
-            // Replace the old renderer with the new one.
-            remainingText->setFirstLetter(*newFirstLetter);
-            newFirstLetter->setFirstLetterRemainingText(remainingText);
-        }
-        // To prevent removal of single anonymous block in RenderBlock::removeChild and causing
-        // |nextSibling| to go stale, we remove the old first letter using removeChildNode first.
-        firstLetterContainer->removeChildInternal(*firstLetter, NotifyChildren);
-        firstLetter->destroy();
-        firstLetter = newFirstLetter;
-        firstLetterContainer->addChild(firstLetter, nextSibling);
-    } else
-        firstLetter->setStyle(WTFMove(pseudoStyle));
-}
-
-void RenderBlock::createFirstLetterRenderer(RenderElement* firstLetterBlock, RenderText* currentTextChild)
-{
-    RenderElement* firstLetterContainer = currentTextChild->parent();
-    auto pseudoStyle = styleForFirstLetter(*firstLetterBlock, *firstLetterContainer);
-    RenderBoxModelObject* firstLetter = nullptr;
-    if (pseudoStyle.display() == INLINE)
-        firstLetter = new RenderInline(document(), WTFMove(pseudoStyle));
-    else
-        firstLetter = new RenderBlockFlow(document(), WTFMove(pseudoStyle));
-    firstLetter->initializeStyle();
-    firstLetterContainer->addChild(firstLetter, currentTextChild);
-
-    // The original string is going to be either a generated content string or a DOM node's
-    // string.  We want the original string before it got transformed in case first-letter has
-    // no text-transform or a different text-transform applied to it.
-    String oldText = currentTextChild->originalText();
-    ASSERT(!oldText.isNull());
-
-    if (!oldText.isEmpty()) {
-        unsigned length = 0;
-
-        // Account for leading spaces and punctuation.
-        while (length < oldText.length() && shouldSkipForFirstLetter(oldText[length]))
-            length++;
-
-        // Account for first grapheme cluster.
-        length += numCharactersInGraphemeClusters(StringView(oldText).substring(length), 1);
-        
-        // Keep looking for whitespace and allowed punctuation, but avoid
-        // accumulating just whitespace into the :first-letter.
-        for (unsigned scanLength = length; scanLength < oldText.length(); ++scanLength) {
-            UChar c = oldText[scanLength];
-            
-            if (!shouldSkipForFirstLetter(c))
-                break;
-
-            if (isPunctuationForFirstLetter(c))
-                length = scanLength + 1;
-         }
-         
-        // Construct a text fragment for the text after the first letter.
-        // This text fragment might be empty.
-        RenderTextFragment* remainingText;
-        if (currentTextChild->textNode())
-            remainingText = new RenderTextFragment(*currentTextChild->textNode(), oldText, length, oldText.length() - length);
-        else
-            remainingText = new RenderTextFragment(document(), oldText, length, oldText.length() - length);
-
-        if (remainingText->textNode())
-            remainingText->textNode()->setRenderer(remainingText);
-
-        firstLetterContainer->addChild(remainingText, currentTextChild);
-        firstLetterContainer->removeChild(*currentTextChild);
-        remainingText->setFirstLetter(*firstLetter);
-        firstLetter->setFirstLetterRemainingText(remainingText);
-        
-        // construct text fragment for the first letter
-        RenderTextFragment* letter;
-        if (remainingText->textNode())
-            letter = new RenderTextFragment(*remainingText->textNode(), oldText, 0, length);
-        else
-            letter = new RenderTextFragment(document(), oldText, 0, length);
-
-        firstLetter->addChild(letter);
-
-        currentTextChild->destroy();
-    }
-}
-    
 void RenderBlock::getFirstLetter(RenderObject*& firstLetter, RenderElement*& firstLetterContainer, RenderObject* skipObject)
 {
     firstLetter = nullptr;
@@ -3335,32 +3160,6 @@ void RenderBlock::getFirstLetter(RenderObject*& firstLetter, RenderElement*& fir
         firstLetterContainer = nullptr;
 }
 
-void RenderBlock::updateFirstLetter()
-{
-    ASSERT_WITH_SECURITY_IMPLICATION(!view().layoutState());
-
-    RenderObject* firstLetterObj;
-    RenderElement* firstLetterContainer;
-    // FIXME: The first letter might be composed of a variety of code units, and therefore might
-    // be contained within multiple RenderElements.
-    getFirstLetter(firstLetterObj, firstLetterContainer);
-
-    if (!firstLetterObj || !firstLetterContainer)
-        return;
-
-    // If the child already has style, then it has already been created, so we just want
-    // to update it.
-    if (firstLetterObj->parent()->style().styleType() == FIRST_LETTER) {
-        updateFirstLetterStyle(firstLetterContainer, firstLetterObj);
-        return;
-    }
-
-    if (!is<RenderText>(*firstLetterObj))
-        return;
-
-    createFirstLetterRenderer(firstLetterContainer, downcast<RenderText>(firstLetterObj));
-}
-
 RenderFlowThread* RenderBlock::cachedFlowThreadContainingBlock() const
 {
     RenderBlockRareData* rareData = getBlockRareData(*this);
index 5c401d3..b20fe7b 100644 (file)
@@ -263,7 +263,6 @@ public:
     LayoutUnit collapsedMarginBeforeForChild(const RenderBox& child) const;
     LayoutUnit collapsedMarginAfterForChild(const RenderBox& child) const;
 
-    virtual void updateFirstLetter();
     void getFirstLetter(RenderObject*& firstLetter, RenderElement*& firstLetterContainer, RenderObject* skipObject = nullptr);
 
     virtual void scrollbarsChanged(bool /*horizontalScrollbarChanged*/, bool /*verticalScrollbarChanged*/) { }
@@ -454,9 +453,6 @@ private:
     bool isSelfCollapsingBlock() const override;
     virtual bool childrenPreventSelfCollapsing() const;
     
-    void createFirstLetterRenderer(RenderElement* firstLetterBlock, RenderText* currentTextChild);
-    void updateFirstLetterStyle(RenderElement* firstLetterBlock, RenderObject* firstLetterContainer);
-
     Node* nodeForHitTest() const;
 
     // FIXME-BLOCKFLOW: Remove virtualizaion when all callers have moved to RenderBlockFlow
index be3980d..0ffb243 100644 (file)
@@ -101,10 +101,6 @@ RenderBlock* RenderRubyRun::firstLineBlock() const
     return 0;
 }
 
-void RenderRubyRun::updateFirstLetter()
-{
-}
-
 bool RenderRubyRun::isChildAllowed(const RenderObject& child, const RenderStyle&) const
 {
     return child.isInline() || child.isRubyText();
index 4019b28..de69e95 100644 (file)
@@ -60,7 +60,6 @@ public:
     void removeChild(RenderObject&) override;
 
     RenderBlock* firstLineBlock() const override;
-    void updateFirstLetter() override;
 
     void getOverhang(bool firstLine, RenderObject* startRenderer, RenderObject* endRenderer, float& startOverhang, float& endOverhang) const;
 
index 26345f4..a9531cc 100644 (file)
@@ -1485,10 +1485,6 @@ RenderBlock* RenderTable::firstLineBlock() const
     return nullptr;
 }
 
-void RenderTable::updateFirstLetter()
-{
-}
-
 int RenderTable::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
 {
     return valueOrCompute(firstLineBaseline(), [&] {
index 2079618..0a0d844 100644 (file)
@@ -302,7 +302,6 @@ private:
     void invalidateCachedColumnOffsets();
 
     RenderBlock* firstLineBlock() const final;
-    void updateFirstLetter() final;
     
     void updateLogicalWidth() final;
 
index 1aa8bd4..15e809e 100644 (file)
@@ -36,6 +36,7 @@
 #include "RenderListMarker.h"
 #include "RenderText.h"
 #include "RenderTextFragment.h"
+#include "RenderTreeUpdaterFirstLetter.h"
 #include "StyleResolver.h"
 
 namespace WebCore {
@@ -157,7 +158,6 @@ auto TextAutoSizingValue::adjustTextNodeSizes() -> StillHasNodes
         parentRenderer->setStyle(WTFMove(newParentStyle));
     }
 
-    // FIXME: All render tree mutations should be done via RenderTreeUpdater.
     for (auto& node : m_autoSizedNodes) {
         auto& textRenderer = *node->renderer();
         if (!is<RenderTextFragment>(textRenderer))
@@ -165,7 +165,8 @@ auto TextAutoSizingValue::adjustTextNodeSizes() -> StillHasNodes
         auto* block = downcast<RenderTextFragment>(textRenderer).blockForAccompanyingFirstLetter();
         if (!block)
             continue;
-        block->updateFirstLetter();
+        // FIXME: All render tree mutations should be done by RenderTreeUpdater commit.
+        RenderTreeUpdater::FirstLetter::update(*block);
     }
 
     return stillHasNodes;
index b2fc2e1..b7fbd99 100644 (file)
@@ -542,10 +542,4 @@ RenderBlock* RenderSVGText::firstLineBlock() const
     return 0;
 }
 
-// Fix for <rdar://problem/8048875>. We should not render :first-letter CSS Style
-// in a SVG text element context.
-void RenderSVGText::updateFirstLetter()
-{
-}
-
 }
index 1cf647f..2b6fabb 100644 (file)
@@ -91,7 +91,6 @@ private:
     std::unique_ptr<RootInlineBox> createRootInlineBox() override;
 
     RenderBlock* firstLineBlock() const override;
-    void updateFirstLetter() override;
 
     bool shouldHandleSubtreeMutations() const;
 
index 72ad520..e70f4a2 100644 (file)
@@ -41,6 +41,7 @@
 #include "RenderFullScreen.h"
 #include "RenderNamedFlowThread.h"
 #include "RenderQuote.h"
+#include "RenderTreeUpdaterFirstLetter.h"
 #include "StyleResolver.h"
 #include "StyleTreeResolver.h"
 #include <wtf/SystemTracing.h>
@@ -234,7 +235,7 @@ void RenderTreeUpdater::popParent()
 
         auto* renderer = parent.element->renderer();
         if (is<RenderBlock>(renderer))
-            downcast<RenderBlock>(*renderer).updateFirstLetter();
+            FirstLetter::update(downcast<RenderBlock>(*renderer));
 
         if (parent.element->hasCustomStyleResolveCallbacks() && parent.styleChange == Style::Detach && renderer)
             parent.element->didAttachRenderers();
index 7aaf900..f808f0f 100644 (file)
@@ -51,6 +51,8 @@ public:
     static void tearDownRenderers(Element&, TeardownType = TeardownType::Normal);
     static void tearDownRenderer(Text&);
 
+    class FirstLetter;
+
 private:
     void updateRenderTree(ContainerNode& root);
     void updateTextRenderer(Text&, const Style::TextUpdate*);
diff --git a/Source/WebCore/style/RenderTreeUpdaterFirstLetter.cpp b/Source/WebCore/style/RenderTreeUpdaterFirstLetter.cpp
new file mode 100644 (file)
index 0000000..9cfeb93
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ *           (C) 1999 Antti Koivisto (koivisto@kde.org)
+ *           (C) 2007 David Smith (catfish.man@gmail.com)
+ * Copyright (C) 2003-2011, 2017 Apple Inc. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "RenderTreeUpdaterFirstLetter.h"
+
+#include "FontCascade.h"
+#include "RenderBlock.h"
+#include "RenderInline.h"
+#include "RenderRubyRun.h"
+#include "RenderSVGText.h"
+#include "RenderStyle.h"
+#include "RenderTable.h"
+#include "RenderTextFragment.h"
+
+namespace WebCore {
+
+static RenderStyle styleForFirstLetter(const RenderElement& firstLetterBlock, const RenderObject& firstLetterContainer)
+{
+    auto* containerFirstLetterStyle = firstLetterBlock.getCachedPseudoStyle(FIRST_LETTER, &firstLetterContainer.firstLineStyle());
+    // FIXME: There appears to be some path where we have a first letter renderer without first letter style.
+    ASSERT(containerFirstLetterStyle);
+    auto firstLetterStyle = RenderStyle::clone(containerFirstLetterStyle ? *containerFirstLetterStyle : firstLetterContainer.firstLineStyle());
+
+    // If we have an initial letter drop that is >= 1, then we need to force floating to be on.
+    if (firstLetterStyle.initialLetterDrop() >= 1 && !firstLetterStyle.isFloating())
+        firstLetterStyle.setFloating(firstLetterStyle.isLeftToRightDirection() ? LeftFloat : RightFloat);
+
+    // We have to compute the correct font-size for the first-letter if it has an initial letter height set.
+    auto* paragraph = firstLetterContainer.isRenderBlockFlow() ? &firstLetterContainer : firstLetterContainer.containingBlock();
+    if (firstLetterStyle.initialLetterHeight() >= 1 && firstLetterStyle.fontMetrics().hasCapHeight() && paragraph->style().fontMetrics().hasCapHeight()) {
+        // FIXME: For ideographic baselines, we want to go from line edge to line edge. This is equivalent to (N-1)*line-height + the font height.
+        // We don't yet support ideographic baselines.
+        // For an N-line first-letter and for alphabetic baselines, the cap-height of the first letter needs to equal (N-1)*line-height of paragraph lines + cap-height of the paragraph
+        // Mathematically we can't rely on font-size, since font().height() doesn't necessarily match. For reliability, the best approach is simply to
+        // compare the final measured cap-heights of the two fonts in order to get to the closest possible value.
+        firstLetterStyle.setLineBoxContain(LineBoxContainInitialLetter);
+        int lineHeight = paragraph->style().computedLineHeight();
+
+        // Set the font to be one line too big and then ratchet back to get to a precise fit. We can't just set the desired font size based off font height metrics
+        // because many fonts bake ascent into the font metrics. Therefore we have to look at actual measured cap height values in order to know when we have a good fit.
+        auto newFontDescription = firstLetterStyle.fontDescription();
+        float capRatio = firstLetterStyle.fontMetrics().floatCapHeight() / firstLetterStyle.computedFontPixelSize();
+        float startingFontSize = ((firstLetterStyle.initialLetterHeight() - 1) * lineHeight + paragraph->style().fontMetrics().capHeight()) / capRatio;
+        newFontDescription.setSpecifiedSize(startingFontSize);
+        newFontDescription.setComputedSize(startingFontSize);
+        firstLetterStyle.setFontDescription(newFontDescription);
+        firstLetterStyle.fontCascade().update(firstLetterStyle.fontCascade().fontSelector());
+
+        int desiredCapHeight = (firstLetterStyle.initialLetterHeight() - 1) * lineHeight + paragraph->style().fontMetrics().capHeight();
+        int actualCapHeight = firstLetterStyle.fontMetrics().capHeight();
+        while (actualCapHeight > desiredCapHeight) {
+            auto newFontDescription = firstLetterStyle.fontDescription();
+            newFontDescription.setSpecifiedSize(newFontDescription.specifiedSize() - 1);
+            newFontDescription.setComputedSize(newFontDescription.computedSize() -1);
+            firstLetterStyle.setFontDescription(newFontDescription);
+            firstLetterStyle.fontCascade().update(firstLetterStyle.fontCascade().fontSelector());
+            actualCapHeight = firstLetterStyle.fontMetrics().capHeight();
+        }
+    }
+
+    // Force inline display (except for floating first-letters).
+    firstLetterStyle.setDisplay(firstLetterStyle.isFloating() ? BLOCK : INLINE);
+    // CSS2 says first-letter can't be positioned.
+    firstLetterStyle.setPosition(StaticPosition);
+    return firstLetterStyle;
+}
+
+// CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter
+// "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe),
+// "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included"
+static inline bool isPunctuationForFirstLetter(UChar c)
+{
+    return U_GET_GC_MASK(c) & (U_GC_PS_MASK | U_GC_PE_MASK | U_GC_PI_MASK | U_GC_PF_MASK | U_GC_PO_MASK);
+}
+
+static inline bool shouldSkipForFirstLetter(UChar c)
+{
+    return isSpaceOrNewline(c) || c == noBreakSpace || isPunctuationForFirstLetter(c);
+}
+
+static void updateFirstLetterStyle(RenderElement& firstLetterBlock, RenderObject& currentChild)
+{
+    RenderElement* firstLetter = currentChild.parent();
+    RenderElement* firstLetterContainer = firstLetter->parent();
+    auto pseudoStyle = styleForFirstLetter(firstLetterBlock, *firstLetterContainer);
+    ASSERT(firstLetter->isFloating() || firstLetter->isInline());
+
+    if (Style::determineChange(firstLetter->style(), pseudoStyle) == Style::Detach) {
+        // The first-letter renderer needs to be replaced. Create a new renderer of the right type.
+        RenderBoxModelObject* newFirstLetter;
+        if (pseudoStyle.display() == INLINE)
+            newFirstLetter = new RenderInline(firstLetterBlock.document(), WTFMove(pseudoStyle));
+        else
+            newFirstLetter = new RenderBlockFlow(firstLetterBlock.document(), WTFMove(pseudoStyle));
+        newFirstLetter->initializeStyle();
+
+        // Move the first letter into the new renderer.
+        LayoutStateDisabler layoutStateDisabler(firstLetterBlock.view());
+        while (RenderObject* child = firstLetter->firstChild()) {
+            if (is<RenderText>(*child))
+                downcast<RenderText>(*child).removeAndDestroyTextBoxes();
+            firstLetter->removeChild(*child);
+            newFirstLetter->addChild(child, nullptr);
+        }
+
+        RenderObject* nextSibling = firstLetter->nextSibling();
+        if (RenderTextFragment* remainingText = downcast<RenderBoxModelObject>(*firstLetter).firstLetterRemainingText()) {
+            ASSERT(remainingText->isAnonymous() || remainingText->textNode()->renderer() == remainingText);
+            // Replace the old renderer with the new one.
+            remainingText->setFirstLetter(*newFirstLetter);
+            newFirstLetter->setFirstLetterRemainingText(remainingText);
+        }
+        // To prevent removal of single anonymous block in RenderBlock::removeChild and causing
+        // |nextSibling| to go stale, we remove the old first letter using removeChildNode first.
+        firstLetterContainer->removeChildInternal(*firstLetter, RenderElement::NotifyChildren);
+        firstLetter->destroy();
+        firstLetter = newFirstLetter;
+        firstLetterContainer->addChild(firstLetter, nextSibling);
+    } else
+        firstLetter->setStyle(WTFMove(pseudoStyle));
+}
+
+static void createFirstLetterRenderer(RenderElement& firstLetterBlock, RenderText& currentTextChild)
+{
+    RenderElement* firstLetterContainer = currentTextChild.parent();
+    auto pseudoStyle = styleForFirstLetter(firstLetterBlock, *firstLetterContainer);
+    RenderBoxModelObject* firstLetter = nullptr;
+    if (pseudoStyle.display() == INLINE)
+        firstLetter = new RenderInline(firstLetterBlock.document(), WTFMove(pseudoStyle));
+    else
+        firstLetter = new RenderBlockFlow(firstLetterBlock.document(), WTFMove(pseudoStyle));
+    firstLetter->initializeStyle();
+    firstLetterContainer->addChild(firstLetter, &currentTextChild);
+
+    // The original string is going to be either a generated content string or a DOM node's
+    // string. We want the original string before it got transformed in case first-letter has
+    // no text-transform or a different text-transform applied to it.
+    String oldText = currentTextChild.originalText();
+    ASSERT(!oldText.isNull());
+
+    if (!oldText.isEmpty()) {
+        unsigned length = 0;
+
+        // Account for leading spaces and punctuation.
+        while (length < oldText.length() && shouldSkipForFirstLetter(oldText[length]))
+            length++;
+
+        // Account for first grapheme cluster.
+        length += numCharactersInGraphemeClusters(StringView(oldText).substring(length), 1);
+
+        // Keep looking for whitespace and allowed punctuation, but avoid
+        // accumulating just whitespace into the :first-letter.
+        for (unsigned scanLength = length; scanLength < oldText.length(); ++scanLength) {
+            UChar c = oldText[scanLength];
+
+            if (!shouldSkipForFirstLetter(c))
+                break;
+
+            if (isPunctuationForFirstLetter(c))
+                length = scanLength + 1;
+        }
+
+        // Construct a text fragment for the text after the first letter.
+        // This text fragment might be empty.
+        RenderTextFragment* remainingText;
+        if (currentTextChild.textNode())
+            remainingText = new RenderTextFragment(*currentTextChild.textNode(), oldText, length, oldText.length() - length);
+        else
+            remainingText = new RenderTextFragment(firstLetterBlock.document(), oldText, length, oldText.length() - length);
+
+        if (remainingText->textNode())
+            remainingText->textNode()->setRenderer(remainingText);
+
+        firstLetterContainer->addChild(remainingText, &currentTextChild);
+        firstLetterContainer->removeChild(currentTextChild);
+        remainingText->setFirstLetter(*firstLetter);
+        firstLetter->setFirstLetterRemainingText(remainingText);
+
+        // construct text fragment for the first letter
+        RenderTextFragment* letter;
+        if (remainingText->textNode())
+            letter = new RenderTextFragment(*remainingText->textNode(), oldText, 0, length);
+        else
+            letter = new RenderTextFragment(firstLetterBlock.document(), oldText, 0, length);
+
+        firstLetter->addChild(letter);
+
+        currentTextChild.destroy();
+    }
+}
+
+static bool supportsFirstLetter(RenderBlock& block)
+{
+    if (is<RenderTable>(block))
+        return false;
+    if (is<RenderSVGText>(block))
+        return false;
+    if (is<RenderRubyRun>(block))
+        return false;
+    return true;
+}
+
+void RenderTreeUpdater::FirstLetter::update(RenderBlock& block)
+{
+    ASSERT_WITH_SECURITY_IMPLICATION(!block.view().layoutState());
+
+    if (!supportsFirstLetter(block))
+        return;
+
+    RenderObject* firstLetterObj;
+    RenderElement* firstLetterContainer;
+    // FIXME: The first letter might be composed of a variety of code units, and therefore might
+    // be contained within multiple RenderElements.
+    block.getFirstLetter(firstLetterObj, firstLetterContainer);
+
+    if (!firstLetterObj || !firstLetterContainer)
+        return;
+
+    // If the child already has style, then it has already been created, so we just want
+    // to update it.
+    if (firstLetterObj->parent()->style().styleType() == FIRST_LETTER) {
+        updateFirstLetterStyle(*firstLetterContainer, *firstLetterObj);
+        return;
+    }
+
+    if (!is<RenderText>(*firstLetterObj))
+        return;
+
+    createFirstLetterRenderer(*firstLetterContainer, downcast<RenderText>(*firstLetterObj));
+}
+
+};
diff --git a/Source/WebCore/style/RenderTreeUpdaterFirstLetter.h b/Source/WebCore/style/RenderTreeUpdaterFirstLetter.h
new file mode 100644 (file)
index 0000000..c2b9eed
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "RenderTreeUpdater.h"
+
+namespace WebCore {
+
+class RenderBlock;
+
+class RenderTreeUpdater::FirstLetter {
+public:
+    static void update(RenderBlock&);
+};
+
+}