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 |