diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/jsmessage_box_handler_win.cc | 5 | ||||
-rw-r--r-- | chrome/common/gfx/chrome_canvas_win.cc | 44 | ||||
-rw-r--r-- | chrome/common/l10n_util.cc | 34 | ||||
-rw-r--r-- | chrome/common/l10n_util.h | 8 | ||||
-rw-r--r-- | chrome/common/l10n_util_unittest.cc | 96 | ||||
-rw-r--r-- | chrome/views/label.cc | 28 | ||||
-rw-r--r-- | chrome/views/label.h | 41 | ||||
-rw-r--r-- | chrome/views/label_unittest.cc | 21 | ||||
-rw-r--r-- | chrome/views/message_box_view.cc | 19 | ||||
-rw-r--r-- | chrome/views/message_box_view.h | 16 |
10 files changed, 289 insertions, 23 deletions
diff --git a/chrome/browser/jsmessage_box_handler_win.cc b/chrome/browser/jsmessage_box_handler_win.cc index 979e7e6..5026e8e 100644 --- a/chrome/browser/jsmessage_box_handler_win.cc +++ b/chrome/browser/jsmessage_box_handler_win.cc @@ -43,8 +43,9 @@ JavascriptMessageBoxHandler::JavascriptMessageBoxHandler( reply_msg_(reply_msg), dialog_flags_(dialog_flags), dialog_(NULL), - message_box_view_(new MessageBoxView(dialog_flags, message_text, - default_prompt_text)) { + message_box_view_(new MessageBoxView( + dialog_flags | MessageBoxView::kAutoDetectAlignment, + message_text, default_prompt_text)) { DCHECK(message_box_view_); DCHECK(reply_msg_); diff --git a/chrome/common/gfx/chrome_canvas_win.cc b/chrome/common/gfx/chrome_canvas_win.cc index f28b0cb..770effb 100644 --- a/chrome/common/gfx/chrome_canvas_win.cc +++ b/chrome/common/gfx/chrome_canvas_win.cc @@ -21,9 +21,14 @@ void DoDrawText(HDC hdc, const std::wstring& text, std::wstring localized_text; const wchar_t* string_ptr = text.c_str(); int string_size = static_cast<int>(text.length()); - if (l10n_util::AdjustStringForLocaleDirection(text, &localized_text)) { - string_ptr = localized_text.c_str(); - string_size = static_cast<int>(localized_text.length()); + // Only adjust string directionality if both of the following are true: + // 1. The current locale is RTL. + // 2. The string itself has RTL directionality. + if (flags & DT_RTLREADING) { + if (l10n_util::AdjustStringForLocaleDirection(text, &localized_text)) { + string_ptr = localized_text.c_str(); + string_size = static_cast<int>(localized_text.length()); + } } DrawText(hdc, string_ptr, string_size, text_bounds, flags); @@ -31,7 +36,7 @@ void DoDrawText(HDC hdc, const std::wstring& text, // Compute the windows flags necessary to implement the provided text // ChromeCanvas flags. -int ComputeFormatFlags(int flags) { +int ComputeFormatFlags(int flags, const std::wstring& text) { int f = 0; // Setting the text alignment explicitly in case it hasn't already been set. @@ -82,9 +87,30 @@ int ComputeFormatFlags(int flags) { // English) this flag also makes sure that if there is not enough space to // display the entire string, the ellipsis is displayed on the left hand side // of the truncated string and not on the right hand side. - if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) - f |= DT_RTLREADING; - + // + // We make a distinction between Chrome UI strings and text coming from a web + // page. + // + // For text coming from a web page we determine the alignment based on the + // first character with strong directionality. If the directionality of the + // first character with strong directionality in the text is LTR, the + // alignment is set to DT_LEFT, and the directionality should not be set as + // DT_RTLREADING. + // + // This heuristic doesn't work for Chrome UI strings since even in RTL + // locales, some of those might start with English text but we know they're + // localized so we always want them to be right aligned, and their + // directionality should be set as DT_RTLREADING. + // + // Caveat: If the string is purely LTR, don't set DTL_RTLREADING since when + // the flag is set, LRE-PDF don't have the desired effect of rendering + // multiline English-only text as LTR. + if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT && + (f & DT_RIGHT)) { + if (l10n_util::StringContainsStrongRTLChars(text)) { + f |= DT_RTLREADING; + } + } return f; } @@ -115,7 +141,7 @@ void ChromeCanvas::SizeStringInt(const std::wstring& text, b.right = 1; } b.bottom = *height; - DoDrawText(dc, text, &b, ComputeFormatFlags(flags) | DT_CALCRECT); + DoDrawText(dc, text, &b, ComputeFormatFlags(flags, text) | DT_CALCRECT); // Restore the old font. This way we don't have to worry if the caller // deletes the font and the DC lives longer. @@ -141,7 +167,7 @@ void ChromeCanvas::DrawStringInt(const std::wstring& text, HFONT font, SkColorGetB(color)); SetTextColor(dc, brush_color); - int f = ComputeFormatFlags(flags); + int f = ComputeFormatFlags(flags, text); DoDrawText(dc, text, &text_bounds, f); endPlatformPaint(); diff --git a/chrome/common/l10n_util.cc b/chrome/common/l10n_util.cc index 6365291..6fb5286 100644 --- a/chrome/common/l10n_util.cc +++ b/chrome/common/l10n_util.cc @@ -502,6 +502,40 @@ TextDirection GetTextDirection() { return g_text_direction; } +TextDirection GetFirstStrongCharacterDirection(const std::wstring& text) { +#if defined(WCHAR_T_IS_UTF32) + string16 text_utf16 = WideToUTF16(text); + const UChar* string = text_utf16.c_str(); +#else + const UChar* string = text.c_str(); +#endif + size_t length = text.length(); + size_t position = 0; + while (position < length) { + UChar32 character; + size_t next_position = position; + U16_NEXT(string, next_position, length, character); + + // Now that we have the character, we use ICU in order to query for the + // appropriate Unicode BiDi character type. + int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS); + if ((property == U_RIGHT_TO_LEFT) || + (property == U_RIGHT_TO_LEFT_ARABIC) || + (property == U_RIGHT_TO_LEFT_EMBEDDING) || + (property == U_RIGHT_TO_LEFT_OVERRIDE)) { + return RIGHT_TO_LEFT; + } else if ((property == U_LEFT_TO_RIGHT) || + (property == U_LEFT_TO_RIGHT_EMBEDDING) || + (property == U_LEFT_TO_RIGHT_OVERRIDE)) { + return LEFT_TO_RIGHT; + } + + position = next_position; + } + + return LEFT_TO_RIGHT; +} + bool AdjustStringForLocaleDirection(const std::wstring& text, std::wstring* localized_text) { if (GetTextDirection() == LEFT_TO_RIGHT || text.length() == 0) diff --git a/chrome/common/l10n_util.h b/chrome/common/l10n_util.h index 0c67712..c0a5d0d 100644 --- a/chrome/common/l10n_util.h +++ b/chrome/common/l10n_util.h @@ -116,6 +116,14 @@ enum TextDirection { // * UNKNOWN_DIRECTION: unknown (or error). TextDirection GetTextDirection(); +// Given the string in |text|, returns the directionality of the first +// character with strong directionality in the string. If no character in the +// text has strong directionality, LEFT_TO_RIGHT is returned. The Bidi +// character types L, LRE, LRO, R, AL, RLE, and RLO are considered as strong +// directionality characters. Please refer to http://unicode.org/reports/tr9/ +// for more information. +TextDirection GetFirstStrongCharacterDirection(const std::wstring& text); + // Given the string in |text|, this function creates a copy of the string with // the appropriate Unicode formatting marks that mark the string direction // (either left-to-right or right-to-left). The new string is returned in diff --git a/chrome/common/l10n_util_unittest.cc b/chrome/common/l10n_util_unittest.cc index 3d93ef7..88c1cad 100644 --- a/chrome/common/l10n_util_unittest.cc +++ b/chrome/common/l10n_util_unittest.cc @@ -160,6 +160,102 @@ TEST_F(L10nUtilTest, GetAppLocale) { Locale::setDefault(locale, error_code); } +TEST_F(L10nUtilTest, GetFirstStrongCharacterDirection) { + // Test pure LTR string. + std::wstring string(L"foo bar"); + EXPECT_EQ(l10n_util::LEFT_TO_RIGHT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string in which the first character with strong directionality + // is a character with type L. + string.assign(L"foo \x05d0 bar"); + EXPECT_EQ(l10n_util::LEFT_TO_RIGHT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string in which the first character with strong directionality + // is a character with type R. + string.assign(L"\x05d0 foo bar"); + EXPECT_EQ(l10n_util::RIGHT_TO_LEFT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string which starts with a character with weak directionality + // and in which the first character with strong directionality is a character + // with type L. + string.assign(L"!foo \x05d0 bar"); + EXPECT_EQ(l10n_util::LEFT_TO_RIGHT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string which starts with a character with weak directionality + // and in which the first character with strong directionality is a character + // with type R. + string.assign(L",\x05d0 foo bar"); + EXPECT_EQ(l10n_util::RIGHT_TO_LEFT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string in which the first character with strong directionality + // is a character with type LRE. + string.assign(L"\x202a \x05d0 foo bar"); + EXPECT_EQ(l10n_util::LEFT_TO_RIGHT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string in which the first character with strong directionality + // is a character with type LRO. + string.assign(L"\x202d \x05d0 foo bar"); + EXPECT_EQ(l10n_util::LEFT_TO_RIGHT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string in which the first character with strong directionality + // is a character with type RLE. + string.assign(L"\x202b foo \x05d0 bar"); + EXPECT_EQ(l10n_util::RIGHT_TO_LEFT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string in which the first character with strong directionality + // is a character with type RLO. + string.assign(L"\x202e foo \x05d0 bar"); + EXPECT_EQ(l10n_util::RIGHT_TO_LEFT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test bidi string in which the first character with strong directionality + // is a character with type AL. + string.assign(L"\x0622 foo \x05d0 bar"); + EXPECT_EQ(l10n_util::RIGHT_TO_LEFT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test a string without strong directionality characters. + string.assign(L",!.{}"); + EXPECT_EQ(l10n_util::LEFT_TO_RIGHT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test empty string. + string.assign(L""); + EXPECT_EQ(l10n_util::LEFT_TO_RIGHT, + l10n_util::GetFirstStrongCharacterDirection(string)); + + // Test characters in non-BMP (e.g. Phoenician letters. Please refer to + // http://demo.icu-project.org/icu-bin/ubrowse?scr=151&b=10910 for more + // information). +#if defined(WCHAR_T_IS_UTF32) + string.assign(L" ! \x10910" L"abc 123"); +#elif defined(WCHAR_T_IS_UTF16) + string.assign(L" ! \xd802\xdd10" L"abc 123"); +#else +#error wchar_t should be either UTF-16 or UTF-32 +#endif + EXPECT_EQ(l10n_util::RIGHT_TO_LEFT, + l10n_util::GetFirstStrongCharacterDirection(string)); + +#if defined(WCHAR_T_IS_UTF32) + string.assign(L" ! \x10401" L"abc 123"); +#elif defined(WCHAR_T_IS_UTF16) + string.assign(L" ! \xd801\xdc01" L"abc 123"); +#else +#error wchar_t should be either UTF-16 or UTF-32 +#endif + EXPECT_EQ(l10n_util::LEFT_TO_RIGHT, + l10n_util::GetFirstStrongCharacterDirection(string)); +} + typedef struct { std::wstring path; std::wstring wrapped_path; diff --git a/chrome/views/label.cc b/chrome/views/label.cc index f0c38d8..0489479 100644 --- a/chrome/views/label.cc +++ b/chrome/views/label.cc @@ -45,6 +45,7 @@ void Label::Init(const std::wstring& text, const ChromeFont& font) { horiz_alignment_ = ALIGN_CENTER; is_multi_line_ = false; collapse_when_hidden_ = false; + rtl_alignment_mode_ = USE_UI_ALIGNMENT; } Label::~Label() { @@ -238,17 +239,16 @@ const SkColor Label::GetColor() const { } void Label::SetHorizontalAlignment(Alignment a) { + // If the View's UI layout is right-to-left and rtl_alignment_mode_ is + // USE_UI_ALIGNMENT, we need to flip the alignment so that the alignment + // settings take into account the text directionality. + if (UILayoutIsRightToLeft() && rtl_alignment_mode_ == USE_UI_ALIGNMENT) { + if (a == ALIGN_LEFT) + a = ALIGN_RIGHT; + else if (a == ALIGN_RIGHT) + a = ALIGN_LEFT; + } if (horiz_alignment_ != a) { - - // If the View's UI layout is right-to-left, we need to flip the alignment - // so that the alignment settings take into account the text - // directionality. - if (UILayoutIsRightToLeft()) { - if (a == ALIGN_LEFT) - a = ALIGN_RIGHT; - else if (a == ALIGN_RIGHT) - a = ALIGN_LEFT; - } horiz_alignment_ = a; SchedulePaint(); } @@ -258,6 +258,14 @@ Label::Alignment Label::GetHorizontalAlignment() const { return horiz_alignment_; } +void Label::SetRTLAlignmentMode(RTLAlignmentMode mode) { + rtl_alignment_mode_ = mode; +} + +Label::RTLAlignmentMode Label::GetRTLAlignmentMode() const { + return rtl_alignment_mode_; +} + void Label::SetMultiLine(bool f) { if (f != is_multi_line_) { is_multi_line_ = f; diff --git a/chrome/views/label.h b/chrome/views/label.h index 84e685c..d70ff3a 100644 --- a/chrome/views/label.h +++ b/chrome/views/label.h @@ -26,6 +26,22 @@ class Label : public View { ALIGN_CENTER, ALIGN_RIGHT }; + // The following enum is used to indicate whether using the Chrome UI's + // alignment as the label's alignment, or autodetecting the label's + // alignment. + // + // If the label text originates from the Chrome UI, we should use the Chrome + // UI's alignment as the label's alignment. + // + // If the text originates from a web page, the text's alignment is determined + // based on the first character with strong directionality, disregarding what + // directionality the Chrome UI is. And its alignment will not be flipped + // around in RTL locales. + enum RTLAlignmentMode { + USE_UI_ALIGNMENT = 0, + AUTO_DETECT_ALIGNMENT + }; + // The view class name. static const char kViewClassName[]; @@ -81,10 +97,29 @@ class Label : public View { // Return a reference to the currently used color virtual const SkColor GetColor() const; - // Alignment + // Set horizontal alignment. If the locale is RTL, and the RTL alignment + // setting is set as USE_UI_ALIGNMENT, the alignment is flipped around. + // + // Caveat: for labels originating from a web page, the RTL alignment mode + // should be reset to AUTO_DETECT_ALIGNMENT before the horizontal alignment + // is set. Otherwise, the label's alignment specified as a parameter will be + // flipped in RTL locales. Please see the comments in SetRTLAlignmentMode for + // more information. void SetHorizontalAlignment(Alignment a); + Alignment GetHorizontalAlignment() const; + // Set the RTL alignment mode. The RTL alignment mode is initialized to + // USE_UI_ALIGNMENT when the label is constructed. USE_UI_ALIGNMENT applies + // to every label that originates from the Chrome UI. However, if the label + // originates from a web page, its alignment should not be flipped around for + // RTL locales. For such labels, we need to set the RTL alignment mode to + // AUTO_DETECT_ALIGNMENT so that subsequent SetHorizontalAlignment() calls + // will not flip the label's alignment around. + void SetRTLAlignmentMode(RTLAlignmentMode mode); + + RTLAlignmentMode GetRTLAlignmentMode() const; + // Set whether the label text can wrap on multiple lines. // Default is false void SetMultiLine(bool f); @@ -192,6 +227,10 @@ class Label : public View { scoped_ptr<Background> mouse_over_background_; // Whether to collapse the label when it's not visible. bool collapse_when_hidden_; + // The following member variable is used to control whether the alignment + // needs to be flipped around for RTL locales. Please refer to the definition + // of RTLAlignmentMode for more information. + RTLAlignmentMode rtl_alignment_mode_; DISALLOW_COPY_AND_ASSIGN(Label); }; diff --git a/chrome/views/label_unittest.cc b/chrome/views/label_unittest.cc index e1f80bb0..96b4d63 100644 --- a/chrome/views/label_unittest.cc +++ b/chrome/views/label_unittest.cc @@ -61,6 +61,27 @@ TEST(LabelTest, AlignmentProperty) { label.GetHorizontalAlignment()); label.SetHorizontalAlignment(Label::ALIGN_CENTER); EXPECT_EQ(Label::ALIGN_CENTER, label.GetHorizontalAlignment()); + + // The label's alignment should not be flipped if the RTL alignment mode + // is AUTO_DETECT_ALIGNMENT. + label.SetRTLAlignmentMode(Label::AUTO_DETECT_ALIGNMENT); + label.SetHorizontalAlignment(Label::ALIGN_RIGHT); + EXPECT_EQ(Label::ALIGN_RIGHT, label.GetHorizontalAlignment()); + label.SetHorizontalAlignment(Label::ALIGN_LEFT); + EXPECT_EQ(Label::ALIGN_LEFT, label.GetHorizontalAlignment()); + label.SetHorizontalAlignment(Label::ALIGN_CENTER); + EXPECT_EQ(Label::ALIGN_CENTER, label.GetHorizontalAlignment()); +} + +TEST(LabelTest, RTLAlignmentModeProperty) { + Label label; + EXPECT_EQ(Label::USE_UI_ALIGNMENT, label.GetRTLAlignmentMode()); + + label.SetRTLAlignmentMode(Label::AUTO_DETECT_ALIGNMENT); + EXPECT_EQ(Label::AUTO_DETECT_ALIGNMENT, label.GetRTLAlignmentMode()); + + label.SetRTLAlignmentMode(Label::USE_UI_ALIGNMENT); + EXPECT_EQ(Label::USE_UI_ALIGNMENT, label.GetRTLAlignmentMode()); } TEST(LabelTest, MultiLineProperty) { diff --git a/chrome/views/message_box_view.cc b/chrome/views/message_box_view.cc index 818cf50..26fd1c2 100644 --- a/chrome/views/message_box_view.cc +++ b/chrome/views/message_box_view.cc @@ -94,7 +94,24 @@ void MessageBoxView::ViewHierarchyChanged(bool is_add, void MessageBoxView::Init(int dialog_flags, const std::wstring& default_prompt) { message_label_->SetMultiLine(true); - message_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + if (dialog_flags & kAutoDetectAlignment) { + // Determine the alignment and directionality based on the first character + // with strong directionality. + l10n_util::TextDirection direction = + l10n_util::GetFirstStrongCharacterDirection(message_label_->GetText()); + views::Label::Alignment alignment; + if (direction == l10n_util::RIGHT_TO_LEFT) + alignment = views::Label::ALIGN_RIGHT; + else + alignment = views::Label::ALIGN_LEFT; + // In addition, we should set the RTL alignment mode as + // AUTO_DETECT_ALIGNMENT so that the alignment will not be flipped around + // in RTL locales. + message_label_->SetRTLAlignmentMode(views::Label::AUTO_DETECT_ALIGNMENT); + message_label_->SetHorizontalAlignment(alignment); + } else { + message_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + } if (dialog_flags & kFlagHasPromptField) { prompt_field_ = new views::TextField; diff --git a/chrome/views/message_box_view.h b/chrome/views/message_box_view.h index a8f0dca..e71db2c 100644 --- a/chrome/views/message_box_view.h +++ b/chrome/views/message_box_view.h @@ -25,6 +25,22 @@ class MessageBoxView : public views::View { static const int kFlagHasPromptField = 0x4; static const int kFlagHasMessage = 0x8; + // The following flag is used to indicate whether the message's alignment + // should be autodetected or inherited from Chrome UI. Callers should pass + // the correct flag based on the origin of the message. If the message is + // from a web page (such as the JavaScript alert message), its alignment and + // directionality are based on the first character with strong directionality + // in the message. Chrome UI strings are localized string and therefore they + // should have the same alignment and directionality as those of the Chrome + // UI. For example, in RTL locales, even though some strings might begin with + // an English character, they should still be right aligned and be displayed + // Right-To-Left. + // + // TODO(xji): If the message is from a web page, then the message + // directionality should be determined based on the directionality of the web + // page. Please refer to http://crbug.com/7166 for more information. + static const int kAutoDetectAlignment = 0x10; + static const int kIsConfirmMessageBox = kFlagHasMessage | kFlagHasOKButton | kFlagHasCancelButton; |