Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Neither the name of Google Inc. nor the names of its | 10 * * Neither the name of Google Inc. nor the names of its |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 */ | 25 */ |
| 26 | 26 |
| 27 #include "config.h" | 27 #include "config.h" |
| 28 #include "core/dom/PseudoElement.h" | 28 #include "core/dom/PseudoElement.h" |
| 29 | 29 |
| 30 #include "core/inspector/InspectorInstrumentation.h" | 30 #include "core/inspector/InspectorInstrumentation.h" |
| 31 #include "core/rendering/RenderObject.h" | 31 #include "core/rendering/RenderObject.h" |
| 32 #include "core/rendering/RenderQuote.h" | 32 #include "core/rendering/RenderQuote.h" |
| 33 #include "core/rendering/RenderTextFragment.h" | |
| 33 #include "core/rendering/style/ContentData.h" | 34 #include "core/rendering/style/ContentData.h" |
| 34 | 35 |
| 35 namespace blink { | 36 namespace blink { |
| 36 | 37 |
| 37 const QualifiedName& pseudoElementTagName(PseudoId pseudoId) | 38 const QualifiedName& pseudoElementTagName(PseudoId pseudoId) |
| 38 { | 39 { |
| 39 switch (pseudoId) { | 40 switch (pseudoId) { |
| 40 case AFTER: { | 41 case AFTER: { |
| 41 DEFINE_STATIC_LOCAL(QualifiedName, after, (nullAtom, "<pseudo:after>", n ullAtom)); | 42 DEFINE_STATIC_LOCAL(QualifiedName, after, (nullAtom, "<pseudo:after>", n ullAtom)); |
| 42 return after; | 43 return after; |
| 43 } | 44 } |
| 44 case BEFORE: { | 45 case BEFORE: { |
| 45 DEFINE_STATIC_LOCAL(QualifiedName, before, (nullAtom, "<pseudo:before>", nullAtom)); | 46 DEFINE_STATIC_LOCAL(QualifiedName, before, (nullAtom, "<pseudo:before>", nullAtom)); |
| 46 return before; | 47 return before; |
| 47 } | 48 } |
| 48 case BACKDROP: { | 49 case BACKDROP: { |
| 49 DEFINE_STATIC_LOCAL(QualifiedName, backdrop, (nullAtom, "<pseudo:backdro p>", nullAtom)); | 50 DEFINE_STATIC_LOCAL(QualifiedName, backdrop, (nullAtom, "<pseudo:backdro p>", nullAtom)); |
| 50 return backdrop; | 51 return backdrop; |
| 51 } | 52 } |
| 53 case FIRST_LETTER: { | |
| 54 DEFINE_STATIC_LOCAL(QualifiedName, firstLetter, (nullAtom, "<pseudo:firs t-letter>", nullAtom)); | |
| 55 return firstLetter; | |
| 56 } | |
| 52 default: { | 57 default: { |
| 53 ASSERT_NOT_REACHED(); | 58 ASSERT_NOT_REACHED(); |
| 54 } | 59 } |
| 55 } | 60 } |
| 56 DEFINE_STATIC_LOCAL(QualifiedName, name, (nullAtom, "<pseudo>", nullAtom)); | 61 DEFINE_STATIC_LOCAL(QualifiedName, name, (nullAtom, "<pseudo>", nullAtom)); |
| 57 return name; | 62 return name; |
| 58 } | 63 } |
| 59 | 64 |
| 60 String PseudoElement::pseudoElementNameForEvents(PseudoId pseudoId) | 65 String PseudoElement::pseudoElementNameForEvents(PseudoId pseudoId) |
| 61 { | 66 { |
| 62 DEFINE_STATIC_LOCAL(const String, after, ("::after")); | 67 DEFINE_STATIC_LOCAL(const String, after, ("::after")); |
| 63 DEFINE_STATIC_LOCAL(const String, before, ("::before")); | 68 DEFINE_STATIC_LOCAL(const String, before, ("::before")); |
| 64 switch (pseudoId) { | 69 switch (pseudoId) { |
| 65 case AFTER: | 70 case AFTER: |
| 66 return after; | 71 return after; |
| 67 case BEFORE: | 72 case BEFORE: |
| 68 return before; | 73 return before; |
| 69 default: | 74 default: |
| 70 return emptyString(); | 75 return emptyString(); |
| 71 } | 76 } |
| 72 } | 77 } |
| 73 | 78 |
| 74 PseudoElement::PseudoElement(Element* parent, PseudoId pseudoId) | 79 PseudoElement::PseudoElement(Element* parent, PseudoId pseudoId) |
| 75 : Element(pseudoElementTagName(pseudoId), &parent->document(), CreateElement ) | 80 : Element(pseudoElementTagName(pseudoId), &parent->document(), CreateElement ) |
| 76 , m_pseudoId(pseudoId) | 81 , m_pseudoId(pseudoId) |
| 82 , m_remainingTextRenderer(nullptr) | |
| 77 { | 83 { |
| 78 ASSERT(pseudoId != NOPSEUDO); | 84 ASSERT(pseudoId != NOPSEUDO); |
| 79 setParentOrShadowHostNode(parent); | 85 setParentOrShadowHostNode(parent); |
| 80 setHasCustomStyleCallbacks(); | 86 setHasCustomStyleCallbacks(); |
| 81 } | 87 } |
| 82 | 88 |
| 83 PassRefPtr<RenderStyle> PseudoElement::customStyleForRenderer() | 89 PassRefPtr<RenderStyle> PseudoElement::customStyleForRenderer() |
| 84 { | 90 { |
| 85 return parentOrShadowHostElement()->renderer()->getCachedPseudoStyle(m_pseud oId); | 91 return parentOrShadowHostElement()->renderer()->getCachedPseudoStyle(m_pseud oId); |
| 86 } | 92 } |
| 87 | 93 |
| 88 void PseudoElement::dispose() | 94 void PseudoElement::dispose() |
| 89 { | 95 { |
| 90 ASSERT(parentOrShadowHostElement()); | 96 ASSERT(parentOrShadowHostElement()); |
| 91 | 97 |
| 92 InspectorInstrumentation::pseudoElementDestroyed(this); | 98 InspectorInstrumentation::pseudoElementDestroyed(this); |
| 93 | 99 |
| 94 ASSERT(!nextSibling()); | 100 ASSERT(!nextSibling()); |
| 95 ASSERT(!previousSibling()); | 101 ASSERT(!previousSibling()); |
| 96 | 102 |
| 103 if (m_remainingTextRenderer && m_remainingTextRenderer->node()) { | |
| 104 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.
| |
| 105 Text* textNode = toText(m_remainingTextRenderer->node()); | |
| 106 m_remainingTextRenderer->setText(textNode->dataImpl(), true); | |
| 107 } | |
| 108 m_remainingTextRenderer = nullptr; | |
| 109 | |
| 97 detach(); | 110 detach(); |
| 98 RefPtrWillBeRawPtr<Element> parent = parentOrShadowHostElement(); | 111 RefPtrWillBeRawPtr<Element> parent = parentOrShadowHostElement(); |
| 99 setParentOrShadowHostNode(0); | 112 setParentOrShadowHostNode(0); |
| 100 removedFrom(parent.get()); | 113 removedFrom(parent.get()); |
| 101 } | 114 } |
| 102 | 115 |
| 103 void PseudoElement::attach(const AttachContext& context) | 116 void PseudoElement::attach(const AttachContext& context) |
| 104 { | 117 { |
| 105 ASSERT(!renderer()); | 118 ASSERT(!renderer()); |
| 106 | 119 |
| 107 Element::attach(context); | 120 Element::attach(context); |
| 108 | 121 |
| 109 RenderObject* renderer = this->renderer(); | 122 RenderObject* renderer = this->renderer(); |
| 110 if (!renderer) | 123 if (!renderer) |
| 111 return; | 124 return; |
| 125 | |
| 126 // For a first letter renderer, we remove any current first letter children | |
| 127 // and attach new renderers. | |
| 128 if (isFirstLetterPseudoElement()) { | |
| 129 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.
| |
| 130 this->attachFirstLetterTextRenderers(); | |
|
esprehn
2014/09/30 09:00:30
no this->
dsinclair
2014/09/30 21:46:34
Done.
| |
| 131 return; | |
| 132 } | |
| 133 | |
| 112 RenderStyle* style = renderer->style(); | 134 RenderStyle* style = renderer->style(); |
| 113 if (style->styleType() != BEFORE && style->styleType() != AFTER) | 135 if (style->styleType() != BEFORE && style->styleType() != AFTER) |
| 114 return; | 136 return; |
| 115 ASSERT(style->contentData()); | 137 ASSERT(style->contentData()); |
| 116 | 138 |
| 117 for (const ContentData* content = style->contentData(); content; content = c ontent->next()) { | 139 for (const ContentData* content = style->contentData(); content; content = c ontent->next()) { |
| 118 RenderObject* child = content->createRenderer(document(), style); | 140 RenderObject* child = content->createRenderer(document(), style); |
| 119 if (renderer->isChildAllowed(child, style)) { | 141 if (renderer->isChildAllowed(child, style)) { |
| 120 renderer->addChild(child); | 142 renderer->addChild(child); |
| 121 if (child->isQuote()) | 143 if (child->isQuote()) |
| 122 toRenderQuote(child)->attachQuote(); | 144 toRenderQuote(child)->attachQuote(); |
| 123 } else | 145 } else |
| 124 child->destroy(); | 146 child->destroy(); |
| 125 } | 147 } |
| 126 } | 148 } |
| 127 | 149 |
| 150 RenderStyle* PseudoElement::styleForFirstLetter(RenderObject* rendererContainer) | |
| 151 { | |
| 152 if (!rendererContainer) | |
| 153 return nullptr; | |
| 154 | |
| 155 RenderObject* styleContainer = parentOrShadowHostElement()->renderer(); | |
| 156 if (!styleContainer) | |
| 157 return nullptr; | |
| 158 | |
| 159 // We always force the pseudo style to recompute as the first-letter style | |
| 160 // computed by the style container may not have taken the renderers styles | |
| 161 // into account. | |
| 162 styleContainer->style()->removeCachedPseudoStyle(FIRST_LETTER); | |
| 163 | |
| 164 RenderStyle* pseudoStyle = styleContainer->getCachedPseudoStyle(FIRST_LETTER , rendererContainer->firstLineStyle()); | |
| 165 ASSERT(pseudoStyle); | |
| 166 | |
| 167 // Force inline display (except for floating first-letters). | |
| 168 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); | |
| 169 | |
| 170 // CSS2 says first-letter can't be positioned. | |
| 171 pseudoStyle->setPosition(StaticPosition); | |
| 172 | |
| 173 return pseudoStyle; | |
| 174 } | |
| 175 | |
| 176 void PseudoElement::attachFirstLetterTextRenderers() | |
| 177 { | |
| 178 ASSERT(m_pseudoId == FIRST_LETTER); | |
| 179 | |
| 180 RenderObject* nextRenderer = firstLetterTextRenderer(); | |
| 181 ASSERT(nextRenderer && nextRenderer->isText()); | |
| 182 | |
| 183 // The original string is going to be either a generated content string or a DOM node's | |
| 184 // string. We want the original string before it got transformed in case fir st-letter has | |
| 185 // no text-transform or a different text-transform applied to it. | |
| 186 String oldText = toRenderText(nextRenderer)->isTextFragment() ? toRenderText Fragment(nextRenderer)->completeText() : toRenderText(nextRenderer)->originalTex t(); | |
| 187 ASSERT(oldText.impl()); | |
| 188 | |
| 189 RenderStyle* pseudoStyle = styleForFirstLetter(nextRenderer->parent()); | |
| 190 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.
| |
| 191 | |
| 192 // FIXME: This would already have been calculated in firstLetterRenderer. Ca n we pass the length through? | |
| 193 unsigned length = firstLetterLength(oldText); | |
| 194 | |
| 195 // Construct a text fragment for the text after the first letter. | |
| 196 // This text fragment might be empty. | |
| 197 RenderTextFragment* remainingText = | |
| 198 new RenderTextFragment(nextRenderer->node() ? nextRenderer->node() : &ne xtRenderer->document(), oldText.impl(), length, oldText.length() - length); | |
| 199 remainingText->setStyle(nextRenderer->style()); | |
| 200 if (remainingText->node()) | |
| 201 remainingText->node()->setRenderer(remainingText); | |
| 202 m_remainingTextRenderer = remainingText; | |
| 203 | |
| 204 RenderObject* nextSibling = this->renderer()->nextSibling(); | |
| 205 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
| |
| 206 | |
| 207 // Construct text fragment for the first letter. | |
| 208 RenderTextFragment* letter = new RenderTextFragment(this, oldText.impl(), 0, length); | |
| 209 letter->setStyle(pseudoStyle); | |
| 210 this->renderer()->addChild(letter); | |
| 211 | |
| 212 nextRenderer->destroy(); | |
| 213 nextRenderer = remainingText; | |
| 214 } | |
| 215 | |
| 128 bool PseudoElement::rendererIsNeeded(const RenderStyle& style) | 216 bool PseudoElement::rendererIsNeeded(const RenderStyle& style) |
| 129 { | 217 { |
| 130 return pseudoElementRendererIsNeeded(&style); | 218 return pseudoElementRendererIsNeeded(&style); |
| 131 } | 219 } |
| 132 | 220 |
| 133 void PseudoElement::didRecalcStyle(StyleRecalcChange) | 221 void PseudoElement::didRecalcStyle(StyleRecalcChange) |
| 134 { | 222 { |
| 135 if (!renderer()) | 223 if (!renderer()) |
| 136 return; | 224 return; |
| 137 | 225 |
| 138 // The renderers inside pseudo elements are anonymous so they don't get noti fied of recalcStyle and must have | 226 // The renderers inside pseudo elements are anonymous so they don't get noti fied of recalcStyle and must have |
| 139 // the style propagated downward manually similar to RenderObject::propagate StyleToAnonymousChildren. | 227 // the style propagated downward manually similar to RenderObject::propagate StyleToAnonymousChildren. |
| 140 RenderObject* renderer = this->renderer(); | 228 RenderObject* renderer = this->renderer(); |
| 141 for (RenderObject* child = renderer->nextInPreOrder(renderer); child; child = child->nextInPreOrder(renderer)) { | 229 for (RenderObject* child = renderer->nextInPreOrder(renderer); child; child = child->nextInPreOrder(renderer)) { |
| 230 // We need to re-calculate the correct style for the first letter elemen t | |
| 231 // and then apply that to the container and the text fragment inside. | |
| 232 if (child->style()->styleType() == FIRST_LETTER && m_remainingTextRender er) { | |
| 233 if (RenderStyle* pseudoStyle = styleForFirstLetter(m_remainingTextRe nderer->parent())) { | |
| 234 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.
| |
| 235 child->setPseudoStyle(pseudoStyle); | |
| 236 } | |
| 237 continue; | |
| 238 } | |
| 239 | |
| 142 // We only manage the style for the generated content items. | 240 // We only manage the style for the generated content items. |
| 143 if (!child->isText() && !child->isQuote() && !child->isImage()) | 241 if (!child->isText() && !child->isQuote() && !child->isImage()) |
| 144 continue; | 242 continue; |
| 145 | 243 |
| 146 // The style for the RenderTextFragment for first letter is managed by a n enclosing block, not by us. | |
| 147 if (child->style()->styleType() == FIRST_LETTER) | |
| 148 continue; | |
| 149 | |
| 150 child->setPseudoStyle(renderer->style()); | 244 child->setPseudoStyle(renderer->style()); |
| 151 } | 245 } |
| 152 } | 246 } |
| 153 | 247 |
| 248 // A pseudoElement isn't, strictly, in the dom tree, it hands off the parent | |
| 249 // node. So, we need to have the parent node retrieve its first letter text | |
| 250 // renderer. | |
| 251 RenderObject* PseudoElement::firstLetterTextRenderer() const | |
| 252 { | |
| 253 ASSERT(parentOrShadowHostElement()); | |
| 254 return parentOrShadowHostElement()->firstLetterTextRenderer(); | |
| 255 } | |
| 256 | |
| 154 } // namespace | 257 } // namespace |
| OLD | NEW |