summaryrefslogtreecommitdiffstats
path: root/ui/gfx
diff options
context:
space:
mode:
authormsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-28 21:01:51 +0000
committermsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-28 21:01:51 +0000
commit6bc200d5ff9f3f466144d55504fa8d94755ee8ef (patch)
treefe5e65c48b44b166f8114c07c660aabe20cae4e2 /ui/gfx
parent9c2c1ea8f79577f6ba6b735ec3e9be573f3b5172 (diff)
downloadchromium_src-6bc200d5ff9f3f466144d55504fa8d94755ee8ef.zip
chromium_src-6bc200d5ff9f3f466144d55504fa8d94755ee8ef.tar.gz
chromium_src-6bc200d5ff9f3f466144d55504fa8d94755ee8ef.tar.bz2
Support obscured RenderTextWin passwords.
Use asterisks instead of actual text when obscured on Win. Use GetLayoutText() and index conversion in RenderTextWin. Add UpdateObscuredText(), called on Set[Text|Obscured](). Shortcut word breaking when obscured like RenderTextLinux. Expand upon tests and enable on Windows; refactoring; etc. Fix and simplify TextfieldViewsModelTest.Clipboard. ( It incorrectly expected obscured text to support word-break cursor movement ) ( It was disabled on Linux Aura for long-fixed http://crbug.com/97845 ) BUG=97845,138222 TEST=Views examples and ftp auth --enable-views-textfield password textfields work, etc. R=xji@chromium.org,asvitkine@chromium.org TBR=sky@chromium.org Review URL: https://chromiumcodereview.appspot.com/10821079 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@159335 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/gfx')
-rw-r--r--ui/gfx/render_text.cc22
-rw-r--r--ui/gfx/render_text.h24
-rw-r--r--ui/gfx/render_text_linux.cc38
-rw-r--r--ui/gfx/render_text_linux.h8
-rw-r--r--ui/gfx/render_text_mac.cc12
-rw-r--r--ui/gfx/render_text_mac.h4
-rw-r--r--ui/gfx/render_text_unittest.cc54
-rw-r--r--ui/gfx/render_text_win.cc103
-rw-r--r--ui/gfx/render_text_win.h4
9 files changed, 174 insertions, 95 deletions
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 4971dca..a8246c2 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -399,6 +399,7 @@ void RenderText::SetText(const string16& text) {
if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT)
text_direction_ = base::i18n::UNKNOWN_DIRECTION;
+ UpdateObscuredText();
ResetLayout();
}
@@ -444,6 +445,7 @@ void RenderText::SetObscured(bool obscured) {
if (obscured != obscured_) {
obscured_ = obscured;
cached_bounds_and_offset_valid_ = false;
+ UpdateObscuredText();
ResetLayout();
}
}
@@ -619,7 +621,7 @@ base::i18n::TextDirection RenderText::GetTextDirection() {
// Derive the direction from the display text, which differs from text()
// in the case of obscured (password) textfields.
text_direction_ =
- base::i18n::GetFirstStrongCharacterDirection(GetDisplayText());
+ base::i18n::GetFirstStrongCharacterDirection(GetLayoutText());
break;
case DIRECTIONALITY_FROM_UI:
text_direction_ = base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT :
@@ -795,12 +797,8 @@ void RenderText::SetSelectionModel(const SelectionModel& model) {
cached_bounds_and_offset_valid_ = false;
}
-string16 RenderText::GetDisplayText() const {
- if (!obscured_)
- return text_;
- size_t obscured_text_length =
- static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length()));
- return string16(obscured_text_length, kPasswordReplacementChar);
+const string16& RenderText::GetLayoutText() const {
+ return obscured() ? obscured_text_ : text();
}
void RenderText::ApplyCompositionAndSelectionStyles(
@@ -952,6 +950,16 @@ void RenderText::MoveCursorTo(size_t position, bool select) {
(cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
}
+void RenderText::UpdateObscuredText() {
+ if (!obscured_)
+ return;
+
+ const size_t obscured_text_length =
+ static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length()));
+ if (obscured_text_.length() != obscured_text_length)
+ obscured_text_.resize(obscured_text_length, kPasswordReplacementChar);
+}
+
void RenderText::UpdateCachedBoundsAndOffset() {
if (cached_bounds_and_offset_valid_)
return;
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 184cf56..3ba9f87 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -160,7 +160,7 @@ class UI_EXPORT RenderText {
void set_default_style(const StyleRange& style) { default_style_ = style; }
// In an obscured (password) field, all text is drawn as asterisks or bullets.
- bool is_obscured() const { return obscured_; }
+ bool obscured() const { return obscured_; }
void SetObscured(bool obscured);
const Rect& display_rect() const { return display_rect_; }
@@ -339,7 +339,13 @@ class UI_EXPORT RenderText {
// These bounds are in local coordinates, but may be outside the visible
// region if the text is longer than the textfield. Subsequent text, cursor,
// or bounds changes may invalidate returned values.
- virtual std::vector<Rect> GetSubstringBounds(ui::Range range) = 0;
+ virtual std::vector<Rect> GetSubstringBounds(const ui::Range& range) = 0;
+
+ // Convert between indices into |text_| and indices into |obscured_text_|,
+ // which differ when the text is obscured. Regardless of whether or not the
+ // text is obscured, the character (code point) offsets always match.
+ virtual size_t TextIndexToLayoutIndex(size_t index) const = 0;
+ virtual size_t LayoutIndexToTextIndex(size_t index) const = 0;
// Return true if cursor can appear in front of the character at |position|,
// which means it is a grapheme boundary or the first character in the text.
@@ -354,9 +360,8 @@ class UI_EXPORT RenderText {
// Draw the text.
virtual void DrawVisualText(Canvas* canvas) = 0;
- // Like text() except that it returns asterisks or bullets if this is an
- // obscured field.
- string16 GetDisplayText() const;
+ // Returns the text used for layout, which may be |obscured_text_|.
+ const string16& GetLayoutText() const;
// Apply composition style (underline) to composition range and selection
// style (foreground) to selection range.
@@ -400,7 +405,7 @@ class UI_EXPORT RenderText {
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, CustomDefaultStyle);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust);
- FRIEND_TEST_ALL_PREFIXES(RenderTextTest, PasswordCensorship);
+ FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ObscuredText);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, GraphemePositions);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, EdgeSelectionModels);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, OriginForDrawing);
@@ -412,6 +417,9 @@ class UI_EXPORT RenderText {
// it is a NO-OP.
void MoveCursorTo(size_t position, bool select);
+ // Updates |obscured_text_| if the text is obscured.
+ void UpdateObscuredText();
+
// Update the cached bounds and display offset to ensure that the current
// cursor is within the visible display area.
void UpdateCachedBoundsAndOffset();
@@ -473,8 +481,10 @@ class UI_EXPORT RenderText {
// The default text style.
StyleRange default_style_;
- // True if this is an obscured (password) field.
+ // A flag and the text to display for obscured (password) fields.
+ // Asterisks are used instead of the actual text glyphs when true.
bool obscured_;
+ string16 obscured_text_;
// Fade text head and/or tail, if text doesn't fit into |display_rect_|.
bool fade_head_;
diff --git a/ui/gfx/render_text_linux.cc b/ui/gfx/render_text_linux.cc
index 372e72b..bd9724f 100644
--- a/ui/gfx/render_text_linux.cc
+++ b/ui/gfx/render_text_linux.cc
@@ -181,7 +181,7 @@ SelectionModel RenderTextLinux::AdjacentCharSelectionModel(
SelectionModel RenderTextLinux::AdjacentWordSelectionModel(
const SelectionModel& selection,
VisualCursorDirection direction) {
- if (is_obscured())
+ if (obscured())
return EdgeSelectionModel(direction);
base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
@@ -223,7 +223,7 @@ void RenderTextLinux::GetGlyphBounds(size_t index,
*height = PANGO_PIXELS(pos.height);
}
-std::vector<Rect> RenderTextLinux::GetSubstringBounds(ui::Range range) {
+std::vector<Rect> RenderTextLinux::GetSubstringBounds(const ui::Range& range) {
DCHECK_LE(range.GetMax(), text().length());
if (range.is_empty())
@@ -235,6 +235,20 @@ std::vector<Rect> RenderTextLinux::GetSubstringBounds(ui::Range range) {
return CalculateSubstringBounds(range);
}
+size_t RenderTextLinux::TextIndexToLayoutIndex(size_t index) const {
+ DCHECK(layout_);
+ const ptrdiff_t offset = ui::UTF16IndexToOffset(text(), 0, index);
+ const char* layout_pointer = g_utf8_offset_to_pointer(layout_text_, offset);
+ return (layout_pointer - layout_text_);
+}
+
+size_t RenderTextLinux::LayoutIndexToTextIndex(size_t index) const {
+ DCHECK(layout_);
+ const char* layout_pointer = layout_text_ + index;
+ const long offset = g_utf8_pointer_to_offset(layout_text_, layout_pointer);
+ return ui::UTF16OffsetToIndex(text(), 0, offset);
+}
+
bool RenderTextLinux::IsCursorablePosition(size_t position) {
if (position == 0 && text().empty())
return true;
@@ -281,7 +295,7 @@ void RenderTextLinux::EnsureLayout() {
cairo_surface_destroy(surface);
SetupPangoLayoutWithFontDescription(layout_,
- GetDisplayText(),
+ GetLayoutText(),
font_list().GetFontDescriptionString(),
0,
GetTextDirection(),
@@ -490,24 +504,6 @@ SelectionModel RenderTextLinux::LastSelectionModelInsideRun(
return SelectionModel(caret, CURSOR_FORWARD);
}
-size_t RenderTextLinux::TextIndexToLayoutIndex(size_t text_index) const {
- // If the text is obscured then |layout_text_| is not the same as |text()|,
- // but whether or not the text is obscured, the character (code point) offset
- // in |layout_text_| is the same as that in |text()|.
- DCHECK(layout_);
- ptrdiff_t offset = ui::UTF16IndexToOffset(text(), 0, text_index);
- const char* layout_pointer = g_utf8_offset_to_pointer(layout_text_, offset);
- return (layout_pointer - layout_text_);
-}
-
-size_t RenderTextLinux::LayoutIndexToTextIndex(size_t layout_index) const {
- // See |TextIndexToLayoutIndex()|.
- DCHECK(layout_);
- const char* layout_pointer = layout_text_ + layout_index;
- long offset = g_utf8_pointer_to_offset(layout_text_, layout_pointer);
- return ui::UTF16OffsetToIndex(text(), 0, offset);
-}
-
std::vector<Rect> RenderTextLinux::CalculateSubstringBounds(ui::Range range) {
int* ranges;
int n_ranges;
diff --git a/ui/gfx/render_text_linux.h b/ui/gfx/render_text_linux.h
index 190f2dd..12f61ba 100644
--- a/ui/gfx/render_text_linux.h
+++ b/ui/gfx/render_text_linux.h
@@ -36,7 +36,9 @@ class RenderTextLinux : public RenderText {
virtual void GetGlyphBounds(size_t index,
ui::Range* xspan,
int* height) OVERRIDE;
- virtual std::vector<Rect> GetSubstringBounds(ui::Range range) OVERRIDE;
+ virtual std::vector<Rect> GetSubstringBounds(const ui::Range& range) OVERRIDE;
+ virtual size_t TextIndexToLayoutIndex(size_t index) const OVERRIDE;
+ virtual size_t LayoutIndexToTextIndex(size_t index) const OVERRIDE;
virtual bool IsCursorablePosition(size_t position) OVERRIDE;
virtual void ResetLayout() OVERRIDE;
virtual void EnsureLayout() OVERRIDE;
@@ -62,10 +64,6 @@ class RenderTextLinux : public RenderText {
PangoAttribute* pango_attr,
PangoAttrList* attrs);
- // Convert between indices into text() and indices into |layout_text_|.
- size_t TextIndexToLayoutIndex(size_t index) const;
- size_t LayoutIndexToTextIndex(size_t index) const;
-
// Calculate the visual bounds containing the logical substring within the
// given range.
std::vector<Rect> CalculateSubstringBounds(ui::Range range);
diff --git a/ui/gfx/render_text_mac.cc b/ui/gfx/render_text_mac.cc
index 3396ceb..9ee069f 100644
--- a/ui/gfx/render_text_mac.cc
+++ b/ui/gfx/render_text_mac.cc
@@ -74,11 +74,21 @@ void RenderTextMac::GetGlyphBounds(size_t index,
// TODO(asvitkine): Implement this. http://crbug.com/131618
}
-std::vector<Rect> RenderTextMac::GetSubstringBounds(ui::Range range) {
+std::vector<Rect> RenderTextMac::GetSubstringBounds(const ui::Range& range) {
// TODO(asvitkine): Implement this. http://crbug.com/131618
return std::vector<Rect>();
}
+size_t RenderTextMac::TextIndexToLayoutIndex(size_t index) const {
+ // TODO(asvitkine): Implement this. http://crbug.com/131618
+ return index;
+}
+
+size_t RenderTextMac::LayoutIndexToTextIndex(size_t index) const {
+ // TODO(asvitkine): Implement this. http://crbug.com/131618
+ return index;
+}
+
bool RenderTextMac::IsCursorablePosition(size_t position) {
// TODO(asvitkine): Implement this. http://crbug.com/131618
return false;
diff --git a/ui/gfx/render_text_mac.h b/ui/gfx/render_text_mac.h
index 97ac971..3bbfae4 100644
--- a/ui/gfx/render_text_mac.h
+++ b/ui/gfx/render_text_mac.h
@@ -42,7 +42,9 @@ class RenderTextMac : public RenderText {
virtual void GetGlyphBounds(size_t index,
ui::Range* xspan,
int* height) OVERRIDE;
- virtual std::vector<Rect> GetSubstringBounds(ui::Range range) OVERRIDE;
+ virtual std::vector<Rect> GetSubstringBounds(const ui::Range& range) OVERRIDE;
+ virtual size_t TextIndexToLayoutIndex(size_t index) const OVERRIDE;
+ virtual size_t LayoutIndexToTextIndex(size_t index) const OVERRIDE;
virtual bool IsCursorablePosition(size_t position) OVERRIDE;
virtual void ResetLayout() OVERRIDE;
virtual void EnsureLayout() OVERRIDE;
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 2680800..fa3a6e9 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -323,6 +323,7 @@ TEST_F(RenderTextTest, StyleRangesAdjust) {
void TestVisualCursorMotionInObscuredField(RenderText* render_text,
const string16& text,
bool select) {
+ ASSERT_TRUE(render_text->obscured());
render_text->SetText(text);
int len = text.length();
render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, select);
@@ -347,49 +348,70 @@ void TestVisualCursorMotionInObscuredField(RenderText* render_text,
EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
}
-TEST_F(RenderTextTest, PasswordCensorship) {
+TEST_F(RenderTextTest, ObscuredText) {
const string16 seuss = ASCIIToUTF16("hop on pop");
const string16 no_seuss = ASCIIToUTF16("**********");
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
- // GetObscuredText returns asterisks when the obscured bit is set.
+ // GetLayoutText() returns asterisks when the obscured bit is set.
render_text->SetText(seuss);
render_text->SetObscured(true);
EXPECT_EQ(seuss, render_text->text());
- EXPECT_EQ(no_seuss, render_text->GetDisplayText());
+ EXPECT_EQ(no_seuss, render_text->GetLayoutText());
render_text->SetObscured(false);
EXPECT_EQ(seuss, render_text->text());
- EXPECT_EQ(seuss, render_text->GetDisplayText());
+ EXPECT_EQ(seuss, render_text->GetLayoutText());
-// TODO(benrg): No Windows implementation yet.
-#if !defined(OS_WIN)
render_text->SetObscured(true);
// Surrogate pairs are counted as one code point.
const char16 invalid_surrogates[] = {0xDC00, 0xD800, 0};
render_text->SetText(invalid_surrogates);
- EXPECT_EQ(ASCIIToUTF16("**"), render_text->GetDisplayText());
+ EXPECT_EQ(ASCIIToUTF16("**"), render_text->GetLayoutText());
const char16 valid_surrogates[] = {0xD800, 0xDC00, 0};
render_text->SetText(valid_surrogates);
- EXPECT_EQ(ASCIIToUTF16("*"), render_text->GetDisplayText());
+ EXPECT_EQ(ASCIIToUTF16("*"), render_text->GetLayoutText());
EXPECT_EQ(0U, render_text->cursor_position());
render_text->MoveCursor(CHARACTER_BREAK, CURSOR_RIGHT, false);
EXPECT_EQ(2U, render_text->cursor_position());
- // Cursoring is independent of the underlying characters when the text is
- // obscured.
+ // Test index conversion and cursor validity with a valid surrogate pair.
+ EXPECT_EQ(0U, render_text->TextIndexToLayoutIndex(0U));
+ EXPECT_EQ(1U, render_text->TextIndexToLayoutIndex(1U));
+ EXPECT_EQ(1U, render_text->TextIndexToLayoutIndex(2U));
+ EXPECT_EQ(0U, render_text->LayoutIndexToTextIndex(0U));
+ EXPECT_EQ(2U, render_text->LayoutIndexToTextIndex(1U));
+ EXPECT_TRUE(render_text->IsCursorablePosition(0U));
+ EXPECT_FALSE(render_text->IsCursorablePosition(1U));
+ EXPECT_TRUE(render_text->IsCursorablePosition(2U));
+
+ // FindCursorPosition() should not return positions between a surrogate pair.
+ render_text->SetDisplayRect(Rect(0, 0, 20, 20));
+ EXPECT_EQ(render_text->FindCursorPosition(Point(0, 0)).caret_pos(), 0U);
+ EXPECT_EQ(render_text->FindCursorPosition(Point(20, 0)).caret_pos(), 2U);
+ for (int x = -1; x <= 20; ++x) {
+ SelectionModel selection = render_text->FindCursorPosition(Point(x, 0));
+ EXPECT_TRUE(selection.caret_pos() == 0U || selection.caret_pos() == 2U);
+ }
+
+ // GetGlyphBounds() should yield the entire string bounds for text index 0.
+ int height = 0;
+ ui::Range bounds;
+ render_text->GetGlyphBounds(0U, &bounds, &height);
+ EXPECT_EQ(render_text->GetStringSize().width(),
+ static_cast<int>(bounds.length()));
+
+ // Cursoring is independent of underlying characters when text is obscured.
const wchar_t* const texts[] = {
- L"hop on pop", // word boundaries
- L"ab \x5D0\x5D1" L"12", // bidi embedding level of 2
- L"\x5D0\x5D1" L"12", // RTL paragraph direction on Linux
- L"\x5D0\x5D1" // pure RTL
+ kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl,
+ L"hop on pop", // Check LTR word boundaries.
+ L"\x05d0\x05d1 \x05d0\x05d2 \x05d1\x05d2", // Check RTL word boundaries.
};
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(texts); ++i) {
+ for (size_t i = 0; i < arraysize(texts); ++i) {
string16 text = WideToUTF16(texts[i]);
TestVisualCursorMotionInObscuredField(render_text.get(), text, false);
TestVisualCursorMotionInObscuredField(render_text.get(), text, true);
}
-#endif // !defined(OS_WIN)
}
TEST_F(RenderTextTest, GetTextDirection) {
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index cd9c074..0b72ab6 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -12,6 +12,7 @@
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/win/windows_version.h"
+#include "ui/base/text/utf16_indexing.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font_fallback_win.h"
#include "ui/gfx/font_smoothing_win.h"
@@ -222,7 +223,7 @@ SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
DCHECK(SUCCEEDED(hr));
DCHECK_GE(trailing, 0);
position += run->range.start();
- size_t cursor = position + trailing;
+ const size_t cursor = LayoutIndexToTextIndex(position + trailing);
DCHECK_LE(cursor, text().length());
return SelectionModel(cursor, trailing ? CURSOR_BACKWARD : CURSOR_FORWARD);
}
@@ -231,8 +232,11 @@ std::vector<RenderText::FontSpan> RenderTextWin::GetFontSpansForTesting() {
EnsureLayout();
std::vector<RenderText::FontSpan> spans;
- for (size_t i = 0; i < runs_.size(); ++i)
- spans.push_back(RenderText::FontSpan(runs_[i]->font, runs_[i]->range));
+ for (size_t i = 0; i < runs_.size(); ++i) {
+ spans.push_back(RenderText::FontSpan(runs_[i]->font,
+ ui::Range(LayoutIndexToTextIndex(runs_[i]->range.start()),
+ LayoutIndexToTextIndex(runs_[i]->range.end()))));
+ }
return spans;
}
@@ -258,12 +262,12 @@ SelectionModel RenderTextWin::AdjacentCharSelectionModel(
bool forward_motion =
run->script_analysis.fRTL == (direction == CURSOR_LEFT);
if (forward_motion) {
- if (caret < run->range.end()) {
+ if (caret < LayoutIndexToTextIndex(run->range.end())) {
caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
return SelectionModel(caret, CURSOR_BACKWARD);
}
} else {
- if (caret > run->range.start()) {
+ if (caret > LayoutIndexToTextIndex(run->range.start())) {
caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
return SelectionModel(caret, CURSOR_FORWARD);
}
@@ -284,6 +288,9 @@ SelectionModel RenderTextWin::AdjacentCharSelectionModel(
SelectionModel RenderTextWin::AdjacentWordSelectionModel(
const SelectionModel& selection,
VisualCursorDirection direction) {
+ if (obscured())
+ return EdgeSelectionModel(direction);
+
base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
bool success = iter.Init();
DCHECK(success);
@@ -338,35 +345,37 @@ void RenderTextWin::SetSelectionModel(const SelectionModel& model) {
void RenderTextWin::GetGlyphBounds(size_t index,
ui::Range* xspan,
int* height) {
- size_t run_index =
+ const size_t run_index =
GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
DCHECK_LT(run_index, runs_.size());
internal::TextRun* run = runs_[run_index];
- xspan->set_start(GetGlyphXBoundary(run, index, false));
- xspan->set_end(GetGlyphXBoundary(run, index, true));
+ const size_t layout_index = TextIndexToLayoutIndex(index);
+ xspan->set_start(GetGlyphXBoundary(run, layout_index, false));
+ xspan->set_end(GetGlyphXBoundary(run, layout_index, true));
*height = run->font.GetHeight();
}
-std::vector<Rect> RenderTextWin::GetSubstringBounds(ui::Range range) {
+std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) {
DCHECK(!needs_layout_);
DCHECK(ui::Range(0, text().length()).Contains(range));
- Point display_offset(GetUpdatedDisplayOffset());
- HRESULT hr = 0;
+ ui::Range layout_range(TextIndexToLayoutIndex(range.start()),
+ TextIndexToLayoutIndex(range.end()));
+ DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range));
std::vector<Rect> bounds;
- if (range.is_empty())
+ if (layout_range.is_empty())
return bounds;
// Add a Rect for each run/selection intersection.
// TODO(msw): The bounds should probably not always be leading the range ends.
for (size_t i = 0; i < runs_.size(); ++i) {
internal::TextRun* run = runs_[visual_to_logical_[i]];
- ui::Range intersection = run->range.Intersect(range);
+ ui::Range intersection = run->range.Intersect(layout_range);
if (intersection.IsValid()) {
DCHECK(!intersection.is_reversed());
- ui::Range range(GetGlyphXBoundary(run, intersection.start(), false),
- GetGlyphXBoundary(run, intersection.end(), false));
- Rect rect(range.GetMin(), 0, range.length(), run->font.GetHeight());
+ ui::Range range_x(GetGlyphXBoundary(run, intersection.start(), false),
+ GetGlyphXBoundary(run, intersection.end(), false));
+ Rect rect(range_x.GetMin(), 0, range_x.length(), run->font.GetHeight());
// Center the rect vertically in the display area.
rect.Offset(0, (display_rect().height() - rect.height()) / 2);
rect.set_origin(ToViewPoint(rect.origin()));
@@ -381,22 +390,44 @@ std::vector<Rect> RenderTextWin::GetSubstringBounds(ui::Range range) {
return bounds;
}
+size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const {
+ if (!obscured())
+ return index;
+
+ DCHECK_LE(index, text().length());
+ const ptrdiff_t offset = ui::UTF16IndexToOffset(text(), 0, index);
+ DCHECK_GE(offset, 0);
+ DCHECK_LE(static_cast<size_t>(offset), GetLayoutText().length());
+ return static_cast<size_t>(offset);
+}
+
+size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const {
+ if (!obscured())
+ return index;
+
+ DCHECK_LE(index, GetLayoutText().length());
+ const size_t text_index = ui::UTF16OffsetToIndex(text(), 0, index);
+ DCHECK_LE(text_index, text().length());
+ return text_index;
+}
+
bool RenderTextWin::IsCursorablePosition(size_t position) {
if (position == 0 || position == text().length())
return true;
EnsureLayout();
- size_t run_index =
+ const size_t run_index =
GetRunContainingCaret(SelectionModel(position, CURSOR_FORWARD));
if (run_index >= runs_.size())
return false;
internal::TextRun* run = runs_[run_index];
- size_t start = run->range.start();
- if (position == start)
+ const size_t start = run->range.start();
+ const size_t layout_position = TextIndexToLayoutIndex(position);
+ if (layout_position == start)
return true;
- return run->logical_clusters[position - start] !=
- run->logical_clusters[position - start - 1];
+ return run->logical_clusters[layout_position - start] !=
+ run->logical_clusters[layout_position - start - 1];
}
void RenderTextWin::ResetLayout() {
@@ -483,18 +514,16 @@ void RenderTextWin::ItemizeLogicalText() {
if (text().empty())
return;
- const wchar_t* raw_text = text().c_str();
- const int text_length = text().length();
-
HRESULT hr = E_OUTOFMEMORY;
int script_items_count = 0;
std::vector<SCRIPT_ITEM> script_items;
+ const int text_length = GetLayoutText().length();
for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
// Derive the array of Uniscribe script items from the logical text.
// ScriptItemize always adds a terminal array item so that the length of the
// last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
script_items.resize(n);
- hr = ScriptItemize(raw_text,
+ hr = ScriptItemize(GetLayoutText().c_str(),
text_length,
n - 1,
&script_control_,
@@ -527,8 +556,8 @@ void RenderTextWin::ItemizeLogicalText() {
run->script_analysis = script_item->a;
// Find the range end and advance the structures as needed.
- int script_item_end = (script_item + 1)->iCharPos;
- int style_range_end = style->range.end();
+ const int script_item_end = (script_item + 1)->iCharPos;
+ const int style_range_end = TextIndexToLayoutIndex(style->range.end());
run_break = std::min(script_item_end, style_range_end);
if (script_item_end <= style_range_end)
script_item++;
@@ -599,7 +628,7 @@ void RenderTextWin::LayoutVisualText() {
void RenderTextWin::LayoutTextRun(internal::TextRun* run) {
const size_t run_length = run->range.length();
- const wchar_t* run_text = &(text()[run->range.start()]);
+ const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
Font original_font = run->font;
LinkedFontsIterator fonts(original_font);
bool tried_cached_font = false;
@@ -709,7 +738,7 @@ HRESULT RenderTextWin::ShapeTextRunWithFont(internal::TextRun* run,
HRESULT hr = E_OUTOFMEMORY;
const size_t run_length = run->range.length();
- const wchar_t* run_text = &(text()[run->range.start()]);
+ const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
// Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
while (hr == E_OUTOFMEMORY && max_glyphs < kMaxGlyphs) {
@@ -738,7 +767,7 @@ int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const {
properties.cBytes = sizeof(properties);
ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties);
- const wchar_t* run_text = &(text()[run->range.start()]);
+ const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
for (size_t char_index = 0; char_index < run->range.length(); ++char_index) {
const int glyph_index = run->logical_clusters[char_index];
DCHECK_GE(glyph_index, 0);
@@ -767,11 +796,11 @@ int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const {
size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const {
DCHECK(!needs_layout_);
- size_t position = caret.caret_pos();
+ size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos());
LogicalCursorDirection affinity = caret.caret_affinity();
size_t run = 0;
for (; run < runs_.size(); ++run)
- if (RangeContainsCaret(runs_[run]->range, position, affinity))
+ if (RangeContainsCaret(runs_[run]->range, layout_position, affinity))
break;
return run;
}
@@ -789,14 +818,16 @@ size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
const internal::TextRun* run) {
- size_t cursor = IndexOfAdjacentGrapheme(run->range.start(), CURSOR_FORWARD);
- return SelectionModel(cursor, CURSOR_BACKWARD);
+ size_t position = LayoutIndexToTextIndex(run->range.start());
+ position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD);
+ return SelectionModel(position, CURSOR_BACKWARD);
}
SelectionModel RenderTextWin::LastSelectionModelInsideRun(
const internal::TextRun* run) {
- size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD);
- return SelectionModel(caret, CURSOR_FORWARD);
+ size_t position = LayoutIndexToTextIndex(run->range.end());
+ position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
+ return SelectionModel(position, CURSOR_FORWARD);
}
RenderText* RenderText::CreateInstance() {
diff --git a/ui/gfx/render_text_win.h b/ui/gfx/render_text_win.h
index a33dbe2..df33b64 100644
--- a/ui/gfx/render_text_win.h
+++ b/ui/gfx/render_text_win.h
@@ -83,7 +83,9 @@ class RenderTextWin : public RenderText {
virtual void GetGlyphBounds(size_t index,
ui::Range* xspan,
int* height) OVERRIDE;
- virtual std::vector<Rect> GetSubstringBounds(ui::Range range) OVERRIDE;
+ virtual std::vector<Rect> GetSubstringBounds(const ui::Range& range) OVERRIDE;
+ virtual size_t TextIndexToLayoutIndex(size_t index) const OVERRIDE;
+ virtual size_t LayoutIndexToTextIndex(size_t index) const OVERRIDE;
virtual bool IsCursorablePosition(size_t position) OVERRIDE;
virtual void ResetLayout() OVERRIDE;
virtual void EnsureLayout() OVERRIDE;