OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 * (C) 2007 David Smith (catfish.man@gmail.com) | 4 * (C) 2007 David Smith (catfish.man@gmail.com) |
5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
All rights reserved. | 5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
All rights reserved. |
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
7 * | 7 * |
8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
11 * version 2 of the License, or (at your option) any later version. | 11 * version 2 of the License, or (at your option) any later version. |
12 * | 12 * |
13 * This library is distributed in the hope that it will be useful, | 13 * This library is distributed in the hope that it will be useful, |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 * Library General Public License for more details. | 16 * Library General Public License for more details. |
17 * | 17 * |
18 * You should have received a copy of the GNU Library General Public License | 18 * You should have received a copy of the GNU Library General Public License |
19 * along with this library; see the file COPYING.LIB. If not, write to | 19 * along with this library; see the file COPYING.LIB. If not, write to |
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 * Boston, MA 02110-1301, USA. | 21 * Boston, MA 02110-1301, USA. |
22 */ | 22 */ |
23 | 23 |
24 #include "config.h" | 24 #include "config.h" |
25 #include "core/dom/FirstLetterPseudoElement.h" | 25 #include "core/dom/FirstLetterPseudoElement.h" |
26 | 26 |
27 #include "core/dom/Element.h" | 27 #include "core/dom/Element.h" |
28 #include "core/layout/LayoutObject.h" | 28 #include "core/layout/LayoutObject.h" |
29 #include "core/layout/LayoutObjectInlines.h" | 29 #include "core/layout/LayoutObjectInlines.h" |
30 #include "core/rendering/RenderText.h" | 30 #include "core/layout/LayoutText.h" |
31 #include "core/rendering/RenderTextFragment.h" | 31 #include "core/layout/LayoutTextFragment.h" |
32 #include "wtf/TemporaryChange.h" | 32 #include "wtf/TemporaryChange.h" |
33 #include "wtf/text/WTFString.h" | 33 #include "wtf/text/WTFString.h" |
34 #include "wtf/unicode/icu/UnicodeIcu.h" | 34 #include "wtf/unicode/icu/UnicodeIcu.h" |
35 | 35 |
36 namespace blink { | 36 namespace blink { |
37 | 37 |
38 using namespace WTF; | 38 using namespace WTF; |
39 using namespace Unicode; | 39 using namespace Unicode; |
40 | 40 |
41 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter | 41 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 if (!isPunctuationForFirstLetter(c)) | 84 if (!isPunctuationForFirstLetter(c)) |
85 break; | 85 break; |
86 } | 86 } |
87 return length; | 87 return length; |
88 } | 88 } |
89 | 89 |
90 // Once we see any of these renderers we can stop looking for first-letter as | 90 // Once we see any of these renderers we can stop looking for first-letter as |
91 // they signal the end of the first line of text. | 91 // they signal the end of the first line of text. |
92 static bool isInvalidFirstLetterRenderer(const LayoutObject* obj) | 92 static bool isInvalidFirstLetterRenderer(const LayoutObject* obj) |
93 { | 93 { |
94 return (obj->isBR() || (obj->isText() && toRenderText(obj)->isWordBreak())); | 94 return (obj->isBR() || (obj->isText() && toLayoutText(obj)->isWordBreak())); |
95 } | 95 } |
96 | 96 |
97 LayoutObject* FirstLetterPseudoElement::firstLetterTextRenderer(const Element& e
lement) | 97 LayoutObject* FirstLetterPseudoElement::firstLetterTextRenderer(const Element& e
lement) |
98 { | 98 { |
99 LayoutObject* parentRenderer = 0; | 99 LayoutObject* parentRenderer = 0; |
100 | 100 |
101 // If we are looking at a first letter element then we need to find the | 101 // If we are looking at a first letter element then we need to find the |
102 // first letter text renderer from the parent node, and not ourselves. | 102 // first letter text renderer from the parent node, and not ourselves. |
103 if (element.isFirstLetterPseudoElement()) | 103 if (element.isFirstLetterPseudoElement()) |
104 parentRenderer = element.parentOrShadowHostElement()->renderer(); | 104 parentRenderer = element.parentOrShadowHostElement()->renderer(); |
105 else | 105 else |
106 parentRenderer = element.renderer(); | 106 parentRenderer = element.renderer(); |
107 | 107 |
108 if (!parentRenderer | 108 if (!parentRenderer |
109 || !parentRenderer->style()->hasPseudoStyle(FIRST_LETTER) | 109 || !parentRenderer->style()->hasPseudoStyle(FIRST_LETTER) |
110 || !parentRenderer->canHaveGeneratedChildren() | 110 || !parentRenderer->canHaveGeneratedChildren() |
111 || !(parentRenderer->isLayoutBlockFlow() || parentRenderer->isLayoutButt
on())) | 111 || !(parentRenderer->isLayoutBlockFlow() || parentRenderer->isLayoutButt
on())) |
112 return nullptr; | 112 return nullptr; |
113 | 113 |
114 // Drill down into our children and look for our first text child. | 114 // Drill down into our children and look for our first text child. |
115 LayoutObject* firstLetterTextRenderer = parentRenderer->slowFirstChild(); | 115 LayoutObject* firstLetterTextRenderer = parentRenderer->slowFirstChild(); |
116 while (firstLetterTextRenderer) { | 116 while (firstLetterTextRenderer) { |
117 // This can be called when the first letter renderer is already in the t
ree. We do not | 117 // This can be called when the first letter renderer is already in the t
ree. We do not |
118 // want to consider that renderer for our text renderer so we go to the
sibling (which is | 118 // want to consider that renderer for our text renderer so we go to the
sibling (which is |
119 // the RenderTextFragment for the remaining text). | 119 // the LayoutTextFragment for the remaining text). |
120 if (firstLetterTextRenderer->style() && firstLetterTextRenderer->style()
->styleType() == FIRST_LETTER) { | 120 if (firstLetterTextRenderer->style() && firstLetterTextRenderer->style()
->styleType() == FIRST_LETTER) { |
121 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); | 121 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); |
122 } else if (firstLetterTextRenderer->isText()) { | 122 } else if (firstLetterTextRenderer->isText()) { |
123 // FIXME: If there is leading punctuation in a different RenderText
than | 123 // FIXME: If there is leading punctuation in a different LayoutText
than |
124 // the first letter, we'll not apply the correct style to it. | 124 // the first letter, we'll not apply the correct style to it. |
125 RefPtr<StringImpl> str = toRenderText(firstLetterTextRenderer)->isTe
xtFragment() ? | 125 RefPtr<StringImpl> str = toLayoutText(firstLetterTextRenderer)->isTe
xtFragment() ? |
126 toRenderTextFragment(firstLetterTextRenderer)->completeText() : | 126 toLayoutTextFragment(firstLetterTextRenderer)->completeText() : |
127 toRenderText(firstLetterTextRenderer)->originalText(); | 127 toLayoutText(firstLetterTextRenderer)->originalText(); |
128 if (firstLetterLength(str.get()) || isInvalidFirstLetterRenderer(fir
stLetterTextRenderer)) | 128 if (firstLetterLength(str.get()) || isInvalidFirstLetterRenderer(fir
stLetterTextRenderer)) |
129 break; | 129 break; |
130 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); | 130 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); |
131 } else if (firstLetterTextRenderer->isListMarker()) { | 131 } else if (firstLetterTextRenderer->isListMarker()) { |
132 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); | 132 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); |
133 } else if (firstLetterTextRenderer->isFloatingOrOutOfFlowPositioned()) { | 133 } else if (firstLetterTextRenderer->isFloatingOrOutOfFlowPositioned()) { |
134 if (firstLetterTextRenderer->style()->styleType() == FIRST_LETTER) { | 134 if (firstLetterTextRenderer->style()->styleType() == FIRST_LETTER) { |
135 firstLetterTextRenderer = firstLetterTextRenderer->slowFirstChil
d(); | 135 firstLetterTextRenderer = firstLetterTextRenderer->slowFirstChil
d(); |
136 break; | 136 break; |
137 } | 137 } |
138 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); | 138 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); |
139 } else if (firstLetterTextRenderer->isReplaced() || firstLetterTextRende
rer->isLayoutButton() | 139 } else if (firstLetterTextRenderer->isReplaced() || firstLetterTextRende
rer->isLayoutButton() |
140 || firstLetterTextRenderer->isMenuList()) { | 140 || firstLetterTextRenderer->isMenuList()) { |
141 return nullptr; | 141 return nullptr; |
142 } else if (firstLetterTextRenderer->isFlexibleBoxIncludingDeprecated() |
| firstLetterTextRenderer->isLayoutGrid()) { | 142 } else if (firstLetterTextRenderer->isFlexibleBoxIncludingDeprecated() |
| firstLetterTextRenderer->isLayoutGrid()) { |
143 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); | 143 firstLetterTextRenderer = firstLetterTextRenderer->nextSibling(); |
144 } else if (firstLetterTextRenderer->style()->hasPseudoStyle(FIRST_LETTER
) | 144 } else if (firstLetterTextRenderer->style()->hasPseudoStyle(FIRST_LETTER
) |
145 && firstLetterTextRenderer->canHaveGeneratedChildren()) { | 145 && firstLetterTextRenderer->canHaveGeneratedChildren()) { |
146 // There is a renderer further down the tree which has FIRST_LETTER
set. When that node | 146 // There is a renderer further down the tree which has FIRST_LETTER
set. When that node |
147 // is attached we will handle setting up the first letter then. | 147 // is attached we will handle setting up the first letter then. |
148 return nullptr; | 148 return nullptr; |
149 } else { | 149 } else { |
150 firstLetterTextRenderer = firstLetterTextRenderer->slowFirstChild(); | 150 firstLetterTextRenderer = firstLetterTextRenderer->slowFirstChild(); |
151 } | 151 } |
152 } | 152 } |
153 | 153 |
154 // No first letter text to display, we're done. | 154 // No first letter text to display, we're done. |
155 // FIXME: This black-list of disallowed RenderText subclasses is fragile. cr
bug.com/422336. | 155 // FIXME: This black-list of disallowed LayoutText subclasses is fragile. cr
bug.com/422336. |
156 // Should counter be on this list? What about RenderTextFragment? | 156 // Should counter be on this list? What about LayoutTextFragment? |
157 if (!firstLetterTextRenderer || !firstLetterTextRenderer->isText() || isInva
lidFirstLetterRenderer(firstLetterTextRenderer)) | 157 if (!firstLetterTextRenderer || !firstLetterTextRenderer->isText() || isInva
lidFirstLetterRenderer(firstLetterTextRenderer)) |
158 return nullptr; | 158 return nullptr; |
159 | 159 |
160 return firstLetterTextRenderer; | 160 return firstLetterTextRenderer; |
161 } | 161 } |
162 | 162 |
163 FirstLetterPseudoElement::FirstLetterPseudoElement(Element* parent) | 163 FirstLetterPseudoElement::FirstLetterPseudoElement(Element* parent) |
164 : PseudoElement(parent, FIRST_LETTER) | 164 : PseudoElement(parent, FIRST_LETTER) |
165 , m_remainingTextRenderer(nullptr) | 165 , m_remainingTextRenderer(nullptr) |
166 { | 166 { |
167 } | 167 } |
168 | 168 |
169 FirstLetterPseudoElement::~FirstLetterPseudoElement() | 169 FirstLetterPseudoElement::~FirstLetterPseudoElement() |
170 { | 170 { |
171 } | 171 } |
172 | 172 |
173 void FirstLetterPseudoElement::updateTextFragments() | 173 void FirstLetterPseudoElement::updateTextFragments() |
174 { | 174 { |
175 String oldText = m_remainingTextRenderer->completeText(); | 175 String oldText = m_remainingTextRenderer->completeText(); |
176 ASSERT(oldText.impl()); | 176 ASSERT(oldText.impl()); |
177 | 177 |
178 unsigned length = FirstLetterPseudoElement::firstLetterLength(oldText); | 178 unsigned length = FirstLetterPseudoElement::firstLetterLength(oldText); |
179 m_remainingTextRenderer->setTextFragment(oldText.impl()->substring(length, o
ldText.length()), length, oldText.length() - length); | 179 m_remainingTextRenderer->setTextFragment(oldText.impl()->substring(length, o
ldText.length()), length, oldText.length() - length); |
180 m_remainingTextRenderer->dirtyLineBoxes(); | 180 m_remainingTextRenderer->dirtyLineBoxes(); |
181 | 181 |
182 for (auto child = renderer()->slowFirstChild(); child; child = child->nextSi
bling()) { | 182 for (auto child = renderer()->slowFirstChild(); child; child = child->nextSi
bling()) { |
183 if (!child->isText() || !toRenderText(child)->isTextFragment()) | 183 if (!child->isText() || !toLayoutText(child)->isTextFragment()) |
184 continue; | 184 continue; |
185 RenderTextFragment* childFragment = toRenderTextFragment(child); | 185 LayoutTextFragment* childFragment = toLayoutTextFragment(child); |
186 if (childFragment->firstLetterPseudoElement() != this) | 186 if (childFragment->firstLetterPseudoElement() != this) |
187 continue; | 187 continue; |
188 | 188 |
189 childFragment->setTextFragment(oldText.impl()->substring(0, length), 0,
length); | 189 childFragment->setTextFragment(oldText.impl()->substring(0, length), 0,
length); |
190 childFragment->dirtyLineBoxes(); | 190 childFragment->dirtyLineBoxes(); |
191 break; | 191 break; |
192 } | 192 } |
193 } | 193 } |
194 | 194 |
195 void FirstLetterPseudoElement::setRemainingTextRenderer(RenderTextFragment* frag
ment) | 195 void FirstLetterPseudoElement::setRemainingTextRenderer(LayoutTextFragment* frag
ment) |
196 { | 196 { |
197 // The text fragment we get our content from is being destroyed. We need | 197 // The text fragment we get our content from is being destroyed. We need |
198 // to tell our parent element to recalcStyle so we can get cleaned up | 198 // to tell our parent element to recalcStyle so we can get cleaned up |
199 // as well. | 199 // as well. |
200 if (!fragment) | 200 if (!fragment) |
201 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::creat
e(StyleChangeReason::PseudoClass)); | 201 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::creat
e(StyleChangeReason::PseudoClass)); |
202 | 202 |
203 m_remainingTextRenderer = fragment; | 203 m_remainingTextRenderer = fragment; |
204 } | 204 } |
205 | 205 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 | 244 |
245 void FirstLetterPseudoElement::attachFirstLetterTextRenderers() | 245 void FirstLetterPseudoElement::attachFirstLetterTextRenderers() |
246 { | 246 { |
247 LayoutObject* nextRenderer = FirstLetterPseudoElement::firstLetterTextRender
er(*this); | 247 LayoutObject* nextRenderer = FirstLetterPseudoElement::firstLetterTextRender
er(*this); |
248 ASSERT(nextRenderer); | 248 ASSERT(nextRenderer); |
249 ASSERT(nextRenderer->isText()); | 249 ASSERT(nextRenderer->isText()); |
250 | 250 |
251 // The original string is going to be either a generated content string or a
DOM node's | 251 // The original string is going to be either a generated content string or a
DOM node's |
252 // string. We want the original string before it got transformed in case fir
st-letter has | 252 // string. We want the original string before it got transformed in case fir
st-letter has |
253 // no text-transform or a different text-transform applied to it. | 253 // no text-transform or a different text-transform applied to it. |
254 String oldText = toRenderText(nextRenderer)->isTextFragment() ? toRenderText
Fragment(nextRenderer)->completeText() : toRenderText(nextRenderer)->originalTex
t(); | 254 String oldText = toLayoutText(nextRenderer)->isTextFragment() ? toLayoutText
Fragment(nextRenderer)->completeText() : toLayoutText(nextRenderer)->originalTex
t(); |
255 ASSERT(oldText.impl()); | 255 ASSERT(oldText.impl()); |
256 | 256 |
257 LayoutStyle* pseudoStyle = styleForFirstLetter(nextRenderer->parent()); | 257 LayoutStyle* pseudoStyle = styleForFirstLetter(nextRenderer->parent()); |
258 renderer()->setStyle(pseudoStyle); | 258 renderer()->setStyle(pseudoStyle); |
259 | 259 |
260 // FIXME: This would already have been calculated in firstLetterRenderer. Ca
n we pass the length through? | 260 // FIXME: This would already have been calculated in firstLetterRenderer. Ca
n we pass the length through? |
261 unsigned length = FirstLetterPseudoElement::firstLetterLength(oldText); | 261 unsigned length = FirstLetterPseudoElement::firstLetterLength(oldText); |
262 | 262 |
263 // Construct a text fragment for the text after the first letter. | 263 // Construct a text fragment for the text after the first letter. |
264 // This text fragment might be empty. | 264 // This text fragment might be empty. |
265 RenderTextFragment* remainingText = | 265 LayoutTextFragment* remainingText = |
266 new RenderTextFragment(nextRenderer->node() ? nextRenderer->node() : &ne
xtRenderer->document(), oldText.impl(), length, oldText.length() - length); | 266 new LayoutTextFragment(nextRenderer->node() ? nextRenderer->node() : &ne
xtRenderer->document(), oldText.impl(), length, oldText.length() - length); |
267 remainingText->setFirstLetterPseudoElement(this); | 267 remainingText->setFirstLetterPseudoElement(this); |
268 remainingText->setIsRemainingTextRenderer(true); | 268 remainingText->setIsRemainingTextRenderer(true); |
269 remainingText->setStyle(nextRenderer->style()); | 269 remainingText->setStyle(nextRenderer->style()); |
270 | 270 |
271 if (remainingText->node()) | 271 if (remainingText->node()) |
272 remainingText->node()->setRenderer(remainingText); | 272 remainingText->node()->setRenderer(remainingText); |
273 | 273 |
274 m_remainingTextRenderer = remainingText; | 274 m_remainingTextRenderer = remainingText; |
275 | 275 |
276 LayoutObject* nextSibling = renderer()->nextSibling(); | 276 LayoutObject* nextSibling = renderer()->nextSibling(); |
277 renderer()->parent()->addChild(remainingText, nextSibling); | 277 renderer()->parent()->addChild(remainingText, nextSibling); |
278 | 278 |
279 // Construct text fragment for the first letter. | 279 // Construct text fragment for the first letter. |
280 RenderTextFragment* letter = new RenderTextFragment(&nextRenderer->document(
), oldText.impl(), 0, length); | 280 LayoutTextFragment* letter = new LayoutTextFragment(&nextRenderer->document(
), oldText.impl(), 0, length); |
281 letter->setFirstLetterPseudoElement(this); | 281 letter->setFirstLetterPseudoElement(this); |
282 letter->setStyle(pseudoStyle); | 282 letter->setStyle(pseudoStyle); |
283 renderer()->addChild(letter); | 283 renderer()->addChild(letter); |
284 | 284 |
285 nextRenderer->destroy(); | 285 nextRenderer->destroy(); |
286 } | 286 } |
287 | 287 |
288 void FirstLetterPseudoElement::didRecalcStyle(StyleRecalcChange) | 288 void FirstLetterPseudoElement::didRecalcStyle(StyleRecalcChange) |
289 { | 289 { |
290 if (!renderer()) | 290 if (!renderer()) |
(...skipping 13 matching lines...) Expand all Loading... |
304 | 304 |
305 // We only manage the style for the generated content items. | 305 // We only manage the style for the generated content items. |
306 if (!child->isText() && !child->isQuote() && !child->isImage()) | 306 if (!child->isText() && !child->isQuote() && !child->isImage()) |
307 continue; | 307 continue; |
308 | 308 |
309 child->setPseudoStyle(renderer->style()); | 309 child->setPseudoStyle(renderer->style()); |
310 } | 310 } |
311 } | 311 } |
312 | 312 |
313 } // namespace blink | 313 } // namespace blink |
OLD | NEW |