Chromium Code Reviews| Index: Source/core/dom/PseudoElement.cpp |
| diff --git a/Source/core/dom/PseudoElement.cpp b/Source/core/dom/PseudoElement.cpp |
| index 9e3d981ea39d84921a1f192a096b052e1c2636c4..1adffb84261a049b490bb21c444cf27613d2c302 100644 |
| --- a/Source/core/dom/PseudoElement.cpp |
| +++ b/Source/core/dom/PseudoElement.cpp |
| @@ -27,9 +27,11 @@ |
| #include "config.h" |
| #include "core/dom/PseudoElement.h" |
| +#include "core/dom/FirstLetterHelper.h" |
| #include "core/inspector/InspectorInstrumentation.h" |
| #include "core/rendering/RenderObject.h" |
| #include "core/rendering/RenderQuote.h" |
| +#include "core/rendering/RenderTextFragment.h" |
| #include "core/rendering/style/ContentData.h" |
| namespace blink { |
| @@ -49,6 +51,10 @@ const QualifiedName& pseudoElementTagName(PseudoId pseudoId) |
| DEFINE_STATIC_LOCAL(QualifiedName, backdrop, (nullAtom, "<pseudo:backdrop>", nullAtom)); |
| return backdrop; |
| } |
| + case FIRST_LETTER: { |
| + DEFINE_STATIC_LOCAL(QualifiedName, firstLetter, (nullAtom, "<pseudo:first-letter>", nullAtom)); |
| + return firstLetter; |
| + } |
| default: { |
| ASSERT_NOT_REACHED(); |
| } |
| @@ -74,6 +80,7 @@ String PseudoElement::pseudoElementNameForEvents(PseudoId pseudoId) |
| PseudoElement::PseudoElement(Element* parent, PseudoId pseudoId) |
| : Element(pseudoElementTagName(pseudoId), &parent->document(), CreateElement) |
| , m_pseudoId(pseudoId) |
| + , m_remainingTextRenderer(nullptr) |
| { |
| ASSERT(pseudoId != NOPSEUDO); |
| setParentOrShadowHostNode(parent); |
| @@ -94,6 +101,12 @@ void PseudoElement::dispose() |
| ASSERT(!nextSibling()); |
| ASSERT(!previousSibling()); |
| + if (m_remainingTextRenderer && m_remainingTextRenderer->node()) { |
| + Text* textNode = toText(m_remainingTextRenderer->node()); |
| + m_remainingTextRenderer->setText(textNode->dataImpl(), true); |
| + } |
|
Julien - ping for review
2014/10/01 21:14:47
Why do we need this code as we are destroying m_re
dsinclair
2014/10/04 02:01:34
We aren't destroying m_remainingTextRenderer. We
|
| + m_remainingTextRenderer = nullptr; |
| + |
| detach(); |
| RefPtrWillBeRawPtr<Element> parent = parentOrShadowHostElement(); |
| setParentOrShadowHostNode(0); |
| @@ -109,6 +122,14 @@ void PseudoElement::attach(const AttachContext& context) |
| RenderObject* renderer = this->renderer(); |
| if (!renderer) |
| return; |
| + |
| + // For a first letter renderer, we remove any current first letter children |
| + // and attach new renderers. |
| + if (isFirstLetterPseudoElement()) { |
| + attachFirstLetterTextRenderers(); |
| + return; |
| + } |
| + |
| RenderStyle* style = renderer->style(); |
| if (style->styleType() != BEFORE && style->styleType() != AFTER) |
| return; |
| @@ -125,6 +146,72 @@ void PseudoElement::attach(const AttachContext& context) |
| } |
| } |
| +RenderStyle* PseudoElement::styleForFirstLetter(RenderObject* rendererContainer) |
| +{ |
| + if (!rendererContainer) |
| + return nullptr; |
| + |
| + RenderObject* styleContainer = parentOrShadowHostElement()->renderer(); |
| + if (!styleContainer) |
| + return nullptr; |
| + |
| + // We always force the pseudo style to recompute as the first-letter style |
| + // computed by the style container may not have taken the renderers styles |
| + // into account. |
| + styleContainer->style()->removeCachedPseudoStyle(FIRST_LETTER); |
| + |
| + RenderStyle* pseudoStyle = styleContainer->getCachedPseudoStyle(FIRST_LETTER, rendererContainer->firstLineStyle()); |
| + ASSERT(pseudoStyle); |
| + |
| + // 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; |
| +} |
| + |
| +void PseudoElement::attachFirstLetterTextRenderers() |
| +{ |
| + ASSERT(m_pseudoId == FIRST_LETTER); |
| + |
| + RenderObject* nextRenderer = FirstLetterHelper::firstLetterTextRenderer(*this); |
| + ASSERT(nextRenderer && nextRenderer->isText()); |
| + |
| + // 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 = toRenderText(nextRenderer)->isTextFragment() ? toRenderTextFragment(nextRenderer)->completeText() : toRenderText(nextRenderer)->originalText(); |
| + ASSERT(oldText.impl()); |
| + |
| + RenderStyle* pseudoStyle = styleForFirstLetter(nextRenderer->parent()); |
| + renderer()->setStyle(pseudoStyle); |
| + |
| + // FIXME: This would already have been calculated in firstLetterRenderer. Can we pass the length through? |
| + unsigned length = FirstLetterHelper::firstLetterLength(oldText); |
| + |
| + // Construct a text fragment for the text after the first letter. |
| + // This text fragment might be empty. |
| + RenderTextFragment* remainingText = |
| + new RenderTextFragment(nextRenderer->node() ? nextRenderer->node() : &nextRenderer->document(), oldText.impl(), length, oldText.length() - length); |
| + remainingText->setStyle(nextRenderer->style()); |
| + if (remainingText->node()) |
| + remainingText->node()->setRenderer(remainingText); |
| + m_remainingTextRenderer = remainingText; |
| + |
| + RenderObject* nextSibling = renderer()->nextSibling(); |
| + renderer()->parent()->addChild(remainingText, nextSibling); |
| + |
| + // Construct text fragment for the first letter. |
| + RenderTextFragment* letter = new RenderTextFragment(this, oldText.impl(), 0, length); |
| + letter->setStyle(pseudoStyle); |
| + renderer()->addChild(letter); |
| + |
| + nextRenderer->destroy(); |
| + nextRenderer = remainingText; |
| +} |
| + |
| bool PseudoElement::rendererIsNeeded(const RenderStyle& style) |
| { |
| return pseudoElementRendererIsNeeded(&style); |
| @@ -139,12 +226,16 @@ void PseudoElement::didRecalcStyle(StyleRecalcChange) |
| // the style propagated downward manually similar to RenderObject::propagateStyleToAnonymousChildren. |
| RenderObject* renderer = this->renderer(); |
| for (RenderObject* child = renderer->nextInPreOrder(renderer); child; child = child->nextInPreOrder(renderer)) { |
| - // We only manage the style for the generated content items. |
| - if (!child->isText() && !child->isQuote() && !child->isImage()) |
| + // We need to re-calculate the correct style for the first letter element |
| + // and then apply that to the container and the text fragment inside. |
| + if (child->style()->styleType() == FIRST_LETTER && m_remainingTextRenderer) { |
| + if (RenderStyle* pseudoStyle = styleForFirstLetter(m_remainingTextRenderer->parent())) |
| + child->setPseudoStyle(pseudoStyle); |
| continue; |
| + } |
| - // The style for the RenderTextFragment for first letter is managed by an enclosing block, not by us. |
| - if (child->style()->styleType() == FIRST_LETTER) |
| + // We only manage the style for the generated content items. |
| + if (!child->isText() && !child->isQuote() && !child->isImage()) |
| continue; |
| child->setPseudoStyle(renderer->style()); |