Index: Source/core/rendering/RenderBlock.cpp |
diff --git a/Source/core/rendering/RenderBlock.cpp b/Source/core/rendering/RenderBlock.cpp |
index a23bfbb8af6da719c0329019d101c1b0f6ab86d1..9c0e78214aac58c70c50b07203ce34298acc2bd1 100644 |
--- a/Source/core/rendering/RenderBlock.cpp |
+++ b/Source/core/rendering/RenderBlock.cpp |
@@ -4,6 +4,7 @@ |
* (C) 2007 David Smith (catfish.man@gmail.com) |
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. |
* Copyright (C) Research In Motion Limited 2010. All rights reserved. |
+ * Copyright (C) 2014 Samsung Electronics. 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 |
@@ -4106,40 +4107,6 @@ void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderO |
} |
} |
-static inline unsigned firstLetterLength(const String& text) |
-{ |
- unsigned length = 0; |
- unsigned textLength = text.length(); |
- |
- // Account for leading spaces first. |
- while (length < textLength && isSpaceForFirstLetter(text[length])) |
- length++; |
- |
- // Now account for leading punctuation. |
- while (length < textLength && isPunctuationForFirstLetter(text[length])) |
- length++; |
- |
- // Bail if we didn't find a letter before the end of the text or before a space. |
- if (isSpaceForFirstLetter(text[length]) || (textLength && length == textLength)) |
- return 0; |
- |
- // Account the next character for first letter. |
- length++; |
- |
- // Keep looking allowed punctuation for the :first-letter. |
- for (unsigned scanLength = length; scanLength < textLength; ++scanLength) { |
- UChar c = text[scanLength]; |
- |
- if (!isPunctuationForFirstLetter(c)) |
- break; |
- |
- length = scanLength + 1; |
- } |
- |
- // FIXME: If textLength is 0, length may still be 1! |
- return length; |
-} |
- |
void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, RenderObject* currentChild, unsigned length) |
{ |
ASSERT(length && currentChild->isText()); |
@@ -4184,69 +4151,196 @@ void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, Rend |
textObj->destroy(); |
} |
-void RenderBlock::updateFirstLetter() |
+static String getRendererTextForFirstLetter(RenderObject* renderer) |
eseidel
2014/03/26 15:31:52
We don't normally use "get" for getters in Blink s
|
{ |
- if (!document().styleEngine()->usesFirstLetterRules()) |
- return; |
- // Don't recur |
- if (style()->styleType() == FIRST_LETTER) |
- return; |
+ ASSERT(renderer->isText()); |
- // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find |
- // an efficient way to check for that situation though before implementing anything. |
- RenderObject* firstLetterBlock = findFirstLetterBlock(this); |
- if (!firstLetterBlock) |
- return; |
+ RenderText* textRenderer = toRenderText(renderer); |
+ String result = textRenderer->originalText(); |
- // Drill into inlines looking for our first text child. |
- RenderObject* currChild = firstLetterBlock->firstChild(); |
+ // BR and Word breaks are not allowed to be part of the first-letter |
+ // pseudo element, so we don't fallback to text() in those case. |
+ if (!result && !textRenderer->isBR() && !textRenderer->isWordBreak()) |
+ result = textRenderer->text(); |
+ |
+ return result; |
+} |
+ |
+// This helper function fills the vector of RenderObjects with the ones that would be considered |
+// as part of the first-letter pseudo element and returns the number of characters in the last |
+// renderer found that should be considered as part of such a pseudo element (the other renderers |
+// in the Vector will be considered "in full"). Also, this function might update the firstLetterBlock |
+// pointer if a lower-level node with first-letter style is found while traversing the subtree under |
+// the RenderObject originally passed through that parameter. |
+static unsigned findTextRenderersForFirstLetterBlock(RenderObject*& firstLetterBlock, Vector<RenderObject*>& renderers) |
+{ |
+ String text; |
+ unsigned textLength = 0; |
unsigned length = 0; |
+ unsigned previousLength = 0; |
+ bool leadingSpacesChecked = false; |
+ bool leadingPunctuationChecked = false; |
+ bool firstLetterRenderersFound = false; |
+ |
+ // Drill into inlines looking for the render objects to be transformed into first-letter pseudoelements. |
+ RenderObject* currChild = firstLetterBlock->firstChild(); |
while (currChild) { |
if (currChild->isText()) { |
- // FIXME: If there is leading punctuation in a different RenderText than |
- // the first letter, we'll not apply the correct style to it. |
- length = firstLetterLength(toRenderText(currChild)->originalText()); |
- if (length) |
+ text = getRendererTextForFirstLetter(currChild); |
eseidel
2014/03/26 15:31:22
Please break this block out into its own function.
|
+ textLength = text.length(); |
+ previousLength = length; |
+ length = 0; |
+ |
+ // Early checks in case we encounter the wrong characters in the wrong place. |
+ if ((leadingSpacesChecked && isSpaceForFirstLetter(text[length])) |
+ || (firstLetterRenderersFound && !isPunctuationForFirstLetter(text[length]))) |
break; |
- currChild = currChild->nextSibling(); |
+ |
+ if (!leadingSpacesChecked && length < textLength) { |
+ while (length < textLength && isSpaceForFirstLetter(text[length])) |
+ length++; |
+ |
+ // We now we finished checking if there are still more characters. |
+ if (length < textLength) |
+ leadingSpacesChecked = true; |
+ } |
+ |
+ // If leading spaces have been checked, account now for leading punctuation. |
+ if (!leadingPunctuationChecked && length < textLength) { |
+ while (length < textLength && isPunctuationForFirstLetter(text[length])) |
+ length++; |
+ |
+ // We now we finished checking if there are still more characters. |
+ if (length < textLength) |
+ leadingPunctuationChecked = true; |
+ } |
+ |
+ // Now account for the next character for first letter and the trailing punctuation (if any). |
+ if (!firstLetterRenderersFound && length < textLength) { |
+ // Now spaces are allowed between leading punctuation and the letter. |
+ if (isSpaceForFirstLetter(text[length])) |
+ break; |
+ |
+ firstLetterRenderersFound = true; |
+ length++; |
+ } |
+ |
+ // Keep looking allowed punctuation for the :first-letter. |
+ for (unsigned scanLength = length; scanLength < textLength; ++scanLength) { |
+ UChar c = text[scanLength]; |
+ if (!isPunctuationForFirstLetter(c)) |
+ break; |
+ |
+ length = scanLength + 1; |
+ } |
+ |
+ // If we haven't found anything with the current renderer, it does not make sense to keep |
+ // checking more renderers, as they won't be part of the first-letter pseudoelement anyway. |
+ if (!length) |
+ break; |
+ |
+ // Save the renderer so we can apply the right style to it later. |
+ renderers.append(currChild); |
+ |
+ // No need to keep looking if we already made a decision. |
+ if (length < textLength) |
+ break; |
+ |
+ // We need to look the next object traversing the tree in preorder as if the current renderer |
+ // was a leaf node (which probably is anyway) but without leaving the scope of the parent block. |
+ currChild = currChild->nextInPreOrderAfterChildren(firstLetterBlock); |
} else if (currChild->isListMarker()) { |
currChild = currChild->nextSibling(); |
} else if (currChild->isFloatingOrOutOfFlowPositioned()) { |
if (currChild->style()->styleType() == FIRST_LETTER) { |
currChild = currChild->firstChild(); |
+ if (currChild) { |
+ // If found a floating/out-of-flow element with the first_letter |
+ // style already applied, it means it has been previously identified |
+ // and so we should discard whatever we found so far and use that. |
+ firstLetterRenderersFound = true; |
+ renderers.append(currChild); |
+ } |
break; |
} |
currChild = currChild->nextSibling(); |
} else if (currChild->isReplaced() || currChild->isRenderButton() || currChild->isMenuList()) |
break; |
else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild->canHaveGeneratedChildren()) { |
- // We found a lower-level node with first-letter, which supersedes the higher-level style |
+ // We found a lower-level node with first-letter, which supersedes the higher-level style. |
firstLetterBlock = currChild; |
currChild = currChild->firstChild(); |
} else |
currChild = currChild->firstChild(); |
} |
- if (!currChild) |
+ if (!firstLetterRenderersFound) { |
+ // Empty the list of renderers if we did not find a correct set of them |
+ // to further generate new elements to apply the first-letter style over. |
+ renderers.clear(); |
+ } else if (!length) { |
+ // If we found renderers for the first-letter and length is zero it means that the last valid |
+ // renderer was added in the previous iteration of the loop, so we consider its length instead. |
+ length = previousLength; |
+ } |
+ |
+ // Return the number of characters used for the first-letter pseudo element coming |
+ // from the last text renderer found, needed to create the last generated renderer. |
+ return renderers.isEmpty() ? 0 : length; |
+} |
+ |
+void RenderBlock::updateFirstLetter() |
+{ |
+ if (!document().styleEngine()->usesFirstLetterRules()) |
return; |
- // If the child already has style, then it has already been created, so we just want |
- // to update it. |
- if (currChild->parent()->style()->styleType() == FIRST_LETTER) { |
- updateFirstLetterStyle(firstLetterBlock, currChild); |
+ // Don't recur |
+ if (style()->styleType() == FIRST_LETTER) |
+ return; |
+ |
+ // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find |
+ // an efficient way to check for that situation though before implementing anything. |
+ RenderObject* firstLetterBlock = findFirstLetterBlock(this); |
+ if (!firstLetterBlock) |
return; |
- } |
- // FIXME: This black-list of disallowed RenderText subclasses is fragile. |
- // Should counter be on this list? What about RenderTextFragment? |
- if (!currChild->isText() || currChild->isBR() || toRenderText(currChild)->isWordBreak()) |
+ // Find the renderers to apply the first-letter style over. |
+ Vector<RenderObject*> renderers; |
+ unsigned lengthForLastRenderer = findTextRenderersForFirstLetterBlock(firstLetterBlock, renderers); |
+ if (renderers.isEmpty()) |
return; |
- // Our layout state is not valid for the repaints we are going to trigger by |
- // adding and removing children of firstLetterContainer. |
- LayoutStateDisabler layoutStateDisabler(*this); |
+ // Create the new renderers for the first-letter pseudo elements. |
+ for (Vector<RenderObject*>::const_iterator it = renderers.begin(); it != renderers.end(); ++it) { |
+ RenderObject* currentRenderer = *it; |
+ ASSERT(currentRenderer->isText()); |
- createFirstLetterRenderer(firstLetterBlock, currChild, length); |
+ // If the child already has style, then it has already been created, so we just want |
+ // to update it. |
+ if (currentRenderer->parent()->style()->styleType() == FIRST_LETTER) { |
+ updateFirstLetterStyle(firstLetterBlock, currentRenderer); |
+ continue; |
+ } |
+ |
+ // FIXME: This black-list of disallowed RenderText subclasses is fragile. |
+ // Should counter be on this list? What about RenderTextFragment? |
+ RenderText* textRenderer = toRenderText(currentRenderer); |
+ if (!currentRenderer->isText() || currentRenderer->isBR() || textRenderer->isWordBreak()) |
+ continue; |
+ |
+ // Our layout state is not valid for the repaints we are going to trigger by |
+ // adding and removing children of firstLetterContainer. |
+ LayoutStateDisabler layoutStateDisabler(*this); |
+ |
+ unsigned lengthForRenderer = 0; |
+ if (currentRenderer == renderers.last()) |
+ lengthForRenderer = lengthForLastRenderer; |
+ else |
+ lengthForRenderer = getRendererTextForFirstLetter(currentRenderer).length(); |
+ |
+ if (lengthForRenderer) |
+ createFirstLetterRenderer(firstLetterBlock, currentRenderer, lengthForRenderer); |
+ } |
} |
// Helper methods for obtaining the last line, computing line counts and heights for line counts |