Index: Source/core/dom/PseudoElement.cpp |
diff --git a/Source/core/dom/PseudoElement.cpp b/Source/core/dom/PseudoElement.cpp |
index 9e3d981ea39d84921a1f192a096b052e1c2636c4..d38519fd056ce17bef60f7854f954104cd65c696 100644 |
--- a/Source/core/dom/PseudoElement.cpp |
+++ b/Source/core/dom/PseudoElement.cpp |
@@ -30,6 +30,7 @@ |
#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 +50,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 +79,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 +100,13 @@ void PseudoElement::dispose() |
ASSERT(!nextSibling()); |
ASSERT(!previousSibling()); |
+ if (m_remainingTextRenderer && m_remainingTextRenderer->node()) { |
+ ASSERT(m_remainingTextRenderer->node()->isTextNode()); |
esprehn
2014/09/30 09:00:30
toText() already asserts this, and RenderText can
dsinclair
2014/09/30 21:46:33
Done.
|
+ Text* textNode = toText(m_remainingTextRenderer->node()); |
+ m_remainingTextRenderer->setText(textNode->dataImpl(), true); |
+ } |
+ m_remainingTextRenderer = nullptr; |
+ |
detach(); |
RefPtrWillBeRawPtr<Element> parent = parentOrShadowHostElement(); |
setParentOrShadowHostNode(0); |
@@ -109,6 +122,15 @@ 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()) { |
+ removeChildren(); |
esprehn
2014/09/30 09:00:30
This doesn't make sense, PseudoElement never has r
dsinclair
2014/09/30 21:46:34
Done.
|
+ this->attachFirstLetterTextRenderers(); |
esprehn
2014/09/30 09:00:30
no this->
dsinclair
2014/09/30 21:46:34
Done.
|
+ return; |
+ } |
+ |
RenderStyle* style = renderer->style(); |
if (style->styleType() != BEFORE && style->styleType() != AFTER) |
return; |
@@ -125,6 +147,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 = firstLetterTextRenderer(); |
+ 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()); |
+ this->renderer()->setStyle(pseudoStyle); |
esprehn
2014/09/30 09:00:30
I don't think you need this-> anywhere in here.
dsinclair
2014/09/30 21:46:34
Done.
|
+ |
+ // FIXME: This would already have been calculated in firstLetterRenderer. Can we pass the length through? |
+ unsigned length = 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 = this->renderer()->nextSibling(); |
+ this->renderer()->parent()->addChild(remainingText, nextSibling); |
esprehn
2014/09/30 09:00:30
Woah this is scary, did you write this stuff or di
dsinclair
2014/09/30 21:46:34
I did, the previous version already had the firstL
|
+ |
+ // Construct text fragment for the first letter. |
+ RenderTextFragment* letter = new RenderTextFragment(this, oldText.impl(), 0, length); |
+ letter->setStyle(pseudoStyle); |
+ this->renderer()->addChild(letter); |
+ |
+ nextRenderer->destroy(); |
+ nextRenderer = remainingText; |
+} |
+ |
bool PseudoElement::rendererIsNeeded(const RenderStyle& style) |
{ |
return pseudoElementRendererIsNeeded(&style); |
@@ -139,16 +227,31 @@ 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())) { |
+ renderer->setStyle(pseudoStyle); |
esprehn
2014/09/30 09:00:30
Setting the style on yourself doesn't make sense,
dsinclair
2014/09/30 21:46:34
Done.
|
+ 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()); |
} |
} |
+// A pseudoElement isn't, strictly, in the dom tree, it hands off the parent |
+// node. So, we need to have the parent node retrieve its first letter text |
+// renderer. |
+RenderObject* PseudoElement::firstLetterTextRenderer() const |
+{ |
+ ASSERT(parentOrShadowHostElement()); |
+ return parentOrShadowHostElement()->firstLetterTextRenderer(); |
+} |
+ |
} // namespace |