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 |
11 * contributors may be used to endorse or promote products derived from | 11 * contributors may be used to endorse or promote products derived from |
12 * this software without specific prior written permission. | 12 * this software without specific prior written permission. |
13 * | 13 * |
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
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/dom/FirstLetterHelper.h" | |
30 #include "core/inspector/InspectorInstrumentation.h" | 31 #include "core/inspector/InspectorInstrumentation.h" |
31 #include "core/rendering/RenderObject.h" | 32 #include "core/rendering/RenderObject.h" |
32 #include "core/rendering/RenderQuote.h" | 33 #include "core/rendering/RenderQuote.h" |
34 #include "core/rendering/RenderTextFragment.h" | |
33 #include "core/rendering/style/ContentData.h" | 35 #include "core/rendering/style/ContentData.h" |
34 | 36 |
35 namespace blink { | 37 namespace blink { |
36 | 38 |
37 const QualifiedName& pseudoElementTagName(PseudoId pseudoId) | 39 const QualifiedName& pseudoElementTagName(PseudoId pseudoId) |
38 { | 40 { |
39 switch (pseudoId) { | 41 switch (pseudoId) { |
40 case AFTER: { | 42 case AFTER: { |
41 DEFINE_STATIC_LOCAL(QualifiedName, after, (nullAtom, "<pseudo:after>", n ullAtom)); | 43 DEFINE_STATIC_LOCAL(QualifiedName, after, (nullAtom, "<pseudo:after>", n ullAtom)); |
42 return after; | 44 return after; |
43 } | 45 } |
44 case BEFORE: { | 46 case BEFORE: { |
45 DEFINE_STATIC_LOCAL(QualifiedName, before, (nullAtom, "<pseudo:before>", nullAtom)); | 47 DEFINE_STATIC_LOCAL(QualifiedName, before, (nullAtom, "<pseudo:before>", nullAtom)); |
46 return before; | 48 return before; |
47 } | 49 } |
48 case BACKDROP: { | 50 case BACKDROP: { |
49 DEFINE_STATIC_LOCAL(QualifiedName, backdrop, (nullAtom, "<pseudo:backdro p>", nullAtom)); | 51 DEFINE_STATIC_LOCAL(QualifiedName, backdrop, (nullAtom, "<pseudo:backdro p>", nullAtom)); |
50 return backdrop; | 52 return backdrop; |
51 } | 53 } |
54 case FIRST_LETTER: { | |
55 DEFINE_STATIC_LOCAL(QualifiedName, firstLetter, (nullAtom, "<pseudo:firs t-letter>", nullAtom)); | |
56 return firstLetter; | |
57 } | |
52 default: { | 58 default: { |
53 ASSERT_NOT_REACHED(); | 59 ASSERT_NOT_REACHED(); |
54 } | 60 } |
55 } | 61 } |
56 DEFINE_STATIC_LOCAL(QualifiedName, name, (nullAtom, "<pseudo>", nullAtom)); | 62 DEFINE_STATIC_LOCAL(QualifiedName, name, (nullAtom, "<pseudo>", nullAtom)); |
57 return name; | 63 return name; |
58 } | 64 } |
59 | 65 |
60 String PseudoElement::pseudoElementNameForEvents(PseudoId pseudoId) | 66 String PseudoElement::pseudoElementNameForEvents(PseudoId pseudoId) |
61 { | 67 { |
62 DEFINE_STATIC_LOCAL(const String, after, ("::after")); | 68 DEFINE_STATIC_LOCAL(const String, after, ("::after")); |
63 DEFINE_STATIC_LOCAL(const String, before, ("::before")); | 69 DEFINE_STATIC_LOCAL(const String, before, ("::before")); |
64 switch (pseudoId) { | 70 switch (pseudoId) { |
65 case AFTER: | 71 case AFTER: |
66 return after; | 72 return after; |
67 case BEFORE: | 73 case BEFORE: |
68 return before; | 74 return before; |
69 default: | 75 default: |
70 return emptyString(); | 76 return emptyString(); |
71 } | 77 } |
72 } | 78 } |
73 | 79 |
74 PseudoElement::PseudoElement(Element* parent, PseudoId pseudoId) | 80 PseudoElement::PseudoElement(Element* parent, PseudoId pseudoId) |
75 : Element(pseudoElementTagName(pseudoId), &parent->document(), CreateElement ) | 81 : Element(pseudoElementTagName(pseudoId), &parent->document(), CreateElement ) |
76 , m_pseudoId(pseudoId) | 82 , m_pseudoId(pseudoId) |
83 , m_remainingTextRenderer(nullptr) | |
77 { | 84 { |
78 ASSERT(pseudoId != NOPSEUDO); | 85 ASSERT(pseudoId != NOPSEUDO); |
79 setParentOrShadowHostNode(parent); | 86 setParentOrShadowHostNode(parent); |
80 setHasCustomStyleCallbacks(); | 87 setHasCustomStyleCallbacks(); |
81 } | 88 } |
82 | 89 |
83 PassRefPtr<RenderStyle> PseudoElement::customStyleForRenderer() | 90 PassRefPtr<RenderStyle> PseudoElement::customStyleForRenderer() |
84 { | 91 { |
85 return parentOrShadowHostElement()->renderer()->getCachedPseudoStyle(m_pseud oId); | 92 return parentOrShadowHostElement()->renderer()->getCachedPseudoStyle(m_pseud oId); |
86 } | 93 } |
87 | 94 |
88 void PseudoElement::dispose() | 95 void PseudoElement::dispose() |
89 { | 96 { |
90 ASSERT(parentOrShadowHostElement()); | 97 ASSERT(parentOrShadowHostElement()); |
91 | 98 |
92 InspectorInstrumentation::pseudoElementDestroyed(this); | 99 InspectorInstrumentation::pseudoElementDestroyed(this); |
93 | 100 |
94 ASSERT(!nextSibling()); | 101 ASSERT(!nextSibling()); |
95 ASSERT(!previousSibling()); | 102 ASSERT(!previousSibling()); |
96 | 103 |
104 if (m_remainingTextRenderer && m_remainingTextRenderer->node()) { | |
105 Text* textNode = toText(m_remainingTextRenderer->node()); | |
106 m_remainingTextRenderer->setText(textNode->dataImpl(), true); | |
107 } | |
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
| |
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 attachFirstLetterTextRenderers(); | |
130 return; | |
131 } | |
132 | |
112 RenderStyle* style = renderer->style(); | 133 RenderStyle* style = renderer->style(); |
113 if (style->styleType() != BEFORE && style->styleType() != AFTER) | 134 if (style->styleType() != BEFORE && style->styleType() != AFTER) |
114 return; | 135 return; |
115 ASSERT(style->contentData()); | 136 ASSERT(style->contentData()); |
116 | 137 |
117 for (const ContentData* content = style->contentData(); content; content = c ontent->next()) { | 138 for (const ContentData* content = style->contentData(); content; content = c ontent->next()) { |
118 RenderObject* child = content->createRenderer(document(), style); | 139 RenderObject* child = content->createRenderer(document(), style); |
119 if (renderer->isChildAllowed(child, style)) { | 140 if (renderer->isChildAllowed(child, style)) { |
120 renderer->addChild(child); | 141 renderer->addChild(child); |
121 if (child->isQuote()) | 142 if (child->isQuote()) |
122 toRenderQuote(child)->attachQuote(); | 143 toRenderQuote(child)->attachQuote(); |
123 } else | 144 } else |
124 child->destroy(); | 145 child->destroy(); |
125 } | 146 } |
126 } | 147 } |
127 | 148 |
149 RenderStyle* PseudoElement::styleForFirstLetter(RenderObject* rendererContainer) | |
150 { | |
151 if (!rendererContainer) | |
152 return nullptr; | |
153 | |
154 RenderObject* styleContainer = parentOrShadowHostElement()->renderer(); | |
155 if (!styleContainer) | |
156 return nullptr; | |
157 | |
158 // We always force the pseudo style to recompute as the first-letter style | |
159 // computed by the style container may not have taken the renderers styles | |
160 // into account. | |
161 styleContainer->style()->removeCachedPseudoStyle(FIRST_LETTER); | |
162 | |
163 RenderStyle* pseudoStyle = styleContainer->getCachedPseudoStyle(FIRST_LETTER , rendererContainer->firstLineStyle()); | |
164 ASSERT(pseudoStyle); | |
165 | |
166 // Force inline display (except for floating first-letters). | |
167 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); | |
168 | |
169 // CSS2 says first-letter can't be positioned. | |
170 pseudoStyle->setPosition(StaticPosition); | |
171 | |
172 return pseudoStyle; | |
173 } | |
174 | |
175 void PseudoElement::attachFirstLetterTextRenderers() | |
176 { | |
177 ASSERT(m_pseudoId == FIRST_LETTER); | |
178 | |
179 RenderObject* nextRenderer = FirstLetterHelper::firstLetterTextRenderer(*thi s); | |
180 ASSERT(nextRenderer && nextRenderer->isText()); | |
181 | |
182 // The original string is going to be either a generated content string or a DOM node's | |
183 // string. We want the original string before it got transformed in case fir st-letter has | |
184 // no text-transform or a different text-transform applied to it. | |
185 String oldText = toRenderText(nextRenderer)->isTextFragment() ? toRenderText Fragment(nextRenderer)->completeText() : toRenderText(nextRenderer)->originalTex t(); | |
186 ASSERT(oldText.impl()); | |
187 | |
188 RenderStyle* pseudoStyle = styleForFirstLetter(nextRenderer->parent()); | |
189 renderer()->setStyle(pseudoStyle); | |
190 | |
191 // FIXME: This would already have been calculated in firstLetterRenderer. Ca n we pass the length through? | |
192 unsigned length = FirstLetterHelper::firstLetterLength(oldText); | |
193 | |
194 // Construct a text fragment for the text after the first letter. | |
195 // This text fragment might be empty. | |
196 RenderTextFragment* remainingText = | |
197 new RenderTextFragment(nextRenderer->node() ? nextRenderer->node() : &ne xtRenderer->document(), oldText.impl(), length, oldText.length() - length); | |
198 remainingText->setStyle(nextRenderer->style()); | |
199 if (remainingText->node()) | |
200 remainingText->node()->setRenderer(remainingText); | |
201 m_remainingTextRenderer = remainingText; | |
202 | |
203 RenderObject* nextSibling = renderer()->nextSibling(); | |
204 renderer()->parent()->addChild(remainingText, nextSibling); | |
205 | |
206 // Construct text fragment for the first letter. | |
207 RenderTextFragment* letter = new RenderTextFragment(this, oldText.impl(), 0, length); | |
208 letter->setStyle(pseudoStyle); | |
209 renderer()->addChild(letter); | |
210 | |
211 nextRenderer->destroy(); | |
212 nextRenderer = remainingText; | |
213 } | |
214 | |
128 bool PseudoElement::rendererIsNeeded(const RenderStyle& style) | 215 bool PseudoElement::rendererIsNeeded(const RenderStyle& style) |
129 { | 216 { |
130 return pseudoElementRendererIsNeeded(&style); | 217 return pseudoElementRendererIsNeeded(&style); |
131 } | 218 } |
132 | 219 |
133 void PseudoElement::didRecalcStyle(StyleRecalcChange) | 220 void PseudoElement::didRecalcStyle(StyleRecalcChange) |
134 { | 221 { |
135 if (!renderer()) | 222 if (!renderer()) |
136 return; | 223 return; |
137 | 224 |
138 // The renderers inside pseudo elements are anonymous so they don't get noti fied of recalcStyle and must have | 225 // 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. | 226 // the style propagated downward manually similar to RenderObject::propagate StyleToAnonymousChildren. |
140 RenderObject* renderer = this->renderer(); | 227 RenderObject* renderer = this->renderer(); |
141 for (RenderObject* child = renderer->nextInPreOrder(renderer); child; child = child->nextInPreOrder(renderer)) { | 228 for (RenderObject* child = renderer->nextInPreOrder(renderer); child; child = child->nextInPreOrder(renderer)) { |
229 // We need to re-calculate the correct style for the first letter elemen t | |
230 // and then apply that to the container and the text fragment inside. | |
231 if (child->style()->styleType() == FIRST_LETTER && m_remainingTextRender er) { | |
232 if (RenderStyle* pseudoStyle = styleForFirstLetter(m_remainingTextRe nderer->parent())) | |
233 child->setPseudoStyle(pseudoStyle); | |
234 continue; | |
235 } | |
236 | |
142 // We only manage the style for the generated content items. | 237 // We only manage the style for the generated content items. |
143 if (!child->isText() && !child->isQuote() && !child->isImage()) | 238 if (!child->isText() && !child->isQuote() && !child->isImage()) |
144 continue; | 239 continue; |
145 | 240 |
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()); | 241 child->setPseudoStyle(renderer->style()); |
151 } | 242 } |
152 } | 243 } |
153 | 244 |
154 } // namespace | 245 } // namespace |
OLD | NEW |