15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file implements utility functions for eliding and formatting UI text.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note that several of the functions declared in text_elider.h are implemented
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in this file using helper classes in an unnamed namespace.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/text/text_elider.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/break_iterator.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/char_iterator.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/rtl.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string_util.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h"
23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
27ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/common/unicode/rbbi.h"
28ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/common/unicode/uloc.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/font.h"
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "url/gurl.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ui {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// U+2026 in utf8
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kEllipsis[] = "\xE2\x80\xA6";
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst char16 kEllipsisUTF16[] = { 0x2026, 0 };
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char16 kForwardSlash = '/';
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Helper class to split + elide text, while respecting UTF16 surrogate pairs.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class StringSlicer {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StringSlicer(const string16& text,
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               const string16& ellipsis,
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               bool elide_in_middle)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : text_(text),
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ellipsis_(ellipsis),
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        elide_in_middle_(elide_in_middle) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Cuts |text_| to be |length| characters long. If |elide_in_middle_| is true,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the middle of the string is removed to leave equal-length pieces from the
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // beginning and end of the string; otherwise, the end of the string is
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // removed and only the beginning remains. If |insert_ellipsis| is true,
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // then an ellipsis character will be inserted at the cut point.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 CutString(size_t length, bool insert_ellipsis) {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16 ellipsis_text = insert_ellipsis ? ellipsis_ : string16();
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!elide_in_middle_)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return text_.substr(0, FindValidBoundaryBefore(length)) + ellipsis_text;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We put the extra character, if any, before the cut.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const size_t half_length = length / 2;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const size_t prefix_length = FindValidBoundaryBefore(length - half_length);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const size_t suffix_start_guess = text_.length() - half_length;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const size_t suffix_length =
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        half_length - (suffix_start_guess - suffix_start);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return text_.substr(0, prefix_length) + ellipsis_text +
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           text_.substr(suffix_start, suffix_length);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns a valid cut boundary at or before |index|.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t FindValidBoundaryBefore(size_t index) const {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(index, text_.length());
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (index != text_.length())
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      U16_SET_CP_START(text_.data(), 0, index);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return index;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Returns a valid cut boundary at or after |index|.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t FindValidBoundaryAfter(size_t index) const {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(index, text_.length());
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (index != text_.length())
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      U16_SET_CP_LIMIT(text_.data(), 0, index, text_.length());
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return index;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The text to be sliced.
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16& text_;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ellipsis string to use.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16& ellipsis_;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If true, the middle of the string will be elided.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool elide_in_middle_;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(StringSlicer);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Build a path from the first |num_components| elements in |path_elements|.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Prepends |path_prefix|, appends |filename|, inserts ellipsis if appropriate.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 BuildPathFromComponents(const string16& path_prefix,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const std::vector<string16>& path_elements,
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const string16& filename,
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 size_t num_components) {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add the initial elements of the path.
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 path = path_prefix;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Build path from first |num_components| elements.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t j = 0; j < num_components; ++j)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path += path_elements[j] + kForwardSlash;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add |filename|, ellipsis if necessary.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (num_components != (path_elements.size() - 1))
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    path += UTF8ToUTF16(kEllipsis) + kForwardSlash;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  path += filename;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return path;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Takes a prefix (Domain, or Domain+subdomain) and a collection of path
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// components and elides if possible. Returns a string containing the longest
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// possible elided path, or an empty string if elision is not possible.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ElideComponentizedPath(const string16& url_path_prefix,
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                const std::vector<string16>& url_path_elements,
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                const string16& url_filename,
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                const string16& url_query,
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                const gfx::Font& font,
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                int available_pixel_width) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t url_path_number_of_elements = url_path_elements.size();
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(url_path_number_of_elements);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = url_path_number_of_elements - 1; i > 0; --i) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 elided_path = BuildPathFromComponents(url_path_prefix,
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        url_path_elements, url_filename, i);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (available_pixel_width >= font.GetStringWidth(elided_path))
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ElideText(elided_path + url_query,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       font, available_pixel_width, ELIDE_AT_END);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return string16();
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ElideEmail(const string16& email,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const gfx::Font& font,
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    int available_pixel_width) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (font.GetStringWidth(email) <= available_pixel_width)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return email;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Split the email into its local-part (username) and domain-part. The email
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // spec technically allows for @ symbols in the local-part (username) of the
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // email under some special requirements. It is guaranteed that there is no @
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // symbol in the domain part of the email however so splitting at the last @
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // symbol is safe.
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t split_index = email.find_last_of('@');
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(split_index, string16::npos);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 username = email.substr(0, split_index);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 domain = email.substr(split_index + 1);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!username.empty());
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!domain.empty());
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Subtract the @ symbol from the available width as it is mandatory.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16 kAtSignUTF16 = ASCIIToUTF16("@");
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  available_pixel_width -= font.GetStringWidth(kAtSignUTF16);
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check whether eliding the domain is necessary: if eliding the username
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is sufficient, the domain will not be elided.
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int full_username_width = font.GetStringWidth(username);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int available_domain_width =
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      available_pixel_width -
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::min(full_username_width,
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               font.GetStringWidth(username.substr(0, 1) + kEllipsisUTF16));
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (font.GetStringWidth(domain) > available_domain_width) {
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Elide the domain so that it only takes half of the available width.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Should the username not need all the width available in its half, the
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // domain will occupy the leftover width.
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If |desired_domain_width| is greater than |available_domain_width|: the
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // minimal username elision allowed by the specifications will not fit; thus
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // |desired_domain_width| must be <= |available_domain_width| at all cost.
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int desired_domain_width =
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::min(available_domain_width,
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 std::max(available_pixel_width - full_username_width,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          available_pixel_width / 2));
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    domain = ElideText(domain, font, desired_domain_width, ELIDE_IN_MIDDLE);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Failing to elide the domain such that at least one character remains
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // (other than the ellipsis itself) remains: return a single ellipsis.
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (domain.length() <= 1U)
193eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return string16(kEllipsisUTF16);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Fit the username in the remaining width (at this point the elided username
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is guaranteed to fit with at least one character remaining given all the
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // precautions taken earlier).
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  username = ElideText(username,
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       font,
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       available_pixel_width - font.GetStringWidth(domain),
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       ELIDE_AT_END);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return username + kAtSignUTF16 + domain;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(pkasting): http://crbug.com/77883 This whole function gets
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// kerning/ligatures/etc. issues potentially wrong by assuming that the width of
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a rendered string is always the sum of the widths of its substrings.  Also I
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// suspect it could be made simpler.
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ElideUrl(const GURL& url,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const gfx::Font& font,
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  int available_pixel_width,
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const std::string& languages) {
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get a formatted string and corresponding parsing of the url.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_parse::Parsed parsed;
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16 url_string =
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::FormatUrl(url, languages, net::kFormatUrlOmitAll,
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     net::UnescapeRule::SPACES, &parsed, NULL, NULL);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (available_pixel_width <= 0)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return url_string;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If non-standard, return plain eliding.
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!url.IsStandard())
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ElideText(url_string, font, available_pixel_width, ELIDE_AT_END);
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now start eliding url_string to fit within available pixel width.
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Fist pass - check to see whether entire url_string fits.
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int pixel_width_url_string = font.GetStringWidth(url_string);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (available_pixel_width >= pixel_width_url_string)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return url_string;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the path substring, including query and reference.
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t path_start_index = parsed.path.begin;
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t path_len = parsed.path.len;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 url_path_query_etc = url_string.substr(path_start_index);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 url_path = url_string.substr(path_start_index, path_len);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Return general elided text if url minus the query fits.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16 url_minus_query =
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_string.substr(0, path_start_index + path_len);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (available_pixel_width >= font.GetStringWidth(url_minus_query))
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ElideText(url_string, font, available_pixel_width, ELIDE_AT_END);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get Host.
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 url_host = UTF8ToUTF16(url.host());
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get domain and registry information from the URL.
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 url_domain = UTF8ToUTF16(
250a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      net::registry_controlled_domains::GetDomainAndRegistry(
251a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)          url, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES));
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url_domain.empty())
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_domain = url_host;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add port if required.
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!url.port().empty()) {
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_host += UTF8ToUTF16(":" + url.port());
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_domain += UTF8ToUTF16(":" + url.port());
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get sub domain.
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 url_subdomain;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t domain_start_index = url_host.find(url_domain);
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (domain_start_index != string16::npos)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_subdomain = url_host.substr(0, domain_start_index);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16 kWwwPrefix = UTF8ToUTF16("www.");
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((url_subdomain == kWwwPrefix || url_subdomain.empty() ||
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url.SchemeIsFile())) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_subdomain.clear();
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If this is a file type, the path is now defined as everything after ":".
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For example, "C:/aa/aa/bb", the path is "/aa/bb/cc". Interesting, the
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // domain is now C: - this is a nice hack for eliding to work pleasantly.
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url.SchemeIsFile()) {
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Split the path string using ":"
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<string16> file_path_split;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::SplitString(url_path, ':', &file_path_split);
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (file_path_split.size() > 1) {  // File is of type "file:///C:/.."
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_host.clear();
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_domain.clear();
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_subdomain.clear();
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const string16 kColon = UTF8ToUTF16(":");
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_host = url_domain = file_path_split.at(0).substr(1) + kColon;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_path_query_etc = url_path = file_path_split.at(1);
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Second Pass - remove scheme - the rest fits.
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int pixel_width_url_host = font.GetStringWidth(url_host);
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int pixel_width_url_path = font.GetStringWidth(url_path_query_etc);
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (available_pixel_width >=
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pixel_width_url_host + pixel_width_url_path)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return url_host + url_path_query_etc;
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Third Pass: Subdomain, domain and entire path fits.
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int pixel_width_url_domain = font.GetStringWidth(url_domain);
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int pixel_width_url_subdomain = font.GetStringWidth(url_subdomain);
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (available_pixel_width >=
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pixel_width_url_subdomain + pixel_width_url_domain +
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pixel_width_url_path)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return url_subdomain + url_domain + url_path_query_etc;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Query element.
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 url_query;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kPixelWidthDotsTrailer =
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      font.GetStringWidth(UTF8ToUTF16(kEllipsis));
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (parsed.query.is_nonempty()) {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_query = UTF8ToUTF16("?") + url_string.substr(parsed.query.begin);
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (available_pixel_width >= (pixel_width_url_subdomain +
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pixel_width_url_domain + pixel_width_url_path -
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        font.GetStringWidth(url_query))) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ElideText(url_subdomain + url_domain + url_path_query_etc,
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       font, available_pixel_width, ELIDE_AT_END);
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Parse url_path using '/'.
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<string16> url_path_elements;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitString(url_path, kForwardSlash, &url_path_elements);
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get filename - note that for a path ending with /
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // such as www.google.com/intl/ads/, the file name is ads/.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t url_path_number_of_elements = url_path_elements.size();
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(url_path_number_of_elements != 0);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 url_filename;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((url_path_elements.at(url_path_number_of_elements - 1)).length() > 0) {
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_filename = *(url_path_elements.end() - 1);
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (url_path_number_of_elements > 1) {  // Path ends with a '/'.
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_filename = url_path_elements.at(url_path_number_of_elements - 2) +
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kForwardSlash;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_path_number_of_elements--;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(url_path_number_of_elements != 0);
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t kMaxNumberOfUrlPathElementsAllowed = 1024;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url_path_number_of_elements <= 1 ||
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_path_number_of_elements > kMaxNumberOfUrlPathElementsAllowed) {
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No path to elide, or too long of a path (could overflow in loop below)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Just elide this as a text string.
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ElideText(url_subdomain + url_domain + url_path_query_etc, font,
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     available_pixel_width, ELIDE_AT_END);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start eliding the path and replacing elements by ".../".
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16 kEllipsisAndSlash = UTF8ToUTF16(kEllipsis) + kForwardSlash;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int pixel_width_ellipsis_slash = font.GetStringWidth(kEllipsisAndSlash);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check with both subdomain and domain.
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 elided_path =
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ElideComponentizedPath(url_subdomain + url_domain, url_path_elements,
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             url_filename, url_query, font,
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             available_pixel_width);
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!elided_path.empty())
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return elided_path;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check with only domain.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If a subdomain is present, add an ellipsis before domain.
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This is added only if the subdomain pixel width is larger than
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the pixel width of kEllipsis. Otherwise, subdomain remains,
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // which means that this case has been resolved earlier.
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 url_elided_domain = url_subdomain + url_domain;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pixel_width_url_subdomain > kPixelWidthDotsTrailer) {
365eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (!url_subdomain.empty())
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_elided_domain = kEllipsisAndSlash[0] + url_domain;
367eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    else
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_elided_domain = url_domain;
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elided_path = ElideComponentizedPath(url_elided_domain, url_path_elements,
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         url_filename, url_query, font,
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         available_pixel_width);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!elided_path.empty())
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return elided_path;
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Return elided domain/.../filename anyway.
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 final_elided_url_string(url_elided_domain);
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int url_elided_domain_width = font.GetStringWidth(url_elided_domain);
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // A hack to prevent trailing ".../...".
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((available_pixel_width - url_elided_domain_width) >
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pixel_width_ellipsis_slash + kPixelWidthDotsTrailer +
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      font.GetStringWidth(ASCIIToUTF16("UV"))) {
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    final_elided_url_string += BuildPathFromComponents(string16(),
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        url_path_elements, url_filename, 1);
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    final_elided_url_string += url_path;
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ElideText(final_elided_url_string, font, available_pixel_width,
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   ELIDE_AT_END);
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)string16 ElideFilename(const base::FilePath& filename,
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const gfx::Font& font,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       int available_pixel_width) {
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 filename_utf16 = filename.value();
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 extension = filename.Extension();
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 rootname = filename.BaseName().RemoveExtension().value();
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_POSIX)
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 filename_utf16 = WideToUTF16(base::SysNativeMBToWide(
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filename.value()));
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 extension = WideToUTF16(base::SysNativeMBToWide(
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filename.Extension()));
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 rootname = WideToUTF16(base::SysNativeMBToWide(
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      filename.BaseName().RemoveExtension().value()));
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int full_width = font.GetStringWidth(filename_utf16);
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (full_width <= available_pixel_width)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::i18n::GetDisplayStringInLTRDirectionality(filename_utf16);
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rootname.empty() || extension.empty()) {
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16 elided_name = ElideText(filename_utf16, font,
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           available_pixel_width, ELIDE_AT_END);
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int ext_width = font.GetStringWidth(extension);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int root_width = font.GetStringWidth(rootname);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We may have trimmed the path.
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (root_width + ext_width <= available_pixel_width) {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16 elided_name = rootname + extension;
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ext_width >= available_pixel_width) {
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16 elided_name = ElideText(rootname + extension, font,
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           available_pixel_width,
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           ELIDE_IN_MIDDLE);
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int available_root_width = available_pixel_width - ext_width;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 elided_name =
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ElideText(rootname, font, available_root_width, ELIDE_AT_END);
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elided_name += extension;
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return base::i18n::GetDisplayStringInLTRDirectionality(elided_name);
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ElideText(const string16& text,
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   const gfx::Font& font,
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   int available_pixel_width,
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   ElideBehavior elide_behavior) {
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (text.empty())
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return text;
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int current_text_pixel_width = font.GetStringWidth(text);
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const bool elide_in_middle = (elide_behavior == ELIDE_IN_MIDDLE);
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const bool insert_ellipsis = (elide_behavior != TRUNCATE_AT_END);
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
456eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const string16 ellipsis = string16(kEllipsisUTF16);
457eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  StringSlicer slicer(text, ellipsis, elide_in_middle);
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Pango will return 0 width for absurdly long strings. Cut the string in
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // half and try again.
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This is caused by an int overflow in Pango (specifically, in
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // pango_glyph_string_extents_range). It's actually more subtle than just
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // returning 0, since on super absurdly long strings, the int can wrap and
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // return positive numbers again. Detecting that is probably not worth it
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (eliding way too much from a ridiculous string is probably still
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ridiculous), but we should check other widths for bogus values as well.
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_text_pixel_width <= 0 && !text.empty()) {
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16 cut = slicer.CutString(text.length() / 2, false);
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ElideText(cut, font, available_pixel_width, elide_behavior);
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_text_pixel_width <= available_pixel_width)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return text;
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (insert_ellipsis && font.GetStringWidth(ellipsis) > available_pixel_width)
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string16();
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Use binary search to compute the elided text.
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t lo = 0;
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t hi = text.length() - 1;
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t guess;
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) {
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We check the length of the whole desired string at once to ensure we
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // handle kerning/ligatures/etc. correctly.
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16 cut = slicer.CutString(guess, insert_ellipsis);
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int guess_length = font.GetStringWidth(cut);
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Check again that we didn't hit a Pango width overflow. If so, cut the
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // current string in half and start over.
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (guess_length <= 0) {
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ElideText(slicer.CutString(guess / 2, false),
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       font, available_pixel_width, elide_behavior);
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (guess_length > available_pixel_width)
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hi = guess - 1;
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lo = guess + 1;
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return slicer.CutString(guess, insert_ellipsis);
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SortedDisplayURL::SortedDisplayURL(const GURL& url,
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   const std::string& languages) {
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net::AppendFormattedHost(url, languages, &sort_host_);
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 host_minus_www = net::StripWWW(sort_host_);
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_parse::Parsed parsed;
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  display_url_ =
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::FormatUrl(url, languages, net::kFormatUrlOmitAll,
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     net::UnescapeRule::SPACES, &parsed, &prefix_end_, NULL);
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sort_host_.length() > host_minus_www.length()) {
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix_end_ += sort_host_.length() - host_minus_www.length();
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sort_host_.swap(host_minus_www);
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SortedDisplayURL::SortedDisplayURL() : prefix_end_(0) {
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SortedDisplayURL::~SortedDisplayURL() {
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int SortedDisplayURL::Compare(const SortedDisplayURL& other,
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              icu::Collator* collator) const {
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Compare on hosts first. The host won't contain 'www.'.
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UErrorCode compare_status = U_ZERO_ERROR;
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UCollationResult host_compare_result = collator->compare(
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const UChar*>(sort_host_.c_str()),
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(sort_host_.length()),
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const UChar*>(other.sort_host_.c_str()),
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(other.sort_host_.length()),
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      compare_status);
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(U_SUCCESS(compare_status));
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (host_compare_result != 0)
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return host_compare_result;
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hosts match, compare on the portion of the url after the host.
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 path = this->AfterHost();
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 o_path = other.AfterHost();
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  compare_status = U_ZERO_ERROR;
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UCollationResult path_compare_result = collator->compare(
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const UChar*>(path.c_str()),
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(path.length()),
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const UChar*>(o_path.c_str()),
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(o_path.length()),
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      compare_status);
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(U_SUCCESS(compare_status));
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (path_compare_result != 0)
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return path_compare_result;
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hosts and paths match, compare on the complete url. This'll push the www.
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ones to the end.
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  compare_status = U_ZERO_ERROR;
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UCollationResult display_url_compare_result = collator->compare(
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const UChar*>(display_url_.c_str()),
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(display_url_.length()),
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const UChar*>(other.display_url_.c_str()),
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<int>(other.display_url_.length()),
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      compare_status);
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(U_SUCCESS(compare_status));
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return display_url_compare_result;
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 SortedDisplayURL::AfterHost() const {
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t slash_index = display_url_.find(sort_host_, prefix_end_);
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (slash_index == string16::npos) {
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string16();
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return display_url_.substr(slash_index + sort_host_.length());
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ElideString(const string16& input, int max_len, string16* output) {
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GE(max_len, 0);
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (static_cast<int>(input.length()) <= max_len) {
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output->assign(input);
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (max_len) {
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 0:
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->clear();
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 1:
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, 1));
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 2:
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, 2));
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 3:
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, 1) + ASCIIToUTF16(".") +
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     input.substr(input.length() - 1));
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 4:
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, 1) + ASCIIToUTF16("..") +
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     input.substr(input.length() - 1));
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default: {
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int rstr_len = (max_len - 3) / 2;
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int lstr_len = rstr_len + ((max_len - 3) % 2);
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output->assign(input.substr(0, lstr_len) + ASCIIToUTF16("...") +
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     input.substr(input.length() - rstr_len));
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ui
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Internal class used to track progress of a rectangular string elide
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// operation.  Exists so the top-level ElideRectangleString() function
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// can be broken into smaller methods sharing this state.
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RectangleString {
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RectangleString(size_t max_rows, size_t max_cols,
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  bool strict, string16 *output)
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : max_rows_(max_rows),
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        max_cols_(max_cols),
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_row_(0),
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_col_(0),
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        strict_(strict),
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        suppressed_(false),
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        output_(output) {}
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Perform deferred initializations following creation.  Must be called
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // before any input can be added via AddString().
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Init() { output_->clear(); }
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add an input string, reformatting to fit the desired dimensions.
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // AddString() may be called multiple times to concatenate together
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // multiple strings into the region (the current caller doesn't do
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this, however).
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddString(const string16& input);
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Perform any deferred output processing.  Must be called after the
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // last AddString() call has occurred.
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool Finalize();
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a line to the rectangular region at the current position,
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either by itself or by breaking it into words.
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddLine(const string16& line);
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a word to the rectangular region at the current position,
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either by itself or by breaking it into characters.
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddWord(const string16& word);
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add text to the output string if the rectangular boundaries
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // have not been exceeded, advancing the current position.
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Append(const string16& string);
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the current position to the beginning of the next line.  If
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |output| is true, add a newline to the output string if the rectangular
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // boundaries have not been exceeded.  If |output| is false, we assume
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // some other mechanism will (likely) do similar breaking after the fact.
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void NewLine(bool output);
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Maximum number of rows allowed in the output string.
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t max_rows_;
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Maximum number of characters allowed in the output string.
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t max_cols_;
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Current row position, always incremented and may exceed max_rows_
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // when the input can not fit in the region.  We stop appending to
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the output string, however, when this condition occurs.  In the
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // future, we may want to expose this value to allow the caller to
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // determine how many rows would actually be required to hold the
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // formatted string.
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t current_row_;
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Current character position, should never exceed max_cols_.
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t current_col_;
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // True when we do whitespace to newline conversions ourselves.
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool strict_;
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // True when some of the input has been truncated.
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool suppressed_;
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // String onto which the output is accumulated.
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16* output_;
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(RectangleString);
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleString::AddString(const string16& input) {
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::i18n::BreakIterator lines(input,
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  base::i18n::BreakIterator::BREAK_NEWLINE);
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (lines.Init()) {
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (lines.Advance())
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddLine(lines.GetString());
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "BreakIterator (lines) init failed";
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool RectangleString::Finalize() {
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (suppressed_) {
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output_->append(ASCIIToUTF16("..."));
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleString::AddLine(const string16& line) {
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (line.length() < max_cols_) {
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Append(line);
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::i18n::BreakIterator words(line,
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    base::i18n::BreakIterator::BREAK_SPACE);
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (words.Init()) {
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (words.Advance())
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        AddWord(words.GetString());
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED() << "BreakIterator (words) init failed";
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Account for naturally-occuring newlines.
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ++current_row_;
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_col_ = 0;
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleString::AddWord(const string16& word) {
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (word.length() < max_cols_) {
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Word can be made to fit, no need to fragment it.
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (current_col_ + word.length() >= max_cols_)
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NewLine(strict_);
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Append(word);
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Word is so big that it must be fragmented.
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int array_start = 0;
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int char_start = 0;
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::i18n::UTF16CharIterator chars(&word);
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (!chars.end()) {
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // When boundary is hit, add as much as will fit on this line.
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (current_col_ + (chars.char_pos() - char_start) >= max_cols_) {
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Append(word.substr(array_start, chars.array_pos() - array_start));
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NewLine(true);
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        array_start = chars.array_pos();
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        char_start = chars.char_pos();
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chars.Advance();
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add the last remaining fragment, if any.
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (array_start != chars.array_pos())
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Append(word.substr(array_start, chars.array_pos() - array_start));
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleString::Append(const string16& string) {
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_row_ < max_rows_)
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output_->append(string);
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suppressed_ = true;
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_col_ += string.length();
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleString::NewLine(bool output) {
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_row_ < max_rows_) {
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (output)
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output_->append(ASCIIToUTF16("\n"));
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suppressed_ = true;
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ++current_row_;
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_col_ = 0;
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Internal class used to track progress of a rectangular text elide
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// operation.  Exists so the top-level ElideRectangleText() function
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// can be broken into smaller methods sharing this state.
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RectangleText {
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RectangleText(const gfx::Font& font,
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                int available_pixel_width,
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                int available_pixel_height,
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                ui::WordWrapBehavior wrap_behavior,
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                std::vector<string16>* lines)
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : font_(font),
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line_height_(font.GetHeight()),
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        available_pixel_width_(available_pixel_width),
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        available_pixel_height_(available_pixel_height),
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        wrap_behavior_(wrap_behavior),
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_width_(0),
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        current_height_(0),
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last_line_ended_in_lf_(false),
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        lines_(lines),
7922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        insufficient_width_(false),
7932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        insufficient_height_(false) {}
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Perform deferred initializions following creation.  Must be called
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // before any input can be added via AddString().
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Init() { lines_->clear(); }
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add an input string, reformatting to fit the desired dimensions.
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // AddString() may be called multiple times to concatenate together
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // multiple strings into the region (the current caller doesn't do
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this, however).
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddString(const string16& input);
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Perform any deferred output processing.  Must be called after the last
8062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // AddString() call has occured. Returns a combination of
8072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |ReformattingResultFlags| indicating whether the given width or height was
8082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // insufficient, leading to elision or truncation.
8092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int Finalize();
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a line to the rectangular region at the current position,
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // either by itself or by breaking it into words.
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddLine(const string16& line);
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Wrap the specified word across multiple lines.
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int WrapWord(const string16& word);
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a long word - wrapping, eliding or truncating per the wrap behavior.
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int AddWordOverflow(const string16& word);
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add a word to the rectangluar region at the current position.
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int AddWord(const string16& word);
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Append the specified |text| to the current output line, incrementing the
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // running width by the specified amount. This is an optimization over
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |AddToCurrentLine()| when |text_width| is already known.
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddToCurrentLineWithWidth(const string16& text, int text_width);
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Append the specified |text| to the current output line.
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddToCurrentLine(const string16& text);
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the current position to the beginning of the next line.
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool NewLine();
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The font used for measuring text width.
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const gfx::Font& font_;
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The height of each line of text.
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int line_height_;
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The number of pixels of available width in the rectangle.
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int available_pixel_width_;
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The number of pixels of available height in the rectangle.
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int available_pixel_height_;
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The wrap behavior for words that are too long to fit on a single line.
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ui::WordWrapBehavior wrap_behavior_;
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The current running width.
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int current_width_;
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The current running height.
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int current_height_;
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The current line of text.
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 current_line_;
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Indicates whether the last line ended with \n.
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool last_line_ended_in_lf_;
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The output vector of lines.
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<string16>* lines_;
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Indicates whether a word was so long that it had to be truncated or elided
8672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // to fit the available width.
8682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool insufficient_width_;
8692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Indicates whether there were too many lines for the available height.
8712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool insufficient_height_;
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(RectangleText);
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleText::AddString(const string16& input) {
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::i18n::BreakIterator lines(input,
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  base::i18n::BreakIterator::BREAK_NEWLINE);
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (lines.Init()) {
8802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while (!insufficient_height_ && lines.Advance()) {
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      string16 line = lines.GetString();
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The BREAK_NEWLINE iterator will keep the trailing newline character,
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // except in the case of the last line, which may not have one.  Remove
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the newline character, if it exists.
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_line_ended_in_lf_ = !line.empty() && line[line.length() - 1] == '\n';
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (last_line_ended_in_lf_)
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line.resize(line.length() - 1);
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddLine(line);
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "BreakIterator (lines) init failed";
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int RectangleText::Finalize() {
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove trailing whitespace from the last line or remove the last line
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // completely, if it's just whitespace.
8982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!insufficient_height_ && !lines_->empty()) {
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TrimWhitespace(lines_->back(), TRIM_TRAILING, &lines_->back());
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (lines_->back().empty() && !last_line_ended_in_lf_)
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lines_->pop_back();
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (last_line_ended_in_lf_)
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines_->push_back(string16());
9052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (insufficient_width_ ? ui::INSUFFICIENT_SPACE_HORIZONTAL : 0) |
9062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         (insufficient_height_ ? ui::INSUFFICIENT_SPACE_VERTICAL : 0);
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleText::AddLine(const string16& line) {
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int line_width = font_.GetStringWidth(line);
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (line_width <= available_pixel_width_) {
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddToCurrentLineWithWidth(line, line_width);
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Iterate over positions that are valid to break the line at. In general,
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // these are word boundaries but after any punctuation following the word.
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::i18n::BreakIterator words(line,
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    base::i18n::BreakIterator::BREAK_LINE);
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (words.Init()) {
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (words.Advance()) {
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        const bool truncate = !current_line_.empty();
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        const string16& word = words.GetString();
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        const int lines_added = AddWord(word);
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (lines_added) {
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (truncate) {
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // Trim trailing whitespace from the line that was added.
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            const int line = lines_->size() - lines_added;
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            TrimWhitespace(lines_->at(line), TRIM_TRAILING, &lines_->at(line));
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (ContainsOnlyWhitespace(word)) {
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            // Skip the first space if the previous line was carried over.
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            current_width_ = 0;
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            current_line_.clear();
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED() << "BreakIterator (words) init failed";
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Account for naturally-occuring newlines.
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NewLine();
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int RectangleText::WrapWord(const string16& word) {
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Word is so wide that it must be fragmented.
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 text = word;
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int lines_added = 0;
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool first_fragment = true;
9492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  while (!insufficient_height_ && !text.empty()) {
950c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    string16 fragment =
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ui::ElideText(text, font_, available_pixel_width_, ui::TRUNCATE_AT_END);
952c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // At least one character has to be added at every line, even if the
953c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // available space is too small.
954c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if(fragment.empty())
955c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      fragment = text.substr(0, 1);
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!first_fragment && NewLine())
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lines_added++;
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddToCurrentLine(fragment);
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    text = text.substr(fragment.length());
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    first_fragment = false;
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return lines_added;
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int RectangleText::AddWordOverflow(const string16& word) {
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int lines_added = 0;
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Unless this is the very first word, put it on a new line.
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!current_line_.empty()) {
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!NewLine())
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0;
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines_added++;
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (wrap_behavior_ == ui::IGNORE_LONG_WORDS) {
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_line_ = word;
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_width_ = available_pixel_width_;
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (wrap_behavior_ == ui::WRAP_LONG_WORDS) {
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines_added += WrapWord(word);
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ui::ElideBehavior elide_behavior =
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (wrap_behavior_ == ui::ELIDE_LONG_WORDS ? ui::ELIDE_AT_END :
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                  ui::TRUNCATE_AT_END);
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16 elided_word =
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ui::ElideText(word, font_, available_pixel_width_, elide_behavior);
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddToCurrentLine(elided_word);
9872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    insufficient_width_ = true;
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return lines_added;
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int RectangleText::AddWord(const string16& word) {
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int lines_added = 0;
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 trimmed;
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TrimWhitespace(word, TRIM_TRAILING, &trimmed);
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int trimmed_width = font_.GetStringWidth(trimmed);
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (trimmed_width <= available_pixel_width_) {
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Word can be made to fit, no need to fragment it.
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((current_width_ + trimmed_width > available_pixel_width_) && NewLine())
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      lines_added++;
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Append the non-trimmed word, in case more words are added after.
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddToCurrentLine(word);
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines_added = AddWordOverflow(wrap_behavior_ == ui::IGNORE_LONG_WORDS ?
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  trimmed : word);
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return lines_added;
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleText::AddToCurrentLine(const string16& text) {
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddToCurrentLineWithWidth(text, font_.GetStringWidth(text));
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RectangleText::AddToCurrentLineWithWidth(const string16& text,
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              int text_width) {
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_height_ >= available_pixel_height_) {
10182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    insufficient_height_ = true;
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_line_.append(text);
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_width_ += text_width;
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool RectangleText::NewLine() {
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool line_added = false;
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (current_height_ < available_pixel_height_) {
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    lines_->push_back(current_line_);
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_line_.clear();
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    line_added = true;
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
10322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    insufficient_height_ = true;
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_height_ += line_height_;
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_width_ = 0;
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return line_added;
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace ui {
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ElideRectangleString(const string16& input, size_t max_rows,
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          size_t max_cols, bool strict, string16* output) {
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RectangleString rect(max_rows, max_cols, strict, output);
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.Init();
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.AddString(input);
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rect.Finalize();
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ElideRectangleText(const string16& input,
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        const gfx::Font& font,
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        int available_pixel_width,
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        int available_pixel_height,
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        WordWrapBehavior wrap_behavior,
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        std::vector<string16>* lines) {
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RectangleText rect(font,
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     available_pixel_width,
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     available_pixel_height,
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     wrap_behavior,
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     lines);
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.Init();
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rect.AddString(input);
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rect.Finalize();
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TruncateString(const string16& string, size_t length) {
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (string.size() <= length)
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // String fits, return it.
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string;
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (length == 0)
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No room for the elide string, return an empty string.
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string16();
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t max = length - 1;
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Added to the end of strings that are too big.
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const char16 kElideString[] = { 0x2026, 0 };
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (max == 0)
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Just enough room for the elide string.
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return kElideString;
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Use a line iterator to find the first boundary.
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UErrorCode status = U_ZERO_ERROR;
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<icu::RuleBasedBreakIterator> bi(
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<icu::RuleBasedBreakIterator*>(
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          icu::RuleBasedBreakIterator::createLineInstance(
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              icu::Locale::getDefault(), status)));
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (U_FAILURE(status))
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string.substr(0, max) + kElideString;
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bi->setText(string.c_str());
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int32_t index = bi->preceding(static_cast<int32_t>(max));
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index == icu::BreakIterator::DONE) {
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    index = static_cast<int32_t>(max);
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Found a valid break (may be the beginning of the string). Now use
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // a character iterator to find the previous non-whitespace character.
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icu::StringCharacterIterator char_iterator(string.c_str());
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (index == 0) {
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // No valid line breaks. Start at the end again. This ensures we break
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // on a valid character boundary.
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      index = static_cast<int32_t>(max);
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    char_iterator.setIndex(index);
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (char_iterator.hasPrevious()) {
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      char_iterator.previous();
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!(u_isspace(char_iterator.current()) ||
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            u_charType(char_iterator.current()) == U_CONTROL_CHAR ||
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            u_charType(char_iterator.current()) == U_NON_SPACING_MARK)) {
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Not a whitespace character. Advance the iterator so that we
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // include the current character in the truncated string.
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        char_iterator.next();
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (char_iterator.hasPrevious()) {
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Found a valid break point.
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      index = char_iterator.getIndex();
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // String has leading whitespace, return the elide string.
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return kElideString;
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return string.substr(0, index) + kElideString;
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace ui
1130