| Index: ui/gfx/render_text_win.cc
|
| diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
|
| index 0469c0f5b7a61a6770457d330cb22d6e1c09401b..05be785e4a53921c4225a43554f869377b445544 100644
|
| --- a/ui/gfx/render_text_win.cc
|
| +++ b/ui/gfx/render_text_win.cc
|
| @@ -22,14 +22,18 @@ namespace gfx {
|
|
|
| namespace {
|
|
|
| -// The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes.
|
| -// TODO(msw): Review memory use/failure? Max string length? Alternate approach?
|
| -const int kGuessItems = 100;
|
| -const int kMaxItems = 10000;
|
| +// The maximum length of text supported for Uniscribe layout and display.
|
| +// This empirically chosen value should prevent major performance degradations.
|
| +// TODO(msw): Support longer text, partial layout/painting, etc.
|
| +const size_t kMaxUniscribeTextLength = 10000;
|
|
|
| -// The maximum supported number of Uniscribe glyphs; a glyph is 1 word.
|
| -// TODO(msw): Review memory use/failure? Max string length? Alternate approach?
|
| -const int kMaxGlyphs = 100000;
|
| +// The initial guess and maximum supported number of runs; arbitrary values.
|
| +// TODO(msw): Support more runs, determine a better initial guess, etc.
|
| +const int kGuessRuns = 100;
|
| +const size_t kMaxRuns = 10000;
|
| +
|
| +// The maximum number of glyphs per run; ScriptShape fails on larger values.
|
| +const size_t kMaxGlyphs = 65535;
|
|
|
| // Callback to |EnumEnhMetaFile()| to intercept font creation.
|
| int CALLBACK MetaFileEnumProc(HDC hdc,
|
| @@ -181,6 +185,8 @@ RenderTextWin::RenderTextWin()
|
| : RenderText(),
|
| common_baseline_(0),
|
| needs_layout_(false) {
|
| + set_truncate_length(kMaxUniscribeTextLength);
|
| +
|
| memset(&script_control_, 0, sizeof(script_control_));
|
| memset(&script_state_, 0, sizeof(script_state_));
|
|
|
| @@ -208,7 +214,7 @@ SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
|
| // Find the run that contains the point and adjust the argument location.
|
| int x = ToTextPoint(point).x();
|
| size_t run_index = GetRunContainingXCoord(x);
|
| - if (run_index == runs_.size())
|
| + if (run_index >= runs_.size())
|
| return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
|
| internal::TextRun* run = runs_[run_index];
|
|
|
| @@ -345,7 +351,9 @@ void RenderTextWin::SetSelectionModel(const SelectionModel& model) {
|
| ui::Range RenderTextWin::GetGlyphBounds(size_t index) {
|
| const size_t run_index =
|
| GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
|
| - DCHECK_LT(run_index, runs_.size());
|
| + // Return edge bounds if the index is invalid or beyond the layout text size.
|
| + if (run_index >= runs_.size())
|
| + return ui::Range(string_size_.width());
|
| internal::TextRun* run = runs_[run_index];
|
| const size_t layout_index = TextIndexToLayoutIndex(index);
|
| return ui::Range(GetGlyphXBoundary(run, layout_index, false),
|
| @@ -386,14 +394,11 @@ std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) {
|
| }
|
|
|
| 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);
|
| + ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index;
|
| + CHECK_GE(i, 0);
|
| + // Clamp layout indices to the length of the text actually used for layout.
|
| + return std::min<size_t>(GetLayoutText().length(), i);
|
| }
|
|
|
| size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const {
|
| @@ -496,23 +501,20 @@ void RenderTextWin::ItemizeLogicalText() {
|
| HRESULT hr = E_OUTOFMEMORY;
|
| int script_items_count = 0;
|
| std::vector<SCRIPT_ITEM> script_items;
|
| - const size_t text_length = GetLayoutText().length();
|
| - for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
|
| + const size_t layout_text_length = GetLayoutText().length();
|
| + // Ensure that |kMaxRuns| is attempted and the loop terminates afterward.
|
| + for (size_t runs = kGuessRuns; hr == E_OUTOFMEMORY && runs <= kMaxRuns;
|
| + runs = std::max(runs + 1, std::min(runs * 2, kMaxRuns))) {
|
| // 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(GetLayoutText().c_str(),
|
| - text_length,
|
| - n - 1,
|
| - &script_control_,
|
| - &script_state_,
|
| - &script_items[0],
|
| - &script_items_count);
|
| + // 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(runs);
|
| + hr = ScriptItemize(GetLayoutText().c_str(), layout_text_length,
|
| + runs - 1, &script_control_, &script_state_,
|
| + &script_items[0], &script_items_count);
|
| }
|
| DCHECK(SUCCEEDED(hr));
|
| -
|
| - if (script_items_count <= 0)
|
| + if (!SUCCEEDED(hr) || script_items_count <= 0)
|
| return;
|
|
|
| // Temporarily apply composition underlines and selection colors.
|
| @@ -522,7 +524,7 @@ void RenderTextWin::ItemizeLogicalText() {
|
| // TODO(msw): Only break for bold/italic, not color etc. See TextRun comment.
|
| internal::StyleIterator style(colors(), styles());
|
| SCRIPT_ITEM* script_item = &script_items[0];
|
| - const size_t layout_text_length = GetLayoutText().length();
|
| + const size_t max_run_length = kMaxGlyphs / 2;
|
| for (size_t run_break = 0; run_break < layout_text_length;) {
|
| internal::TextRun* run = new internal::TextRun();
|
| run->range.set_start(run_break);
|
| @@ -541,6 +543,9 @@ void RenderTextWin::ItemizeLogicalText() {
|
| const size_t script_item_break = (script_item + 1)->iCharPos;
|
| run_break = std::min(script_item_break,
|
| TextIndexToLayoutIndex(style.GetRange().end()));
|
| + // Clamp run lengths to avoid exceeding the maximum supported glyph count.
|
| + if ((run_break - run->range.start()) > max_run_length)
|
| + run_break = run->range.start() + max_run_length;
|
| style.UpdatePosition(LayoutIndexToTextIndex(run_break));
|
| if (script_item_break == run_break)
|
| script_item++;
|
| @@ -740,23 +745,19 @@ HRESULT RenderTextWin::ShapeTextRunWithFont(internal::TextRun* run,
|
| HRESULT hr = E_OUTOFMEMORY;
|
| const size_t run_length = run->range.length();
|
| const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
|
| - // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
|
| + // Guess the expected number of glyphs from the length of the run.
|
| + // MSDN suggests this at 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) {
|
| + while (hr == E_OUTOFMEMORY && max_glyphs <= kMaxGlyphs) {
|
| run->glyph_count = 0;
|
| run->glyphs.reset(new WORD[max_glyphs]);
|
| run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
|
| - hr = ScriptShape(cached_hdc_,
|
| - &run->script_cache,
|
| - run_text,
|
| - run_length,
|
| - max_glyphs,
|
| - &run->script_analysis,
|
| - run->glyphs.get(),
|
| - run->logical_clusters.get(),
|
| - run->visible_attributes.get(),
|
| + hr = ScriptShape(cached_hdc_, &run->script_cache, run_text, run_length,
|
| + max_glyphs, &run->script_analysis, run->glyphs.get(),
|
| + run->logical_clusters.get(), run->visible_attributes.get(),
|
| &run->glyph_count);
|
| - max_glyphs *= 2;
|
| + // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward.
|
| + max_glyphs = std::max(max_glyphs + 1, std::min(max_glyphs * 2, kMaxGlyphs));
|
| }
|
| return hr;
|
| }
|
|
|