| 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 |