OLD | NEW |
1 /* | 1 /* |
2 * (C) 1999 Lars Knoll (knoll@kde.org) | 2 * (C) 1999 Lars Knoll (knoll@kde.org) |
3 * (C) 2000 Dirk Mueller (mueller@kde.org) | 3 * (C) 2000 Dirk Mueller (mueller@kde.org) |
4 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. | 4 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
5 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) | 5 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) |
6 * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) | 6 * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) |
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 |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
85 if (Settings* settings = m_layoutText->document().settings()) { | 85 if (Settings* settings = m_layoutText->document().settings()) { |
86 startOneShot(settings->getPasswordEchoDurationInSeconds(), | 86 startOneShot(settings->getPasswordEchoDurationInSeconds(), |
87 BLINK_FROM_HERE); | 87 BLINK_FROM_HERE); |
88 } | 88 } |
89 } | 89 } |
90 void invalidate() { m_lastTypedCharacterOffset = -1; } | 90 void invalidate() { m_lastTypedCharacterOffset = -1; } |
91 unsigned lastTypedCharacterOffset() { return m_lastTypedCharacterOffset; } | 91 unsigned lastTypedCharacterOffset() { return m_lastTypedCharacterOffset; } |
92 | 92 |
93 private: | 93 private: |
94 void fired() override { | 94 void fired() override { |
95 ASSERT(gSecureTextTimers->contains(m_layoutText)); | 95 DCHECK(gSecureTextTimers->contains(m_layoutText)); |
96 m_layoutText->setText( | 96 m_layoutText->setText( |
97 m_layoutText->text().impl(), | 97 m_layoutText->text().impl(), |
98 true /* forcing setting text as it may be masked later */); | 98 true /* forcing setting text as it may be masked later */); |
99 } | 99 } |
100 | 100 |
101 LayoutText* m_layoutText; | 101 LayoutText* m_layoutText; |
102 int m_lastTypedCharacterOffset; | 102 int m_lastTypedCharacterOffset; |
103 }; | 103 }; |
104 | 104 |
105 static void makeCapitalized(String* string, UChar previous) { | 105 static void makeCapitalized(String* string, UChar previous) { |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 m_linesDirty(false), | 155 m_linesDirty(false), |
156 m_containsReversedText(false), | 156 m_containsReversedText(false), |
157 m_knownToHaveNoOverflowAndNoFallbackFonts(false), | 157 m_knownToHaveNoOverflowAndNoFallbackFonts(false), |
158 m_minWidth(-1), | 158 m_minWidth(-1), |
159 m_maxWidth(-1), | 159 m_maxWidth(-1), |
160 m_firstLineMinWidth(0), | 160 m_firstLineMinWidth(0), |
161 m_lastLineLineMinWidth(0), | 161 m_lastLineLineMinWidth(0), |
162 m_text(std::move(str)), | 162 m_text(std::move(str)), |
163 m_firstTextBox(nullptr), | 163 m_firstTextBox(nullptr), |
164 m_lastTextBox(nullptr) { | 164 m_lastTextBox(nullptr) { |
165 ASSERT(m_text); | 165 DCHECK(m_text); |
166 DCHECK(!node || !node->isDocumentNode()); | 166 DCHECK(!node || !node->isDocumentNode()); |
167 | 167 |
168 setIsText(); | 168 setIsText(); |
169 | 169 |
170 if (node) | 170 if (node) |
171 frameView()->incrementVisuallyNonEmptyCharacterCount(m_text.length()); | 171 frameView()->incrementVisuallyNonEmptyCharacterCount(m_text.length()); |
172 } | 172 } |
173 | 173 |
174 #if DCHECK_IS_ON() | 174 #if DCHECK_IS_ON() |
175 | 175 |
176 LayoutText::~LayoutText() { | 176 LayoutText::~LayoutText() { |
177 ASSERT(!m_firstTextBox); | 177 DCHECK(!m_firstTextBox); |
178 ASSERT(!m_lastTextBox); | 178 DCHECK(!m_lastTextBox); |
179 } | 179 } |
180 | 180 |
181 #endif | 181 #endif |
182 | 182 |
183 LayoutText* LayoutText::createEmptyAnonymous(Document& doc) { | 183 LayoutText* LayoutText::createEmptyAnonymous(Document& doc) { |
184 LayoutText* text = new LayoutText(nullptr, StringImpl::empty); | 184 LayoutText* text = new LayoutText(nullptr, StringImpl::empty); |
185 text->setDocumentForAnonymous(&doc); | 185 text->setDocumentForAnonymous(&doc); |
186 return text; | 186 return text; |
187 } | 187 } |
188 | 188 |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
368 unsigned start, | 368 unsigned start, |
369 unsigned end, | 369 unsigned end, |
370 bool useSelectionHeight) { | 370 bool useSelectionHeight) { |
371 // Work around signed/unsigned issues. This function takes unsigneds, and is | 371 // Work around signed/unsigned issues. This function takes unsigneds, and is |
372 // often passed UINT_MAX to mean "all the way to the end". InlineTextBox | 372 // often passed UINT_MAX to mean "all the way to the end". InlineTextBox |
373 // coordinates are unsigneds, so changing this function to take ints causes | 373 // coordinates are unsigneds, so changing this function to take ints causes |
374 // various internal mismatches. But selectionRect takes ints, and passing | 374 // various internal mismatches. But selectionRect takes ints, and passing |
375 // UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take | 375 // UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take |
376 // unsigneds, but that would cause many ripple effects, so for now we'll just | 376 // unsigneds, but that would cause many ripple effects, so for now we'll just |
377 // clamp our unsigned parameters to INT_MAX. | 377 // clamp our unsigned parameters to INT_MAX. |
378 ASSERT(end == UINT_MAX || end <= INT_MAX); | 378 DCHECK(end == UINT_MAX || end <= INT_MAX); |
379 ASSERT(start <= INT_MAX); | 379 DCHECK_LE(start, static_cast<unsigned>(INT_MAX)); |
380 start = std::min(start, static_cast<unsigned>(INT_MAX)); | 380 start = std::min(start, static_cast<unsigned>(INT_MAX)); |
381 end = std::min(end, static_cast<unsigned>(INT_MAX)); | 381 end = std::min(end, static_cast<unsigned>(INT_MAX)); |
382 | 382 |
383 bool hasCheckedBoxInRange = false; | 383 bool hasCheckedBoxInRange = false; |
384 | 384 |
385 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { | 385 for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { |
386 // Note: box->end() returns the index of the last character, not the index | 386 // Note: box->end() returns the index of the last character, not the index |
387 // past it | 387 // past it |
388 if (start <= box->start() && box->end() < end) { | 388 if (start <= box->start() && box->end() < end) { |
389 FloatRect r(box->frameRect()); | 389 FloatRect r(box->frameRect()); |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
483 unsigned start, | 483 unsigned start, |
484 unsigned end, | 484 unsigned end, |
485 bool useSelectionHeight) { | 485 bool useSelectionHeight) { |
486 // Work around signed/unsigned issues. This function takes unsigneds, and is | 486 // Work around signed/unsigned issues. This function takes unsigneds, and is |
487 // often passed UINT_MAX to mean "all the way to the end". InlineTextBox | 487 // often passed UINT_MAX to mean "all the way to the end". InlineTextBox |
488 // coordinates are unsigneds, so changing this function to take ints causes | 488 // coordinates are unsigneds, so changing this function to take ints causes |
489 // various internal mismatches. But selectionRect takes ints, and passing | 489 // various internal mismatches. But selectionRect takes ints, and passing |
490 // UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take | 490 // UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take |
491 // unsigneds, but that would cause many ripple effects, so for now we'll just | 491 // unsigneds, but that would cause many ripple effects, so for now we'll just |
492 // clamp our unsigned parameters to INT_MAX. | 492 // clamp our unsigned parameters to INT_MAX. |
493 ASSERT(end == UINT_MAX || end <= INT_MAX); | 493 DCHECK(end == UINT_MAX || end <= INT_MAX); |
494 ASSERT(start <= INT_MAX); | 494 DCHECK_LE(start, static_cast<unsigned>(INT_MAX)); |
495 start = std::min(start, static_cast<unsigned>(INT_MAX)); | 495 start = std::min(start, static_cast<unsigned>(INT_MAX)); |
496 end = std::min(end, static_cast<unsigned>(INT_MAX)); | 496 end = std::min(end, static_cast<unsigned>(INT_MAX)); |
497 | 497 |
498 const unsigned caretMinOffset = static_cast<unsigned>(this->caretMinOffset()); | 498 const unsigned caretMinOffset = static_cast<unsigned>(this->caretMinOffset()); |
499 const unsigned caretMaxOffset = static_cast<unsigned>(this->caretMaxOffset()); | 499 const unsigned caretMaxOffset = static_cast<unsigned>(this->caretMaxOffset()); |
500 | 500 |
501 // Narrows |start| and |end| into |caretMinOffset| and |careMaxOffset| | 501 // Narrows |start| and |end| into |caretMinOffset| and |careMaxOffset| |
502 // to ignore unrendered leading and trailing whitespaces. | 502 // to ignore unrendered leading and trailing whitespaces. |
503 start = std::min(std::max(caretMinOffset, start), caretMaxOffset); | 503 start = std::min(std::max(caretMinOffset, start), caretMaxOffset); |
504 end = std::min(std::max(caretMinOffset, end), caretMaxOffset); | 504 end = std::min(std::max(caretMinOffset, end), caretMaxOffset); |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
623 : 0; | 623 : 0; |
624 return box->getLineLayoutItem().createPositionWithAffinity( | 624 return box->getLineLayoutItem().createPositionWithAffinity( |
625 offset + textStartOffset, affinity); | 625 offset + textStartOffset, affinity); |
626 } | 626 } |
627 | 627 |
628 static PositionWithAffinity | 628 static PositionWithAffinity |
629 createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi( | 629 createPositionWithAffinityForBoxAfterAdjustingOffsetForBiDi( |
630 const InlineTextBox* box, | 630 const InlineTextBox* box, |
631 int offset, | 631 int offset, |
632 ShouldAffinityBeDownstream shouldAffinityBeDownstream) { | 632 ShouldAffinityBeDownstream shouldAffinityBeDownstream) { |
633 ASSERT(box); | 633 DCHECK(box); |
634 ASSERT(offset >= 0); | 634 DCHECK_GE(offset, 0); |
635 | 635 |
636 if (offset && static_cast<unsigned>(offset) < box->len()) | 636 if (offset && static_cast<unsigned>(offset) < box->len()) |
637 return createPositionWithAffinityForBox(box, box->start() + offset, | 637 return createPositionWithAffinityForBox(box, box->start() + offset, |
638 shouldAffinityBeDownstream); | 638 shouldAffinityBeDownstream); |
639 | 639 |
640 bool positionIsAtStartOfBox = !offset; | 640 bool positionIsAtStartOfBox = !offset; |
641 if (positionIsAtStartOfBox == box->isLeftToRightDirection()) { | 641 if (positionIsAtStartOfBox == box->isLeftToRightDirection()) { |
642 // offset is on the left edge | 642 // offset is on the left edge |
643 | 643 |
644 const InlineBox* prevBox = box->prevLeafChildIgnoringLineBreak(); | 644 const InlineBox* prevBox = box->prevLeafChildIgnoringLineBreak(); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
766 } | 766 } |
767 return createPositionWithAffinity(0); | 767 return createPositionWithAffinity(0); |
768 } | 768 } |
769 | 769 |
770 LayoutRect LayoutText::localCaretRect(InlineBox* inlineBox, | 770 LayoutRect LayoutText::localCaretRect(InlineBox* inlineBox, |
771 int caretOffset, | 771 int caretOffset, |
772 LayoutUnit* extraWidthToEndOfLine) { | 772 LayoutUnit* extraWidthToEndOfLine) { |
773 if (!inlineBox) | 773 if (!inlineBox) |
774 return LayoutRect(); | 774 return LayoutRect(); |
775 | 775 |
776 ASSERT(inlineBox->isInlineTextBox()); | 776 DCHECK(inlineBox->isInlineTextBox()); |
777 if (!inlineBox->isInlineTextBox()) | 777 if (!inlineBox->isInlineTextBox()) |
778 return LayoutRect(); | 778 return LayoutRect(); |
779 | 779 |
780 InlineTextBox* box = toInlineTextBox(inlineBox); | 780 InlineTextBox* box = toInlineTextBox(inlineBox); |
781 // Find an InlineBox before caret position, which is used to get caret height. | 781 // Find an InlineBox before caret position, which is used to get caret height. |
782 InlineBox* caretBox = box; | 782 InlineBox* caretBox = box; |
783 if (box->getLineLayoutItem().style(box->isFirstLineStyle())->direction() == | 783 if (box->getLineLayoutItem().style(box->isFirstLineStyle())->direction() == |
784 TextDirection::kLtr) { | 784 TextDirection::kLtr) { |
785 if (box->prevLeafChild() && caretOffset == 0) | 785 if (box->prevLeafChild() && caretOffset == 0) |
786 caretBox = box->prevLeafChild(); | 786 caretBox = box->prevLeafChild(); |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
878 FloatRect* glyphBoundsAccumulation) const { | 878 FloatRect* glyphBoundsAccumulation) const { |
879 if (style()->hasTextCombine() && isCombineText()) { | 879 if (style()->hasTextCombine() && isCombineText()) { |
880 const LayoutTextCombine* combineText = toLayoutTextCombine(this); | 880 const LayoutTextCombine* combineText = toLayoutTextCombine(this); |
881 if (combineText->isCombined()) | 881 if (combineText->isCombined()) |
882 return combineText->combinedTextWidth(f); | 882 return combineText->combinedTextWidth(f); |
883 } | 883 } |
884 | 884 |
885 TextRun run = | 885 TextRun run = |
886 constructTextRun(f, this, start, len, styleRef(), textDirection); | 886 constructTextRun(f, this, start, len, styleRef(), textDirection); |
887 run.setCharactersLength(textLength() - start); | 887 run.setCharactersLength(textLength() - start); |
888 ASSERT(run.charactersLength() >= run.length()); | 888 DCHECK_GE(run.charactersLength(), run.length()); |
889 run.setTabSize(!style()->collapseWhiteSpace(), style()->getTabSize()); | 889 run.setTabSize(!style()->collapseWhiteSpace(), style()->getTabSize()); |
890 run.setXPos(leadWidth + textWidthSoFar); | 890 run.setXPos(leadWidth + textWidthSoFar); |
891 | 891 |
892 FloatRect newGlyphBounds; | 892 FloatRect newGlyphBounds; |
893 float result = f.width(run, fallbackFonts, | 893 float result = f.width(run, fallbackFonts, |
894 glyphBoundsAccumulation ? &newGlyphBounds : nullptr); | 894 glyphBoundsAccumulation ? &newGlyphBounds : nullptr); |
895 if (glyphBoundsAccumulation) { | 895 if (glyphBoundsAccumulation) { |
896 newGlyphBounds.move(textWidthSoFar, 0); | 896 newGlyphBounds.move(textWidthSoFar, 0); |
897 glyphBoundsAccumulation->unite(newGlyphBounds); | 897 glyphBoundsAccumulation->unite(newGlyphBounds); |
898 } | 898 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
943 | 943 |
944 floatMinWidth = m_minWidth; | 944 floatMinWidth = m_minWidth; |
945 floatMaxWidth = m_maxWidth; | 945 floatMaxWidth = m_maxWidth; |
946 | 946 |
947 firstLineMinWidth = LayoutUnit(m_firstLineMinWidth); | 947 firstLineMinWidth = LayoutUnit(m_firstLineMinWidth); |
948 lastLineMinWidth = LayoutUnit(m_lastLineLineMinWidth); | 948 lastLineMinWidth = LayoutUnit(m_lastLineLineMinWidth); |
949 | 949 |
950 hasBreakableChar = m_hasBreakableChar; | 950 hasBreakableChar = m_hasBreakableChar; |
951 hasBreak = m_hasBreak; | 951 hasBreak = m_hasBreak; |
952 | 952 |
953 ASSERT(m_text); | 953 DCHECK(m_text); |
954 StringImpl& text = *m_text.impl(); | 954 StringImpl& text = *m_text.impl(); |
955 if (text[0] == spaceCharacter || | 955 if (text[0] == spaceCharacter || |
956 (text[0] == newlineCharacter && !style()->preserveNewline()) || | 956 (text[0] == newlineCharacter && !style()->preserveNewline()) || |
957 text[0] == tabulationCharacter) { | 957 text[0] == tabulationCharacter) { |
958 const Font& font = style()->font(); // FIXME: This ignores first-line. | 958 const Font& font = style()->font(); // FIXME: This ignores first-line. |
959 if (stripFrontSpaces) { | 959 if (stripFrontSpaces) { |
960 const UChar spaceChar = spaceCharacter; | 960 const UChar spaceChar = spaceCharacter; |
961 TextRun run = | 961 TextRun run = |
962 constructTextRun(font, &spaceChar, 1, styleRef(), direction); | 962 constructTextRun(font, &spaceChar, 1, styleRef(), direction); |
963 float spaceWidth = font.width(run); | 963 float spaceWidth = font.width(run); |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1129 } | 1129 } |
1130 if (const LayoutLocale* locale = style.getFontDescription().locale()) | 1130 if (const LayoutLocale* locale = style.getFontDescription().locale()) |
1131 return locale->localeWithBreakKeyword(mode); | 1131 return locale->localeWithBreakKeyword(mode); |
1132 return style.locale(); | 1132 return style.locale(); |
1133 } | 1133 } |
1134 | 1134 |
1135 void LayoutText::computePreferredLogicalWidths( | 1135 void LayoutText::computePreferredLogicalWidths( |
1136 float leadWidth, | 1136 float leadWidth, |
1137 HashSet<const SimpleFontData*>& fallbackFonts, | 1137 HashSet<const SimpleFontData*>& fallbackFonts, |
1138 FloatRect& glyphBounds) { | 1138 FloatRect& glyphBounds) { |
1139 ASSERT(m_hasTab || preferredLogicalWidthsDirty() || | 1139 DCHECK(m_hasTab || preferredLogicalWidthsDirty() || |
1140 !m_knownToHaveNoOverflowAndNoFallbackFonts); | 1140 !m_knownToHaveNoOverflowAndNoFallbackFonts); |
1141 | 1141 |
1142 m_minWidth = 0; | 1142 m_minWidth = 0; |
1143 m_maxWidth = 0; | 1143 m_maxWidth = 0; |
1144 m_firstLineMinWidth = 0; | 1144 m_firstLineMinWidth = 0; |
1145 m_lastLineLineMinWidth = 0; | 1145 m_lastLineLineMinWidth = 0; |
1146 | 1146 |
1147 if (isBR()) | 1147 if (isBR()) |
1148 return; | 1148 return; |
1149 | 1149 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1214 UChar c = uncheckedCharacterAt(i); | 1214 UChar c = uncheckedCharacterAt(i); |
1215 | 1215 |
1216 if (run) { | 1216 if (run) { |
1217 // Treat adjacent runs with the same resolved directionality | 1217 // Treat adjacent runs with the same resolved directionality |
1218 // (TextDirection as opposed to WTF::Unicode::Direction) as belonging | 1218 // (TextDirection as opposed to WTF::Unicode::Direction) as belonging |
1219 // to the same run to avoid breaking unnecessarily. | 1219 // to the same run to avoid breaking unnecessarily. |
1220 while (i >= run->stop() || | 1220 while (i >= run->stop() || |
1221 (run->next() && run->next()->direction() == run->direction())) | 1221 (run->next() && run->next()->direction() == run->direction())) |
1222 run = run->next(); | 1222 run = run->next(); |
1223 | 1223 |
1224 ASSERT(run); | 1224 DCHECK(run); |
1225 ASSERT(i <= run->stop()); | 1225 DCHECK_LE(i, run->stop()); |
1226 textDirection = run->direction(); | 1226 textDirection = run->direction(); |
1227 } | 1227 } |
1228 | 1228 |
1229 bool previousCharacterIsSpace = isSpace; | 1229 bool previousCharacterIsSpace = isSpace; |
1230 bool isNewline = false; | 1230 bool isNewline = false; |
1231 if (c == newlineCharacter) { | 1231 if (c == newlineCharacter) { |
1232 if (styleToUse.preserveNewline()) { | 1232 if (styleToUse.preserveNewline()) { |
1233 m_hasBreak = true; | 1233 m_hasBreak = true; |
1234 isNewline = true; | 1234 isNewline = true; |
1235 isSpace = false; | 1235 isSpace = false; |
(...skipping 21 matching lines...) Expand all Loading... |
1257 | 1257 |
1258 if (!ignoringSpaces && styleToUse.collapseWhiteSpace() && | 1258 if (!ignoringSpaces && styleToUse.collapseWhiteSpace() && |
1259 previousCharacterIsSpace && isSpace) | 1259 previousCharacterIsSpace && isSpace) |
1260 ignoringSpaces = true; | 1260 ignoringSpaces = true; |
1261 | 1261 |
1262 if (ignoringSpaces && !isSpace) | 1262 if (ignoringSpaces && !isSpace) |
1263 ignoringSpaces = false; | 1263 ignoringSpaces = false; |
1264 | 1264 |
1265 // Ignore spaces and soft hyphens | 1265 // Ignore spaces and soft hyphens |
1266 if (ignoringSpaces) { | 1266 if (ignoringSpaces) { |
1267 ASSERT(lastWordBoundary == i); | 1267 DCHECK_EQ(lastWordBoundary, i); |
1268 lastWordBoundary++; | 1268 lastWordBoundary++; |
1269 continue; | 1269 continue; |
1270 } | 1270 } |
1271 if (c == softHyphenCharacter && !disableSoftHyphen) { | 1271 if (c == softHyphenCharacter && !disableSoftHyphen) { |
1272 currMaxWidth += widthFromFont(f, lastWordBoundary, i - lastWordBoundary, | 1272 currMaxWidth += widthFromFont(f, lastWordBoundary, i - lastWordBoundary, |
1273 leadWidth, currMaxWidth, textDirection, | 1273 leadWidth, currMaxWidth, textDirection, |
1274 &fallbackFonts, &glyphBounds); | 1274 &fallbackFonts, &glyphBounds); |
1275 lastWordBoundary = i + 1; | 1275 lastWordBoundary = i + 1; |
1276 continue; | 1276 continue; |
1277 } | 1277 } |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1423 m_firstLineMinWidth = currMaxWidth; | 1423 m_firstLineMinWidth = currMaxWidth; |
1424 } | 1424 } |
1425 | 1425 |
1426 if (currMaxWidth > m_maxWidth) | 1426 if (currMaxWidth > m_maxWidth) |
1427 m_maxWidth = currMaxWidth; | 1427 m_maxWidth = currMaxWidth; |
1428 currMaxWidth = 0; | 1428 currMaxWidth = 0; |
1429 } else { | 1429 } else { |
1430 TextRun run = | 1430 TextRun run = |
1431 constructTextRun(f, this, i, 1, styleToUse, textDirection); | 1431 constructTextRun(f, this, i, 1, styleToUse, textDirection); |
1432 run.setCharactersLength(len - i); | 1432 run.setCharactersLength(len - i); |
1433 ASSERT(run.charactersLength() >= run.length()); | 1433 DCHECK_GE(run.charactersLength(), run.length()); |
1434 run.setTabSize(!style()->collapseWhiteSpace(), style()->getTabSize()); | 1434 run.setTabSize(!style()->collapseWhiteSpace(), style()->getTabSize()); |
1435 run.setXPos(leadWidth + currMaxWidth); | 1435 run.setXPos(leadWidth + currMaxWidth); |
1436 | 1436 |
1437 currMaxWidth += f.width(run); | 1437 currMaxWidth += f.width(run); |
1438 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1; | 1438 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1; |
1439 } | 1439 } |
1440 ASSERT(lastWordBoundary == i); | 1440 DCHECK_EQ(lastWordBoundary, i); |
1441 lastWordBoundary++; | 1441 lastWordBoundary++; |
1442 } | 1442 } |
1443 } | 1443 } |
1444 if (run) | 1444 if (run) |
1445 bidiResolver.runs().deleteRuns(); | 1445 bidiResolver.runs().deleteRuns(); |
1446 | 1446 |
1447 if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord)) | 1447 if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord)) |
1448 currMaxWidth += wordSpacing; | 1448 currMaxWidth += wordSpacing; |
1449 | 1449 |
1450 m_minWidth = std::max(currMinWidth, m_minWidth); | 1450 m_minWidth = std::max(currMinWidth, m_minWidth); |
(...skipping 11 matching lines...) Expand all Loading... |
1462 const SimpleFontData* fontData = f.primaryFont(); | 1462 const SimpleFontData* fontData = f.primaryFont(); |
1463 DCHECK(fontData); | 1463 DCHECK(fontData); |
1464 | 1464 |
1465 GlyphOverflow glyphOverflow; | 1465 GlyphOverflow glyphOverflow; |
1466 if (fontData) { | 1466 if (fontData) { |
1467 glyphOverflow.setFromBounds( | 1467 glyphOverflow.setFromBounds( |
1468 glyphBounds, fontData->getFontMetrics().floatAscent(), | 1468 glyphBounds, fontData->getFontMetrics().floatAscent(), |
1469 fontData->getFontMetrics().floatDescent(), m_maxWidth); | 1469 fontData->getFontMetrics().floatDescent(), m_maxWidth); |
1470 } | 1470 } |
1471 // We shouldn't change our mind once we "know". | 1471 // We shouldn't change our mind once we "know". |
1472 ASSERT(!m_knownToHaveNoOverflowAndNoFallbackFonts || | 1472 DCHECK(!m_knownToHaveNoOverflowAndNoFallbackFonts || |
1473 (fallbackFonts.isEmpty() && glyphOverflow.isApproximatelyZero())); | 1473 (fallbackFonts.isEmpty() && glyphOverflow.isApproximatelyZero())); |
1474 m_knownToHaveNoOverflowAndNoFallbackFonts = | 1474 m_knownToHaveNoOverflowAndNoFallbackFonts = |
1475 fallbackFonts.isEmpty() && glyphOverflow.isApproximatelyZero(); | 1475 fallbackFonts.isEmpty() && glyphOverflow.isApproximatelyZero(); |
1476 | 1476 |
1477 clearPreferredLogicalWidthsDirty(); | 1477 clearPreferredLogicalWidthsDirty(); |
1478 } | 1478 } |
1479 | 1479 |
1480 bool LayoutText::isAllCollapsibleWhitespace() const { | 1480 bool LayoutText::isAllCollapsibleWhitespace() const { |
1481 unsigned length = textLength(); | 1481 unsigned length = textLength(); |
1482 if (is8Bit()) { | 1482 if (is8Bit()) { |
(...skipping 20 matching lines...) Expand all Loading... |
1503 } | 1503 } |
1504 if (offsetInNode >= static_cast<int>(box->start()) && | 1504 if (offsetInNode >= static_cast<int>(box->start()) && |
1505 offsetInNode < static_cast<int>(box->start() + box->len())) | 1505 offsetInNode < static_cast<int>(box->start() + box->len())) |
1506 return true; | 1506 return true; |
1507 } | 1507 } |
1508 | 1508 |
1509 return false; | 1509 return false; |
1510 } | 1510 } |
1511 | 1511 |
1512 bool LayoutText::containsOnlyWhitespace(unsigned from, unsigned len) const { | 1512 bool LayoutText::containsOnlyWhitespace(unsigned from, unsigned len) const { |
1513 ASSERT(m_text); | 1513 DCHECK(m_text); |
1514 StringImpl& text = *m_text.impl(); | 1514 StringImpl& text = *m_text.impl(); |
1515 unsigned currPos; | 1515 unsigned currPos; |
1516 for (currPos = from; | 1516 for (currPos = from; |
1517 currPos < from + len && | 1517 currPos < from + len && |
1518 (text[currPos] == newlineCharacter || text[currPos] == spaceCharacter || | 1518 (text[currPos] == newlineCharacter || text[currPos] == spaceCharacter || |
1519 text[currPos] == tabulationCharacter); | 1519 text[currPos] == tabulationCharacter); |
1520 currPos++) { | 1520 currPos++) { |
1521 } | 1521 } |
1522 return currPos >= (from + len); | 1522 return currPos >= (from + len); |
1523 } | 1523 } |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1625 | 1625 |
1626 // Now we have to walk all of the clean lines and adjust their cached line | 1626 // Now we have to walk all of the clean lines and adjust their cached line |
1627 // break information to reflect our updated offsets. | 1627 // break information to reflect our updated offsets. |
1628 if (lastRootBox) | 1628 if (lastRootBox) |
1629 lastRootBox = lastRootBox->nextRootBox(); | 1629 lastRootBox = lastRootBox->nextRootBox(); |
1630 if (firstRootBox) { | 1630 if (firstRootBox) { |
1631 RootInlineBox* prev = firstRootBox->prevRootBox(); | 1631 RootInlineBox* prev = firstRootBox->prevRootBox(); |
1632 if (prev) | 1632 if (prev) |
1633 firstRootBox = prev; | 1633 firstRootBox = prev; |
1634 } else if (lastTextBox()) { | 1634 } else if (lastTextBox()) { |
1635 ASSERT(!lastRootBox); | 1635 DCHECK(!lastRootBox); |
1636 firstRootBox = &lastTextBox()->root(); | 1636 firstRootBox = &lastTextBox()->root(); |
1637 firstRootBox->markDirty(); | 1637 firstRootBox->markDirty(); |
1638 dirtiedLines = true; | 1638 dirtiedLines = true; |
1639 } | 1639 } |
1640 for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; | 1640 for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; |
1641 curr = curr->nextRootBox()) { | 1641 curr = curr->nextRootBox()) { |
1642 if (curr->lineBreakObj().isEqual(this) && curr->lineBreakPos() > end) | 1642 if (curr->lineBreakObj().isEqual(this) && curr->lineBreakPos() > end) |
1643 curr->setLineBreakPos(clampTo<int>(curr->lineBreakPos() + delta)); | 1643 curr->setLineBreakPos(clampTo<int>(curr->lineBreakPos() + delta)); |
1644 } | 1644 } |
1645 | 1645 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1703 case ETextTransform::kUppercase: | 1703 case ETextTransform::kUppercase: |
1704 text = text.upper(style->locale()); | 1704 text = text.upper(style->locale()); |
1705 break; | 1705 break; |
1706 case ETextTransform::kLowercase: | 1706 case ETextTransform::kLowercase: |
1707 text = text.lower(style->locale()); | 1707 text = text.lower(style->locale()); |
1708 break; | 1708 break; |
1709 } | 1709 } |
1710 } | 1710 } |
1711 | 1711 |
1712 void LayoutText::setTextInternal(PassRefPtr<StringImpl> text) { | 1712 void LayoutText::setTextInternal(PassRefPtr<StringImpl> text) { |
1713 ASSERT(text); | 1713 DCHECK(text); |
1714 m_text = std::move(text); | 1714 m_text = std::move(text); |
1715 | 1715 |
1716 if (style()) { | 1716 if (style()) { |
1717 applyTextTransform(style(), m_text, previousCharacter()); | 1717 applyTextTransform(style(), m_text, previousCharacter()); |
1718 | 1718 |
1719 // We use the same characters here as for list markers. | 1719 // We use the same characters here as for list markers. |
1720 // See the listMarkerText function in LayoutListMarker.cpp. | 1720 // See the listMarkerText function in LayoutListMarker.cpp. |
1721 switch (style()->textSecurity()) { | 1721 switch (style()->textSecurity()) { |
1722 case TSNONE: | 1722 case TSNONE: |
1723 break; | 1723 break; |
1724 case TSCIRCLE: | 1724 case TSCIRCLE: |
1725 secureText(whiteBulletCharacter); | 1725 secureText(whiteBulletCharacter); |
1726 break; | 1726 break; |
1727 case TSDISC: | 1727 case TSDISC: |
1728 secureText(bulletCharacter); | 1728 secureText(bulletCharacter); |
1729 break; | 1729 break; |
1730 case TSSQUARE: | 1730 case TSSQUARE: |
1731 secureText(blackSquareCharacter); | 1731 secureText(blackSquareCharacter); |
1732 } | 1732 } |
1733 } | 1733 } |
1734 | 1734 |
1735 ASSERT(m_text); | 1735 DCHECK(m_text); |
1736 ASSERT(!isBR() || (textLength() == 1 && m_text[0] == newlineCharacter)); | 1736 DCHECK(!isBR() || (textLength() == 1 && m_text[0] == newlineCharacter)); |
1737 } | 1737 } |
1738 | 1738 |
1739 void LayoutText::secureText(UChar mask) { | 1739 void LayoutText::secureText(UChar mask) { |
1740 if (!m_text.length()) | 1740 if (!m_text.length()) |
1741 return; | 1741 return; |
1742 | 1742 |
1743 int lastTypedCharacterOffsetToReveal = -1; | 1743 int lastTypedCharacterOffsetToReveal = -1; |
1744 UChar revealedText; | 1744 UChar revealedText; |
1745 SecureTextTimer* secureTextTimer = | 1745 SecureTextTimer* secureTextTimer = |
1746 gSecureTextTimers ? gSecureTextTimers->at(this) : 0; | 1746 gSecureTextTimers ? gSecureTextTimers->at(this) : 0; |
1747 if (secureTextTimer && secureTextTimer->isActive()) { | 1747 if (secureTextTimer && secureTextTimer->isActive()) { |
1748 lastTypedCharacterOffsetToReveal = | 1748 lastTypedCharacterOffsetToReveal = |
1749 secureTextTimer->lastTypedCharacterOffset(); | 1749 secureTextTimer->lastTypedCharacterOffset(); |
1750 if (lastTypedCharacterOffsetToReveal >= 0) | 1750 if (lastTypedCharacterOffsetToReveal >= 0) |
1751 revealedText = m_text[lastTypedCharacterOffsetToReveal]; | 1751 revealedText = m_text[lastTypedCharacterOffsetToReveal]; |
1752 } | 1752 } |
1753 | 1753 |
1754 m_text.fill(mask); | 1754 m_text.fill(mask); |
1755 if (lastTypedCharacterOffsetToReveal >= 0) { | 1755 if (lastTypedCharacterOffsetToReveal >= 0) { |
1756 m_text.replace(lastTypedCharacterOffsetToReveal, 1, | 1756 m_text.replace(lastTypedCharacterOffsetToReveal, 1, |
1757 String(&revealedText, 1)); | 1757 String(&revealedText, 1)); |
1758 // m_text may be updated later before timer fires. We invalidate the | 1758 // m_text may be updated later before timer fires. We invalidate the |
1759 // lastTypedCharacterOffset to avoid inconsistency. | 1759 // lastTypedCharacterOffset to avoid inconsistency. |
1760 secureTextTimer->invalidate(); | 1760 secureTextTimer->invalidate(); |
1761 } | 1761 } |
1762 } | 1762 } |
1763 | 1763 |
1764 void LayoutText::setText(PassRefPtr<StringImpl> text, bool force) { | 1764 void LayoutText::setText(PassRefPtr<StringImpl> text, bool force) { |
1765 ASSERT(text); | 1765 DCHECK(text); |
1766 | 1766 |
1767 if (!force && equal(m_text.impl(), text.get())) | 1767 if (!force && equal(m_text.impl(), text.get())) |
1768 return; | 1768 return; |
1769 | 1769 |
1770 setTextInternal(std::move(text)); | 1770 setTextInternal(std::move(text)); |
1771 // If preferredLogicalWidthsDirty() of an orphan child is true, | 1771 // If preferredLogicalWidthsDirty() of an orphan child is true, |
1772 // LayoutObjectChildList::insertChildNode() fails to set true to owner. | 1772 // LayoutObjectChildList::insertChildNode() fails to set true to owner. |
1773 // To avoid that, we call setNeedsLayoutAndPrefWidthsRecalc() only if this | 1773 // To avoid that, we call setNeedsLayoutAndPrefWidthsRecalc() only if this |
1774 // LayoutText has parent. | 1774 // LayoutText has parent. |
1775 if (parent()) | 1775 if (parent()) |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1855 fallbackFonts, glyphBounds); | 1855 fallbackFonts, glyphBounds); |
1856 } | 1856 } |
1857 | 1857 |
1858 float LayoutText::width(unsigned from, | 1858 float LayoutText::width(unsigned from, |
1859 unsigned len, | 1859 unsigned len, |
1860 const Font& f, | 1860 const Font& f, |
1861 LayoutUnit xPos, | 1861 LayoutUnit xPos, |
1862 TextDirection textDirection, | 1862 TextDirection textDirection, |
1863 HashSet<const SimpleFontData*>* fallbackFonts, | 1863 HashSet<const SimpleFontData*>* fallbackFonts, |
1864 FloatRect* glyphBounds) const { | 1864 FloatRect* glyphBounds) const { |
1865 ASSERT(from + len <= textLength()); | 1865 DCHECK_LE(from + len, textLength()); |
1866 if (!textLength()) | 1866 if (!textLength()) |
1867 return 0; | 1867 return 0; |
1868 | 1868 |
1869 const SimpleFontData* fontData = f.primaryFont(); | 1869 const SimpleFontData* fontData = f.primaryFont(); |
1870 DCHECK(fontData); | 1870 DCHECK(fontData); |
1871 if (!fontData) | 1871 if (!fontData) |
1872 return 0; | 1872 return 0; |
1873 | 1873 |
1874 float w; | 1874 float w; |
1875 if (&f == &style()->font()) { | 1875 if (&f == &style()->font()) { |
1876 if (!style()->preserveNewline() && !from && len == textLength()) { | 1876 if (!style()->preserveNewline() && !from && len == textLength()) { |
1877 if (fallbackFonts) { | 1877 if (fallbackFonts) { |
1878 ASSERT(glyphBounds); | 1878 DCHECK(glyphBounds); |
1879 if (preferredLogicalWidthsDirty() || | 1879 if (preferredLogicalWidthsDirty() || |
1880 !m_knownToHaveNoOverflowAndNoFallbackFonts) | 1880 !m_knownToHaveNoOverflowAndNoFallbackFonts) |
1881 const_cast<LayoutText*>(this)->computePreferredLogicalWidths( | 1881 const_cast<LayoutText*>(this)->computePreferredLogicalWidths( |
1882 0, *fallbackFonts, *glyphBounds); | 1882 0, *fallbackFonts, *glyphBounds); |
1883 else | 1883 else |
1884 *glyphBounds = | 1884 *glyphBounds = |
1885 FloatRect(0, -fontData->getFontMetrics().floatAscent(), | 1885 FloatRect(0, -fontData->getFontMetrics().floatAscent(), |
1886 m_maxWidth, fontData->getFontMetrics().floatHeight()); | 1886 m_maxWidth, fontData->getFontMetrics().floatHeight()); |
1887 w = m_maxWidth; | 1887 w = m_maxWidth; |
1888 } else { | 1888 } else { |
1889 w = maxLogicalWidth(); | 1889 w = maxLogicalWidth(); |
1890 } | 1890 } |
1891 } else { | 1891 } else { |
1892 w = widthFromFont(f, from, len, xPos.toFloat(), 0, textDirection, | 1892 w = widthFromFont(f, from, len, xPos.toFloat(), 0, textDirection, |
1893 fallbackFonts, glyphBounds); | 1893 fallbackFonts, glyphBounds); |
1894 } | 1894 } |
1895 } else { | 1895 } else { |
1896 TextRun run = | 1896 TextRun run = |
1897 constructTextRun(f, this, from, len, styleRef(), textDirection); | 1897 constructTextRun(f, this, from, len, styleRef(), textDirection); |
1898 run.setCharactersLength(textLength() - from); | 1898 run.setCharactersLength(textLength() - from); |
1899 ASSERT(run.charactersLength() >= run.length()); | 1899 DCHECK_GE(run.charactersLength(), run.length()); |
1900 | 1900 |
1901 run.setTabSize(!style()->collapseWhiteSpace(), style()->getTabSize()); | 1901 run.setTabSize(!style()->collapseWhiteSpace(), style()->getTabSize()); |
1902 run.setXPos(xPos.toFloat()); | 1902 run.setXPos(xPos.toFloat()); |
1903 w = f.width(run, fallbackFonts, glyphBounds); | 1903 w = f.width(run, fallbackFonts, glyphBounds); |
1904 } | 1904 } |
1905 | 1905 |
1906 return w; | 1906 return w; |
1907 } | 1907 } |
1908 | 1908 |
1909 LayoutRect LayoutText::linesBoundingBox() const { | 1909 LayoutRect LayoutText::linesBoundingBox() const { |
1910 LayoutRect result; | 1910 LayoutRect result; |
1911 | 1911 |
1912 ASSERT(!firstTextBox() == | 1912 DCHECK_EQ(!firstTextBox(), |
1913 !lastTextBox()); // Either both are null or both exist. | 1913 !lastTextBox()); // Either both are null or both exist. |
1914 if (firstTextBox() && lastTextBox()) { | 1914 if (firstTextBox() && lastTextBox()) { |
1915 // Return the width of the minimal left side and the maximal right side. | 1915 // Return the width of the minimal left side and the maximal right side. |
1916 float logicalLeftSide = 0; | 1916 float logicalLeftSide = 0; |
1917 float logicalRightSide = 0; | 1917 float logicalRightSide = 0; |
1918 for (InlineTextBox* curr = firstTextBox(); curr; | 1918 for (InlineTextBox* curr = firstTextBox(); curr; |
1919 curr = curr->nextTextBox()) { | 1919 curr = curr->nextTextBox()) { |
1920 if (curr == firstTextBox() || curr->logicalLeft() < logicalLeftSide) | 1920 if (curr == firstTextBox() || curr->logicalLeft() < logicalLeftSide) |
1921 logicalLeftSide = curr->logicalLeft().toFloat(); | 1921 logicalLeftSide = curr->logicalLeft().toFloat(); |
1922 if (curr == firstTextBox() || curr->logicalRight() > logicalRightSide) | 1922 if (curr == firstTextBox() || curr->logicalRight() > logicalRightSide) |
1923 logicalRightSide = curr->logicalRight().toFloat(); | 1923 logicalRightSide = curr->logicalRight().toFloat(); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1962 } | 1962 } |
1963 | 1963 |
1964 LayoutRect LayoutText::localVisualRect() const { | 1964 LayoutRect LayoutText::localVisualRect() const { |
1965 if (style()->visibility() != EVisibility::kVisible) | 1965 if (style()->visibility() != EVisibility::kVisible) |
1966 return LayoutRect(); | 1966 return LayoutRect(); |
1967 | 1967 |
1968 return unionRect(visualOverflowRect(), localSelectionRect()); | 1968 return unionRect(visualOverflowRect(), localSelectionRect()); |
1969 } | 1969 } |
1970 | 1970 |
1971 LayoutRect LayoutText::localSelectionRect() const { | 1971 LayoutRect LayoutText::localSelectionRect() const { |
1972 ASSERT(!needsLayout()); | 1972 DCHECK(!needsLayout()); |
1973 | 1973 |
1974 if (getSelectionState() == SelectionNone) | 1974 if (getSelectionState() == SelectionNone) |
1975 return LayoutRect(); | 1975 return LayoutRect(); |
1976 LayoutBlock* cb = containingBlock(); | 1976 LayoutBlock* cb = containingBlock(); |
1977 if (!cb) | 1977 if (!cb) |
1978 return LayoutRect(); | 1978 return LayoutRect(); |
1979 | 1979 |
1980 // Now calculate startPos and endPos for painting selection. | 1980 // Now calculate startPos and endPos for painting selection. |
1981 // We include a selection while endPos > 0 | 1981 // We include a selection while endPos > 0 |
1982 int startPos, endPos; | 1982 int startPos, endPos; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2033 return len; | 2033 return len; |
2034 } | 2034 } |
2035 | 2035 |
2036 #if DCHECK_IS_ON() | 2036 #if DCHECK_IS_ON() |
2037 | 2037 |
2038 void LayoutText::checkConsistency() const { | 2038 void LayoutText::checkConsistency() const { |
2039 #ifdef CHECK_CONSISTENCY | 2039 #ifdef CHECK_CONSISTENCY |
2040 const InlineTextBox* prev = nullptr; | 2040 const InlineTextBox* prev = nullptr; |
2041 for (const InlineTextBox* child = m_firstTextBox; child; | 2041 for (const InlineTextBox* child = m_firstTextBox; child; |
2042 child = child->nextTextBox()) { | 2042 child = child->nextTextBox()) { |
2043 ASSERT(child->getLineLayoutItem().isEqual(this)); | 2043 DCHECK(child->getLineLayoutItem().isEqual(this)); |
2044 ASSERT(child->prevTextBox() == prev); | 2044 DCHECK_EQ(child->prevTextBox(), prev); |
2045 prev = child; | 2045 prev = child; |
2046 } | 2046 } |
2047 ASSERT(prev == m_lastTextBox); | 2047 DCHECK_EQ(prev, m_lastTextBox); |
2048 #endif | 2048 #endif |
2049 } | 2049 } |
2050 | 2050 |
2051 #endif | 2051 #endif |
2052 | 2052 |
2053 void LayoutText::momentarilyRevealLastTypedCharacter( | 2053 void LayoutText::momentarilyRevealLastTypedCharacter( |
2054 unsigned lastTypedCharacterOffset) { | 2054 unsigned lastTypedCharacterOffset) { |
2055 if (!gSecureTextTimers) | 2055 if (!gSecureTextTimers) |
2056 gSecureTextTimers = new SecureTextTimerMap; | 2056 gSecureTextTimers = new SecureTextTimerMap; |
2057 | 2057 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2090 LayoutRect rect = LayoutRect( | 2090 LayoutRect rect = LayoutRect( |
2091 IntRect(firstRunX(), firstRunY(), linesBox.width(), linesBox.height())); | 2091 IntRect(firstRunX(), firstRunY(), linesBox.width(), linesBox.height())); |
2092 LayoutBlock* block = containingBlock(); | 2092 LayoutBlock* block = containingBlock(); |
2093 if (block && hasTextBoxes()) | 2093 if (block && hasTextBoxes()) |
2094 block->adjustChildDebugRect(rect); | 2094 block->adjustChildDebugRect(rect); |
2095 | 2095 |
2096 return rect; | 2096 return rect; |
2097 } | 2097 } |
2098 | 2098 |
2099 } // namespace blink | 2099 } // namespace blink |
OLD | NEW |