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. |
| 6 * All rights reserved. |
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 7 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
7 * | 8 * |
8 * This library is free software; you can redistribute it and/or | 9 * This library is free software; you can redistribute it and/or |
9 * modify it under the terms of the GNU Library General Public | 10 * modify it under the terms of the GNU Library General Public |
10 * License as published by the Free Software Foundation; either | 11 * License as published by the Free Software Foundation; either |
11 * version 2 of the License, or (at your option) any later version. | 12 * version 2 of the License, or (at your option) any later version. |
12 * | 13 * |
13 * This library is distributed in the hope that it will be useful, | 14 * This library is distributed in the hope that it will be useful, |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
(...skipping 16 matching lines...) Expand all Loading... |
32 #include "core/layout/LayoutTextFragment.h" | 33 #include "core/layout/LayoutTextFragment.h" |
33 #include "core/layout/api/LayoutTextFragmentItem.h" | 34 #include "core/layout/api/LayoutTextFragmentItem.h" |
34 #include "wtf/text/WTFString.h" | 35 #include "wtf/text/WTFString.h" |
35 #include "wtf/text/icu/UnicodeIcu.h" | 36 #include "wtf/text/icu/UnicodeIcu.h" |
36 | 37 |
37 namespace blink { | 38 namespace blink { |
38 | 39 |
39 using namespace WTF; | 40 using namespace WTF; |
40 using namespace Unicode; | 41 using namespace Unicode; |
41 | 42 |
42 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter | 43 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter "Punctuation |
43 // "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps)
, "close" (Pe), | 44 // (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" |
44 // "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that prec
edes or follows the first letter should be included" | 45 // (Pe), "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), |
| 46 // that precedes or follows the first letter should be included" |
45 static inline bool isPunctuationForFirstLetter(UChar c) { | 47 static inline bool isPunctuationForFirstLetter(UChar c) { |
46 CharCategory charCategory = category(c); | 48 CharCategory charCategory = category(c); |
47 return charCategory == Punctuation_Open || | 49 return charCategory == Punctuation_Open || |
48 charCategory == Punctuation_Close || | 50 charCategory == Punctuation_Close || |
49 charCategory == Punctuation_InitialQuote || | 51 charCategory == Punctuation_InitialQuote || |
50 charCategory == Punctuation_FinalQuote || | 52 charCategory == Punctuation_FinalQuote || |
51 charCategory == Punctuation_Other; | 53 charCategory == Punctuation_Other; |
52 } | 54 } |
53 | 55 |
54 static inline bool isSpaceForFirstLetter(UChar c) { | 56 static inline bool isSpaceForFirstLetter(UChar c) { |
55 return isSpaceOrNewline(c) || c == noBreakSpaceCharacter; | 57 return isSpaceOrNewline(c) || c == noBreakSpaceCharacter; |
56 } | 58 } |
57 | 59 |
58 unsigned FirstLetterPseudoElement::firstLetterLength(const String& text) { | 60 unsigned FirstLetterPseudoElement::firstLetterLength(const String& text) { |
59 unsigned length = 0; | 61 unsigned length = 0; |
60 unsigned textLength = text.length(); | 62 unsigned textLength = text.length(); |
61 | 63 |
62 if (textLength == 0) | 64 if (textLength == 0) |
63 return length; | 65 return length; |
64 | 66 |
65 // Account for leading spaces first. | 67 // Account for leading spaces first. |
66 while (length < textLength && isSpaceForFirstLetter(text[length])) | 68 while (length < textLength && isSpaceForFirstLetter(text[length])) |
67 length++; | 69 length++; |
68 // Now account for leading punctuation. | 70 // Now account for leading punctuation. |
69 while (length < textLength && isPunctuationForFirstLetter(text[length])) | 71 while (length < textLength && isPunctuationForFirstLetter(text[length])) |
70 length++; | 72 length++; |
71 | 73 |
72 // Bail if we didn't find a letter before the end of the text or before a spac
e. | 74 // Bail if we didn't find a letter before the end of the text or before a |
| 75 // space. |
73 if (isSpaceForFirstLetter(text[length]) || length == textLength) | 76 if (isSpaceForFirstLetter(text[length]) || length == textLength) |
74 return 0; | 77 return 0; |
75 | 78 |
76 // Account the next character for first letter. | 79 // Account the next character for first letter. |
77 length++; | 80 length++; |
78 | 81 |
79 // Keep looking for allowed punctuation for the :first-letter. | 82 // Keep looking for allowed punctuation for the :first-letter. |
80 for (; length < textLength; ++length) { | 83 for (; length < textLength; ++length) { |
81 UChar c = text[length]; | 84 UChar c = text[length]; |
82 if (!isPunctuationForFirstLetter(c)) | 85 if (!isPunctuationForFirstLetter(c)) |
83 break; | 86 break; |
84 } | 87 } |
85 return length; | 88 return length; |
86 } | 89 } |
87 | 90 |
88 // Once we see any of these layoutObjects we can stop looking for first-letter a
s | 91 // Once we see any of these layoutObjects we can stop looking for first-letter |
89 // they signal the end of the first line of text. | 92 // as they signal the end of the first line of text. |
90 static bool isInvalidFirstLetterLayoutObject(const LayoutObject* obj) { | 93 static bool isInvalidFirstLetterLayoutObject(const LayoutObject* obj) { |
91 return (obj->isBR() || (obj->isText() && toLayoutText(obj)->isWordBreak())); | 94 return (obj->isBR() || (obj->isText() && toLayoutText(obj)->isWordBreak())); |
92 } | 95 } |
93 | 96 |
94 LayoutObject* FirstLetterPseudoElement::firstLetterTextLayoutObject( | 97 LayoutObject* FirstLetterPseudoElement::firstLetterTextLayoutObject( |
95 const Element& element) { | 98 const Element& element) { |
96 LayoutObject* parentLayoutObject = 0; | 99 LayoutObject* parentLayoutObject = 0; |
97 | 100 |
98 // 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 |
99 // first letter text layoutObject from the parent node, and not ourselves. | 102 // first letter text layoutObject from the parent node, and not ourselves. |
100 if (element.isFirstLetterPseudoElement()) | 103 if (element.isFirstLetterPseudoElement()) |
101 parentLayoutObject = element.parentOrShadowHostElement()->layoutObject(); | 104 parentLayoutObject = element.parentOrShadowHostElement()->layoutObject(); |
102 else | 105 else |
103 parentLayoutObject = element.layoutObject(); | 106 parentLayoutObject = element.layoutObject(); |
104 | 107 |
105 if (!parentLayoutObject || | 108 if (!parentLayoutObject || |
106 !parentLayoutObject->style()->hasPseudoStyle(PseudoIdFirstLetter) || | 109 !parentLayoutObject->style()->hasPseudoStyle(PseudoIdFirstLetter) || |
107 !canHaveGeneratedChildren(*parentLayoutObject) || | 110 !canHaveGeneratedChildren(*parentLayoutObject) || |
108 !parentLayoutObject->behavesLikeBlockContainer()) | 111 !parentLayoutObject->behavesLikeBlockContainer()) |
109 return nullptr; | 112 return nullptr; |
110 | 113 |
111 // 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. |
112 LayoutObject* firstLetterTextLayoutObject = | 115 LayoutObject* firstLetterTextLayoutObject = |
113 parentLayoutObject->slowFirstChild(); | 116 parentLayoutObject->slowFirstChild(); |
114 while (firstLetterTextLayoutObject) { | 117 while (firstLetterTextLayoutObject) { |
115 // This can be called when the first letter layoutObject is already in the t
ree. We do not | 118 // This can be called when the first letter layoutObject is already in the |
116 // want to consider that layoutObject for our text layoutObject so we go to
the sibling (which is | 119 // tree. We do not want to consider that layoutObject for our text |
117 // the LayoutTextFragment for the remaining text). | 120 // layoutObject so we go to the sibling (which is the LayoutTextFragment for |
| 121 // the remaining text). |
118 if (firstLetterTextLayoutObject->style() && | 122 if (firstLetterTextLayoutObject->style() && |
119 firstLetterTextLayoutObject->style()->styleType() == | 123 firstLetterTextLayoutObject->style()->styleType() == |
120 PseudoIdFirstLetter) { | 124 PseudoIdFirstLetter) { |
121 firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); | 125 firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); |
122 } else if (firstLetterTextLayoutObject->isText()) { | 126 } else if (firstLetterTextLayoutObject->isText()) { |
123 // FIXME: If there is leading punctuation in a different LayoutText than | 127 // FIXME: If there is leading punctuation in a different LayoutText than |
124 // the first letter, we'll not apply the correct style to it. | 128 // the first letter, we'll not apply the correct style to it. |
125 RefPtr<StringImpl> str = | 129 RefPtr<StringImpl> str = |
126 toLayoutText(firstLetterTextLayoutObject)->isTextFragment() | 130 toLayoutText(firstLetterTextLayoutObject)->isTextFragment() |
127 ? toLayoutTextFragment(firstLetterTextLayoutObject) | 131 ? toLayoutTextFragment(firstLetterTextLayoutObject) |
(...skipping 18 matching lines...) Expand all Loading... |
146 firstLetterTextLayoutObject->isMenuList()) { | 150 firstLetterTextLayoutObject->isMenuList()) { |
147 return nullptr; | 151 return nullptr; |
148 } else if (firstLetterTextLayoutObject | 152 } else if (firstLetterTextLayoutObject |
149 ->isFlexibleBoxIncludingDeprecated() || | 153 ->isFlexibleBoxIncludingDeprecated() || |
150 firstLetterTextLayoutObject->isLayoutGrid()) { | 154 firstLetterTextLayoutObject->isLayoutGrid()) { |
151 firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); | 155 firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); |
152 } else if (!firstLetterTextLayoutObject->isInline() && | 156 } else if (!firstLetterTextLayoutObject->isInline() && |
153 firstLetterTextLayoutObject->style()->hasPseudoStyle( | 157 firstLetterTextLayoutObject->style()->hasPseudoStyle( |
154 PseudoIdFirstLetter) && | 158 PseudoIdFirstLetter) && |
155 canHaveGeneratedChildren(*firstLetterTextLayoutObject)) { | 159 canHaveGeneratedChildren(*firstLetterTextLayoutObject)) { |
156 // There is a layoutObject further down the tree which has PseudoIdFirstLe
tter set. When that node | 160 // There is a layoutObject further down the tree which has |
157 // is attached we will handle setting up the first letter then. | 161 // PseudoIdFirstLetter set. When that node is attached we will handle |
| 162 // setting up the first letter then. |
158 return nullptr; | 163 return nullptr; |
159 } else { | 164 } else { |
160 firstLetterTextLayoutObject = | 165 firstLetterTextLayoutObject = |
161 firstLetterTextLayoutObject->slowFirstChild(); | 166 firstLetterTextLayoutObject->slowFirstChild(); |
162 } | 167 } |
163 } | 168 } |
164 | 169 |
165 // No first letter text to display, we're done. | 170 // No first letter text to display, we're done. |
166 // FIXME: This black-list of disallowed LayoutText subclasses is fragile. crbu
g.com/422336. | 171 // FIXME: This black-list of disallowed LayoutText subclasses is fragile. |
| 172 // crbug.com/422336. |
167 // Should counter be on this list? What about LayoutTextFragment? | 173 // Should counter be on this list? What about LayoutTextFragment? |
168 if (!firstLetterTextLayoutObject || !firstLetterTextLayoutObject->isText() || | 174 if (!firstLetterTextLayoutObject || !firstLetterTextLayoutObject->isText() || |
169 isInvalidFirstLetterLayoutObject(firstLetterTextLayoutObject)) | 175 isInvalidFirstLetterLayoutObject(firstLetterTextLayoutObject)) |
170 return nullptr; | 176 return nullptr; |
171 | 177 |
172 return firstLetterTextLayoutObject; | 178 return firstLetterTextLayoutObject; |
173 } | 179 } |
174 | 180 |
175 FirstLetterPseudoElement::FirstLetterPseudoElement(Element* parent) | 181 FirstLetterPseudoElement::FirstLetterPseudoElement(Element* parent) |
176 : PseudoElement(parent, PseudoIdFirstLetter), | 182 : PseudoElement(parent, PseudoIdFirstLetter), |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
262 | 268 |
263 return pseudoStyle; | 269 return pseudoStyle; |
264 } | 270 } |
265 | 271 |
266 void FirstLetterPseudoElement::attachFirstLetterTextLayoutObjects() { | 272 void FirstLetterPseudoElement::attachFirstLetterTextLayoutObjects() { |
267 LayoutObject* nextLayoutObject = | 273 LayoutObject* nextLayoutObject = |
268 FirstLetterPseudoElement::firstLetterTextLayoutObject(*this); | 274 FirstLetterPseudoElement::firstLetterTextLayoutObject(*this); |
269 DCHECK(nextLayoutObject); | 275 DCHECK(nextLayoutObject); |
270 DCHECK(nextLayoutObject->isText()); | 276 DCHECK(nextLayoutObject->isText()); |
271 | 277 |
272 // The original string is going to be either a generated content string or a D
OM node's | 278 // The original string is going to be either a generated content string or a |
273 // string. We want the original string before it got transformed in case first
-letter has | 279 // DOM node's string. We want the original string before it got transformed in |
274 // no text-transform or a different text-transform applied to it. | 280 // case first-letter has no text-transform or a different text-transform |
| 281 // applied to it. |
275 String oldText = toLayoutText(nextLayoutObject)->isTextFragment() | 282 String oldText = toLayoutText(nextLayoutObject)->isTextFragment() |
276 ? toLayoutTextFragment(nextLayoutObject)->completeText() | 283 ? toLayoutTextFragment(nextLayoutObject)->completeText() |
277 : toLayoutText(nextLayoutObject)->originalText(); | 284 : toLayoutText(nextLayoutObject)->originalText(); |
278 DCHECK(oldText.impl()); | 285 DCHECK(oldText.impl()); |
279 | 286 |
280 ComputedStyle* pseudoStyle = styleForFirstLetter(nextLayoutObject->parent()); | 287 ComputedStyle* pseudoStyle = styleForFirstLetter(nextLayoutObject->parent()); |
281 layoutObject()->setStyle(pseudoStyle); | 288 layoutObject()->setStyle(pseudoStyle); |
282 | 289 |
283 // FIXME: This would already have been calculated in firstLetterLayoutObject.
Can we pass the length through? | 290 // FIXME: This would already have been calculated in firstLetterLayoutObject. |
| 291 // Can we pass the length through? |
284 unsigned length = FirstLetterPseudoElement::firstLetterLength(oldText); | 292 unsigned length = FirstLetterPseudoElement::firstLetterLength(oldText); |
285 | 293 |
286 // Construct a text fragment for the text after the first letter. | 294 // Construct a text fragment for the text after the first letter. |
287 // This text fragment might be empty. | 295 // This text fragment might be empty. |
288 LayoutTextFragment* remainingText = new LayoutTextFragment( | 296 LayoutTextFragment* remainingText = new LayoutTextFragment( |
289 nextLayoutObject->node() ? nextLayoutObject->node() | 297 nextLayoutObject->node() ? nextLayoutObject->node() |
290 : &nextLayoutObject->document(), | 298 : &nextLayoutObject->document(), |
291 oldText.impl(), length, oldText.length() - length); | 299 oldText.impl(), length, oldText.length() - length); |
292 remainingText->setFirstLetterPseudoElement(this); | 300 remainingText->setFirstLetterPseudoElement(this); |
293 remainingText->setIsRemainingTextLayoutObject(true); | 301 remainingText->setIsRemainingTextLayoutObject(true); |
(...skipping 14 matching lines...) Expand all Loading... |
308 letter->setStyle(pseudoStyle); | 316 letter->setStyle(pseudoStyle); |
309 layoutObject()->addChild(letter); | 317 layoutObject()->addChild(letter); |
310 | 318 |
311 nextLayoutObject->destroy(); | 319 nextLayoutObject->destroy(); |
312 } | 320 } |
313 | 321 |
314 void FirstLetterPseudoElement::didRecalcStyle(StyleRecalcChange) { | 322 void FirstLetterPseudoElement::didRecalcStyle(StyleRecalcChange) { |
315 if (!layoutObject()) | 323 if (!layoutObject()) |
316 return; | 324 return; |
317 | 325 |
318 // The layoutObjects inside pseudo elements are anonymous so they don't get no
tified of recalcStyle and must have | 326 // The layoutObjects inside pseudo elements are anonymous so they don't get |
319 // the style propagated downward manually similar to LayoutObject::propagateSt
yleToAnonymousChildren. | 327 // notified of recalcStyle and must have |
| 328 // the style propagated downward manually similar to |
| 329 // LayoutObject::propagateStyleToAnonymousChildren. |
320 LayoutObject* layoutObject = this->layoutObject(); | 330 LayoutObject* layoutObject = this->layoutObject(); |
321 for (LayoutObject* child = layoutObject->nextInPreOrder(layoutObject); child; | 331 for (LayoutObject* child = layoutObject->nextInPreOrder(layoutObject); child; |
322 child = child->nextInPreOrder(layoutObject)) { | 332 child = child->nextInPreOrder(layoutObject)) { |
323 // We need to re-calculate the correct style for the first letter element | 333 // We need to re-calculate the correct style for the first letter element |
324 // and then apply that to the container and the text fragment inside. | 334 // and then apply that to the container and the text fragment inside. |
325 if (child->style()->styleType() == PseudoIdFirstLetter && | 335 if (child->style()->styleType() == PseudoIdFirstLetter && |
326 m_remainingTextLayoutObject) { | 336 m_remainingTextLayoutObject) { |
327 if (ComputedStyle* pseudoStyle = | 337 if (ComputedStyle* pseudoStyle = |
328 styleForFirstLetter(m_remainingTextLayoutObject->parent())) | 338 styleForFirstLetter(m_remainingTextLayoutObject->parent())) |
329 child->setPseudoStyle(pseudoStyle); | 339 child->setPseudoStyle(pseudoStyle); |
330 continue; | 340 continue; |
331 } | 341 } |
332 | 342 |
333 // We only manage the style for the generated content items. | 343 // We only manage the style for the generated content items. |
334 if (!child->isText() && !child->isQuote() && !child->isImage()) | 344 if (!child->isText() && !child->isQuote() && !child->isImage()) |
335 continue; | 345 continue; |
336 | 346 |
337 child->setPseudoStyle(layoutObject->mutableStyle()); | 347 child->setPseudoStyle(layoutObject->mutableStyle()); |
338 } | 348 } |
339 } | 349 } |
340 | 350 |
341 } // namespace blink | 351 } // namespace blink |
OLD | NEW |