Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1189)

Unified Diff: Source/core/rendering/RenderBlock.cpp

Issue 672953002: Convert first letter into a pseudo element. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/rendering/RenderBlock.h ('k') | Source/core/rendering/RenderBoxModelObject.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/rendering/RenderBlock.cpp
diff --git a/Source/core/rendering/RenderBlock.cpp b/Source/core/rendering/RenderBlock.cpp
index e82750028083e7581ca10fe2cdc5ea1e6ef624eb..da3ccc61fe7fcd13b13af7c589254fa7a916c07e 100644
--- a/Source/core/rendering/RenderBlock.cpp
+++ b/Source/core/rendering/RenderBlock.cpp
@@ -1381,9 +1381,6 @@ void RenderBlock::layout()
{
OverflowEventDispatcher dispatcher(this);
- // Update our first letter info now.
- updateFirstLetter();
-
// Table cells call layoutBlock directly, so don't add any logic here. Put code into
// layoutBlock().
layoutBlock(false);
@@ -2983,8 +2980,6 @@ void RenderBlock::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
- updateFirstLetter();
-
m_minPreferredLogicalWidth = 0;
m_maxPreferredLogicalWidth = 0;
@@ -3342,258 +3337,6 @@ RenderBlock* RenderBlock::firstLineBlock() const
return firstLineBlock;
}
-static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer)
-{
- RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle());
- // Force inline display (except for floating first-letters).
- pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE);
- // CSS2 says first-letter can't be positioned.
- pseudoStyle->setPosition(StaticPosition);
- return pseudoStyle;
-}
-
-// 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)
-{
- CharCategory charCategory = category(c);
- return charCategory == Punctuation_Open
- || charCategory == Punctuation_Close
- || charCategory == Punctuation_InitialQuote
- || charCategory == Punctuation_FinalQuote
- || charCategory == Punctuation_Other;
-}
-
-static inline bool isSpaceForFirstLetter(UChar c)
-{
- return isSpaceOrNewline(c) || c == noBreakSpace;
-}
-
-static inline RenderObject* findFirstLetterBlock(RenderBlock* start)
-{
- RenderObject* firstLetterBlock = start;
- while (true) {
- bool canHaveFirstLetterRenderer = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER)
- && firstLetterBlock->canHaveGeneratedChildren()
- && isRenderBlockFlowOrRenderButton(firstLetterBlock);
- if (canHaveFirstLetterRenderer)
- return firstLetterBlock;
-
- RenderObject* parentBlock = firstLetterBlock->parent();
- if (firstLetterBlock->isReplaced() || !parentBlock
- || !isRenderBlockFlowOrRenderButton(parentBlock)) {
- return 0;
- }
- ASSERT(parentBlock->isRenderBlock());
- if (toRenderBlock(parentBlock)->firstChild() != firstLetterBlock)
- return 0;
- firstLetterBlock = parentBlock;
- }
-
- return 0;
-}
-
-void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderObject* currentChild)
-{
- RenderObject* firstLetter = currentChild->parent();
- RenderObject* firstLetterContainer = firstLetter->parent();
- RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
- ASSERT(firstLetter->isFloating() || firstLetter->isInline());
-
- if (RenderStyle::stylePropagationDiff(firstLetter->style(), pseudoStyle) == Reattach) {
- // The first-letter renderer needs to be replaced. Create a new renderer of the right type.
- RenderBoxModelObject* newFirstLetter;
- if (pseudoStyle->display() == INLINE)
- newFirstLetter = RenderInline::createAnonymous(&document());
- else
- newFirstLetter = RenderBlockFlow::createAnonymous(&document());
- newFirstLetter->setStyle(pseudoStyle);
-
- // Move the first letter into the new renderer.
- while (RenderObject* child = firstLetter->slowFirstChild()) {
- if (child->isText())
- toRenderText(child)->removeAndDestroyTextBoxes();
- firstLetter->removeChild(child);
- newFirstLetter->addChild(child, 0);
- }
-
- RenderObject* nextSibling = firstLetter->nextSibling();
- if (RenderTextFragment* remainingText = toRenderBoxModelObject(firstLetter)->firstLetterRemainingText()) {
- ASSERT(remainingText->isAnonymous() || remainingText->node()->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->virtualChildren()->removeChildNode(firstLetterContainer, firstLetter);
- firstLetter->destroy();
- firstLetter = newFirstLetter;
- firstLetterContainer->addChild(firstLetter, nextSibling);
- } else {
- firstLetter->setStyle(pseudoStyle);
- }
-
- for (RenderObject* genChild = firstLetter->slowFirstChild(); genChild; genChild = genChild->nextSibling()) {
- if (genChild->isText())
- genChild->setStyle(pseudoStyle);
- }
-}
-
-static inline unsigned firstLetterLength(const String& text)
-{
- unsigned length = 0;
- unsigned textLength = text.length();
-
- if (textLength == 0)
- return 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]) || 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;
- }
- return length;
-}
-
-void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, RenderText& currentChild, unsigned length)
-{
- ASSERT(length);
-
- RenderObject* firstLetterContainer = currentChild.parent();
- RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer);
- RenderBoxModelObject* firstLetter = 0;
- if (pseudoStyle->display() == INLINE)
- firstLetter = RenderInline::createAnonymous(&document());
- else
- firstLetter = RenderBlockFlow::createAnonymous(&document());
- firstLetter->setStyle(pseudoStyle);
-
- // FIXME: The first letter code should not modify the render tree during
- // layout. crbug.com/370458
- DeprecatedDisableModifyRenderTreeStructureAsserts disabler;
-
- firstLetterContainer->addChild(firstLetter, &currentChild);
-
- // 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 = currentChild.originalText();
- ASSERT(oldText.impl());
-
- // Construct a text fragment for the text after the first letter.
- // This text fragment might be empty.
- RenderTextFragment* remainingText =
- new RenderTextFragment(currentChild.node() ? currentChild.node() : &currentChild.document(), oldText.impl(), length, oldText.length() - length);
- remainingText->setStyle(currentChild.style());
- if (remainingText->node())
- remainingText->node()->setRenderer(remainingText);
-
- firstLetterContainer->addChild(remainingText, &currentChild);
- firstLetterContainer->removeChild(&currentChild);
- remainingText->setFirstLetter(firstLetter);
- firstLetter->setFirstLetterRemainingText(remainingText);
-
- // construct text fragment for the first letter
- RenderTextFragment* letter =
- new RenderTextFragment(remainingText->node() ? remainingText->node() : &remainingText->document(), oldText.impl(), 0, length);
- letter->setStyle(pseudoStyle);
- firstLetter->addChild(letter);
-
- currentChild.destroy();
-}
-
-// Once we see any of these renderers we can stop looking for first-letter as
-// they signal the end of the first line of text.
-bool RenderBlock::isInvalidFirstLetterRenderer(RenderObject* obj) const
-{
- return (obj->isBR() || (obj->isText() && toRenderText(obj)->isWordBreak()));
-}
-
-void RenderBlock::updateFirstLetter()
-{
- if (!document().styleEngine()->usesFirstLetterRules())
- return;
- // 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;
-
- // Drill into inlines looking for our first text child.
- RenderObject* currChild = firstLetterBlock->slowFirstChild();
- unsigned length = 0;
- 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 || isInvalidFirstLetterRenderer(currChild))
- break;
- currChild = currChild->nextSibling();
- } else if (currChild->isListMarker()) {
- currChild = currChild->nextSibling();
- } else if (currChild->isFloatingOrOutOfFlowPositioned()) {
- if (currChild->style()->styleType() == FIRST_LETTER) {
- currChild = currChild->slowFirstChild();
- break;
- }
- currChild = currChild->nextSibling();
- } else if (currChild->isReplaced() || currChild->isRenderButton() || currChild->isMenuList()) {
- break;
- } else if (currChild->isFlexibleBoxIncludingDeprecated() || currChild->isRenderGrid()) {
- return;
- } else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild->canHaveGeneratedChildren()) {
- // We found a lower-level node with first-letter, which supersedes the higher-level style
- firstLetterBlock = currChild;
- currChild = currChild->slowFirstChild();
- } else {
- currChild = currChild->slowFirstChild();
- }
- }
-
- if (!currChild)
- 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);
- return;
- }
-
- // FIXME: This black-list of disallowed RenderText subclasses is fragile.
- // Should counter be on this list? What about RenderTextFragment?
- if (!currChild->isText() || isInvalidFirstLetterRenderer(currChild))
- return;
-
- createFirstLetterRenderer(firstLetterBlock, toRenderText(*currChild), length);
-}
-
// Helper methods for obtaining the last line, computing line counts and heights for line counts
// (crawling into blocks).
static bool shouldCheckLines(RenderObject* obj)
« no previous file with comments | « Source/core/rendering/RenderBlock.h ('k') | Source/core/rendering/RenderBoxModelObject.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698