| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/renderer_host/gtk_im_context_wrapper.h" | 5 #include "chrome/browser/renderer_host/gtk_im_context_wrapper.h" |
| 6 | 6 |
| 7 #include <gdk/gdk.h> | 7 #include <gdk/gdk.h> |
| 8 #include <gdk/gdkkeysyms.h> | 8 #include <gdk/gdkkeysyms.h> |
| 9 #include <gtk/gtk.h> | 9 #include <gtk/gtk.h> |
| 10 | 10 |
| 11 #include <algorithm> | 11 #include <algorithm> |
| 12 | 12 |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
| 15 #include "base/third_party/icu/icu_utf.h" | 15 #include "base/third_party/icu/icu_utf.h" |
| 16 #include "base/utf_string_conversions.h" | 16 #include "base/utf_string_conversions.h" |
| 17 #include "chrome/app/chrome_command_ids.h" | 17 #include "chrome/app/chrome_command_ids.h" |
| 18 #include "chrome/browser/ui/gtk/gtk_util.h" | 18 #include "chrome/browser/ui/gtk/gtk_util.h" |
| 19 #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" | 19 #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" |
| 20 #include "chrome/common/render_messages.h" | 20 #include "chrome/common/render_messages.h" |
| 21 #include "content/browser/renderer_host/render_widget_host.h" | 21 #include "content/browser/renderer_host/render_widget_host.h" |
| 22 #include "content/common/native_web_keyboard_event.h" | 22 #include "content/common/native_web_keyboard_event.h" |
| 23 #include "grit/generated_resources.h" | 23 #include "grit/generated_resources.h" |
| 24 #include "third_party/skia/include/core/SkColor.h" | 24 #include "third_party/skia/include/core/SkColor.h" |
| 25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositionUnderli
ne.h" |
| 26 #include "ui/base/gtk/gtk_im_context_util.h" |
| 25 #include "ui/base/l10n/l10n_util.h" | 27 #include "ui/base/l10n/l10n_util.h" |
| 26 #include "ui/gfx/gtk_util.h" | 28 #include "ui/gfx/gtk_util.h" |
| 27 #include "ui/gfx/rect.h" | 29 #include "ui/gfx/rect.h" |
| 28 | 30 |
| 29 #if !defined(TOOLKIT_VIEWS) | 31 #if !defined(TOOLKIT_VIEWS) |
| 30 #include "chrome/browser/ui/gtk/menu_gtk.h" | 32 #include "chrome/browser/ui/gtk/menu_gtk.h" |
| 31 #endif | 33 #endif |
| 32 | 34 |
| 33 namespace { | 35 namespace { |
| 34 // Copied from third_party/WebKit/Source/WebCore/page/EventHandler.cpp | 36 // Copied from third_party/WebKit/Source/WebCore/page/EventHandler.cpp |
| 35 // | 37 // |
| 36 // Match key code of composition keydown event on windows. | 38 // Match key code of composition keydown event on windows. |
| 37 // IE sends VK_PROCESSKEY which has value 229; | 39 // IE sends VK_PROCESSKEY which has value 229; |
| 38 // | 40 // |
| 39 // Please refer to following documents for detals: | 41 // Please refer to following documents for detals: |
| 40 // - Virtual-Key Codes | 42 // - Virtual-Key Codes |
| 41 // http://msdn.microsoft.com/en-us/library/ms645540(VS.85).aspx | 43 // http://msdn.microsoft.com/en-us/library/ms645540(VS.85).aspx |
| 42 // - How the IME System Works | 44 // - How the IME System Works |
| 43 // http://msdn.microsoft.com/en-us/library/cc194848.aspx | 45 // http://msdn.microsoft.com/en-us/library/cc194848.aspx |
| 44 // - ImmGetVirtualKey Function | 46 // - ImmGetVirtualKey Function |
| 45 // http://msdn.microsoft.com/en-us/library/dd318570(VS.85).aspx | 47 // http://msdn.microsoft.com/en-us/library/dd318570(VS.85).aspx |
| 46 const int kCompositionEventKeyCode = 229; | 48 const int kCompositionEventKeyCode = 229; |
| 47 } // namespace | 49 } // namespace |
| 48 | 50 |
| 51 // ui::CompositionUnderline should be identical to |
| 52 // WebKit::WebCompositionUnderline, so that we can do reinterpret_cast safely. |
| 53 // TODO(suzhe): remove it after migrating all code in chrome to use |
| 54 // ui::CompositionUnderline. |
| 55 COMPILE_ASSERT(sizeof(ui::CompositionUnderline) == |
| 56 sizeof(WebKit::WebCompositionUnderline), |
| 57 ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff); |
| 58 |
| 49 GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view) | 59 GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view) |
| 50 : host_view_(host_view), | 60 : host_view_(host_view), |
| 51 context_(gtk_im_multicontext_new()), | 61 context_(gtk_im_multicontext_new()), |
| 52 context_simple_(gtk_im_context_simple_new()), | 62 context_simple_(gtk_im_context_simple_new()), |
| 53 is_focused_(false), | 63 is_focused_(false), |
| 54 is_composing_text_(false), | 64 is_composing_text_(false), |
| 55 is_enabled_(false), | 65 is_enabled_(false), |
| 56 is_in_key_event_handler_(false), | 66 is_in_key_event_handler_(false), |
| 57 preedit_selection_start_(0), | 67 is_composition_changed_(false), |
| 58 preedit_selection_end_(0), | |
| 59 is_preedit_changed_(false), | |
| 60 suppress_next_commit_(false), | 68 suppress_next_commit_(false), |
| 61 last_key_code_(0), | 69 last_key_code_(0), |
| 62 last_key_was_up_(false), | 70 last_key_was_up_(false), |
| 63 last_key_filtered_no_result_(false) { | 71 last_key_filtered_no_result_(false) { |
| 64 DCHECK(context_); | 72 DCHECK(context_); |
| 65 DCHECK(context_simple_); | 73 DCHECK(context_simple_); |
| 66 | 74 |
| 67 // context_ and context_simple_ share the same callback handlers. | 75 // context_ and context_simple_ share the same callback handlers. |
| 68 // All data come from them are treated equally. | 76 // All data come from them are treated equally. |
| 69 // context_ is for full input method support. | 77 // context_ is for full input method support. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 107 } | 115 } |
| 108 | 116 |
| 109 void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) { | 117 void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) { |
| 110 suppress_next_commit_ = false; | 118 suppress_next_commit_ = false; |
| 111 | 119 |
| 112 // Indicates preedit-changed and commit signal handlers that we are | 120 // Indicates preedit-changed and commit signal handlers that we are |
| 113 // processing a key event. | 121 // processing a key event. |
| 114 is_in_key_event_handler_ = true; | 122 is_in_key_event_handler_ = true; |
| 115 // Reset this flag so that we can know if preedit is changed after | 123 // Reset this flag so that we can know if preedit is changed after |
| 116 // processing this key event. | 124 // processing this key event. |
| 117 is_preedit_changed_ = false; | 125 is_composition_changed_ = false; |
| 118 // Clear it so that we can know if something needs committing after | 126 // Clear it so that we can know if something needs committing after |
| 119 // processing this key event. | 127 // processing this key event. |
| 120 commit_text_.clear(); | 128 commit_text_.clear(); |
| 121 | 129 |
| 122 // According to Document Object Model (DOM) Level 3 Events Specification | 130 // According to Document Object Model (DOM) Level 3 Events Specification |
| 123 // Appendix A: Keyboard events and key identifiers | 131 // Appendix A: Keyboard events and key identifiers |
| 124 // http://www.w3.org/TR/DOM-Level-3-Events/keyset.html: | 132 // http://www.w3.org/TR/DOM-Level-3-Events/keyset.html: |
| 125 // The event sequence would be: | 133 // The event sequence would be: |
| 126 // 1. keydown | 134 // 1. keydown |
| 127 // 2. textInput | 135 // 2. textInput |
| (...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 gtk_im_context_reset(context_simple_); | 346 gtk_im_context_reset(context_simple_); |
| 339 | 347 |
| 340 if (is_focused_) { | 348 if (is_focused_) { |
| 341 // Some input methods may not honour the reset call. Focusing out/in the | 349 // Some input methods may not honour the reset call. Focusing out/in the |
| 342 // |context_| to make sure it gets reset correctly. | 350 // |context_| to make sure it gets reset correctly. |
| 343 gtk_im_context_focus_out(context_); | 351 gtk_im_context_focus_out(context_); |
| 344 gtk_im_context_focus_in(context_); | 352 gtk_im_context_focus_in(context_); |
| 345 } | 353 } |
| 346 | 354 |
| 347 is_composing_text_ = false; | 355 is_composing_text_ = false; |
| 348 preedit_text_.clear(); | 356 composition_.Clear(); |
| 349 preedit_underlines_.clear(); | |
| 350 commit_text_.clear(); | 357 commit_text_.clear(); |
| 351 | 358 |
| 352 is_in_key_event_handler_ = false; | 359 is_in_key_event_handler_ = false; |
| 353 } | 360 } |
| 354 | 361 |
| 355 bool GtkIMContextWrapper::NeedCommitByForwardingCharEvent() const { | 362 bool GtkIMContextWrapper::NeedCommitByForwardingCharEvent() const { |
| 356 // If there is no composition text and has only one character to be | 363 // If there is no composition text and has only one character to be |
| 357 // committed, then the character will be send to webkit as a Char event | 364 // committed, then the character will be send to webkit as a Char event |
| 358 // instead of a confirmed composition text. | 365 // instead of a confirmed composition text. |
| 359 // It should be fine to handle BMP character only, as non-BMP characters | 366 // It should be fine to handle BMP character only, as non-BMP characters |
| 360 // can always be committed as confirmed composition text. | 367 // can always be committed as confirmed composition text. |
| 361 return !is_composing_text_ && commit_text_.length() == 1; | 368 return !is_composing_text_ && commit_text_.length() == 1; |
| 362 } | 369 } |
| 363 | 370 |
| 364 bool GtkIMContextWrapper::HasInputMethodResult() const { | 371 bool GtkIMContextWrapper::HasInputMethodResult() const { |
| 365 return commit_text_.length() || is_preedit_changed_; | 372 return commit_text_.length() || is_composition_changed_; |
| 366 } | 373 } |
| 367 | 374 |
| 368 void GtkIMContextWrapper::ProcessFilteredKeyPressEvent( | 375 void GtkIMContextWrapper::ProcessFilteredKeyPressEvent( |
| 369 NativeWebKeyboardEvent* wke) { | 376 NativeWebKeyboardEvent* wke) { |
| 370 // If IME has filtered this event, then replace virtual key code with | 377 // If IME has filtered this event, then replace virtual key code with |
| 371 // VK_PROCESSKEY. See comment in ProcessKeyEvent() for details. | 378 // VK_PROCESSKEY. See comment in ProcessKeyEvent() for details. |
| 372 // It's only required for keydown events. | 379 // It's only required for keydown events. |
| 373 // To emulate windows behavior, when input method is enabled, if the commit | 380 // To emulate windows behavior, when input method is enabled, if the commit |
| 374 // text can be emulated by a Char event, then don't do this replacement. | 381 // text can be emulated by a Char event, then don't do this replacement. |
| 375 if (!NeedCommitByForwardingCharEvent()) { | 382 if (!NeedCommitByForwardingCharEvent()) { |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 437 host->ImeConfirmComposition(commit_text_); | 444 host->ImeConfirmComposition(commit_text_); |
| 438 // Set this flag to false, as this composition session has been | 445 // Set this flag to false, as this composition session has been |
| 439 // finished. | 446 // finished. |
| 440 is_composing_text_ = false; | 447 is_composing_text_ = false; |
| 441 } | 448 } |
| 442 } | 449 } |
| 443 | 450 |
| 444 // Send preedit text only if it's changed. | 451 // Send preedit text only if it's changed. |
| 445 // If a text has been committed, then we don't need to send the empty | 452 // If a text has been committed, then we don't need to send the empty |
| 446 // preedit text again. | 453 // preedit text again. |
| 447 if (is_preedit_changed_) { | 454 if (is_composition_changed_) { |
| 448 if (preedit_text_.length()) { | 455 if (composition_.text.length()) { |
| 449 // Another composition session has been started. | 456 // Another composition session has been started. |
| 450 is_composing_text_ = true; | 457 is_composing_text_ = true; |
| 451 host->ImeSetComposition(preedit_text_, preedit_underlines_, | 458 // TODO(suzhe): convert both renderer_host and renderer to use |
| 452 preedit_selection_start_, preedit_selection_end_); | 459 // ui::CompositionText. |
| 460 const std::vector<WebKit::WebCompositionUnderline>& underlines = |
| 461 reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>( |
| 462 composition_.underlines); |
| 463 host->ImeSetComposition(composition_.text, underlines, |
| 464 composition_.selection.start(), |
| 465 composition_.selection.end()); |
| 453 } else if (!committed) { | 466 } else if (!committed) { |
| 454 host->ImeCancelComposition(); | 467 host->ImeCancelComposition(); |
| 455 } | 468 } |
| 456 } | 469 } |
| 457 } | 470 } |
| 458 | 471 |
| 459 void GtkIMContextWrapper::ConfirmComposition() { | 472 void GtkIMContextWrapper::ConfirmComposition() { |
| 460 if (!is_enabled_) | 473 if (!is_enabled_) |
| 461 return; | 474 return; |
| 462 | 475 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 501 is_composing_text_ = true; | 514 is_composing_text_ = true; |
| 502 } | 515 } |
| 503 | 516 |
| 504 void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, | 517 void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, |
| 505 PangoAttrList* attrs, | 518 PangoAttrList* attrs, |
| 506 int cursor_position) { | 519 int cursor_position) { |
| 507 // Ignore preedit related signals triggered by CancelComposition() method. | 520 // Ignore preedit related signals triggered by CancelComposition() method. |
| 508 if (suppress_next_commit_) | 521 if (suppress_next_commit_) |
| 509 return; | 522 return; |
| 510 | 523 |
| 511 // Don't set is_preedit_changed_ to false if there is no change, because | 524 // Don't set is_composition_changed_ to false if there is no change, because |
| 512 // this handler might be called multiple times with the same data. | 525 // this handler might be called multiple times with the same data. |
| 513 is_preedit_changed_ = true; | 526 is_composition_changed_ = true; |
| 514 preedit_text_.clear(); | 527 composition_.Clear(); |
| 515 preedit_underlines_.clear(); | |
| 516 preedit_selection_start_ = 0; | |
| 517 preedit_selection_end_ = 0; | |
| 518 | 528 |
| 519 ExtractCompositionInfo(text, attrs, cursor_position, &preedit_text_, | 529 ui::ExtractCompositionTextFromGtkPreedit(text, attrs, cursor_position, |
| 520 &preedit_underlines_, &preedit_selection_start_, | 530 &composition_); |
| 521 &preedit_selection_end_); | 531 |
| 532 // TODO(suzhe): due to a bug of webkit, we currently can't use selection range |
| 533 // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805 |
| 534 composition_.selection = ui::Range(cursor_position); |
| 522 | 535 |
| 523 // In case we are using a buggy input method which doesn't fire | 536 // In case we are using a buggy input method which doesn't fire |
| 524 // "preedit_start" signal. | 537 // "preedit_start" signal. |
| 525 if (preedit_text_.length()) | 538 if (composition_.text.length()) |
| 526 is_composing_text_ = true; | 539 is_composing_text_ = true; |
| 527 | 540 |
| 528 // Nothing needs to do, if it's currently in ProcessKeyEvent() | 541 // Nothing needs to do, if it's currently in ProcessKeyEvent() |
| 529 // handler, which will send preedit text to webkit later. | 542 // handler, which will send preedit text to webkit later. |
| 530 // Otherwise, we need send it here if it's been changed. | 543 // Otherwise, we need send it here if it's been changed. |
| 531 if (!is_in_key_event_handler_ && is_composing_text_ && | 544 if (!is_in_key_event_handler_ && is_composing_text_ && |
| 532 host_view_->GetRenderWidgetHost()) { | 545 host_view_->GetRenderWidgetHost()) { |
| 533 // Workaround http://crbug.com/45478 by sending fake key down/up events. | 546 // Workaround http://crbug.com/45478 by sending fake key down/up events. |
| 534 SendFakeCompositionKeyEvent(WebKit::WebInputEvent::RawKeyDown); | 547 SendFakeCompositionKeyEvent(WebKit::WebInputEvent::RawKeyDown); |
| 548 // TODO(suzhe): convert both renderer_host and renderer to use |
| 549 // ui::CompositionText. |
| 550 const std::vector<WebKit::WebCompositionUnderline>& underlines = |
| 551 reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>( |
| 552 composition_.underlines); |
| 535 host_view_->GetRenderWidgetHost()->ImeSetComposition( | 553 host_view_->GetRenderWidgetHost()->ImeSetComposition( |
| 536 preedit_text_, preedit_underlines_, preedit_selection_start_, | 554 composition_.text, underlines, composition_.selection.start(), |
| 537 preedit_selection_end_); | 555 composition_.selection.end()); |
| 538 SendFakeCompositionKeyEvent(WebKit::WebInputEvent::KeyUp); | 556 SendFakeCompositionKeyEvent(WebKit::WebInputEvent::KeyUp); |
| 539 } | 557 } |
| 540 } | 558 } |
| 541 | 559 |
| 542 void GtkIMContextWrapper::HandlePreeditEnd() { | 560 void GtkIMContextWrapper::HandlePreeditEnd() { |
| 543 if (preedit_text_.length()) { | 561 if (composition_.text.length()) { |
| 544 // The composition session has been finished. | 562 // The composition session has been finished. |
| 545 preedit_text_.clear(); | 563 composition_.Clear(); |
| 546 preedit_underlines_.clear(); | 564 is_composition_changed_ = true; |
| 547 is_preedit_changed_ = true; | |
| 548 | 565 |
| 549 // If there is still a preedit text when firing "preedit-end" signal, | 566 // If there is still a preedit text when firing "preedit-end" signal, |
| 550 // we need inform webkit to clear it. | 567 // we need inform webkit to clear it. |
| 551 // It's only necessary when it's not in ProcessKeyEvent (). | 568 // It's only necessary when it's not in ProcessKeyEvent (). |
| 552 if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) | 569 if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) |
| 553 host_view_->GetRenderWidgetHost()->ImeCancelComposition(); | 570 host_view_->GetRenderWidgetHost()->ImeCancelComposition(); |
| 554 } | 571 } |
| 555 | 572 |
| 556 // Don't set is_composing_text_ to false here, because "preedit_end" | 573 // Don't set is_composing_text_ to false here, because "preedit_end" |
| 557 // signal may be fired before "commit" signal. | 574 // signal may be fired before "commit" signal. |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 609 | 626 |
| 610 void GtkIMContextWrapper::HandleHostViewRealizeThunk( | 627 void GtkIMContextWrapper::HandleHostViewRealizeThunk( |
| 611 GtkWidget* widget, GtkIMContextWrapper* self) { | 628 GtkWidget* widget, GtkIMContextWrapper* self) { |
| 612 self->HandleHostViewRealize(widget); | 629 self->HandleHostViewRealize(widget); |
| 613 } | 630 } |
| 614 | 631 |
| 615 void GtkIMContextWrapper::HandleHostViewUnrealizeThunk( | 632 void GtkIMContextWrapper::HandleHostViewUnrealizeThunk( |
| 616 GtkWidget* widget, GtkIMContextWrapper* self) { | 633 GtkWidget* widget, GtkIMContextWrapper* self) { |
| 617 self->HandleHostViewUnrealize(); | 634 self->HandleHostViewUnrealize(); |
| 618 } | 635 } |
| 619 | |
| 620 void GtkIMContextWrapper::ExtractCompositionInfo( | |
| 621 const gchar* utf8_text, | |
| 622 PangoAttrList* attrs, | |
| 623 int cursor_position, | |
| 624 string16* utf16_text, | |
| 625 std::vector<WebKit::WebCompositionUnderline>* underlines, | |
| 626 int* selection_start, | |
| 627 int* selection_end) { | |
| 628 *utf16_text = UTF8ToUTF16(utf8_text); | |
| 629 | |
| 630 if (utf16_text->empty()) | |
| 631 return; | |
| 632 | |
| 633 // Gtk/Pango uses character index for cursor position and byte index for | |
| 634 // attribute range, but we use char16 offset for them. So we need to do | |
| 635 // conversion here. | |
| 636 std::vector<int> char16_offsets; | |
| 637 int length = static_cast<int>(utf16_text->length()); | |
| 638 for (int offset = 0; offset < length; ++offset) { | |
| 639 char16_offsets.push_back(offset); | |
| 640 if (CBU16_IS_SURROGATE((*utf16_text)[offset])) | |
| 641 ++offset; | |
| 642 } | |
| 643 | |
| 644 // The text length in Unicode characters. | |
| 645 int char_length = static_cast<int>(char16_offsets.size()); | |
| 646 // Make sure we can convert the value of |char_length| as well. | |
| 647 char16_offsets.push_back(length); | |
| 648 | |
| 649 int cursor_offset = | |
| 650 char16_offsets[std::max(0, std::min(char_length, cursor_position))]; | |
| 651 | |
| 652 // TODO(suzhe): due to a bug of webkit, we currently can't use selection range | |
| 653 // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805 | |
| 654 *selection_start = *selection_end = cursor_offset; | |
| 655 | |
| 656 if (attrs) { | |
| 657 int utf8_length = strlen(utf8_text); | |
| 658 PangoAttrIterator* iter = pango_attr_list_get_iterator(attrs); | |
| 659 | |
| 660 // We only care about underline and background attributes and convert | |
| 661 // background attribute into selection if possible. | |
| 662 do { | |
| 663 gint start, end; | |
| 664 pango_attr_iterator_range(iter, &start, &end); | |
| 665 | |
| 666 start = std::min(start, utf8_length); | |
| 667 end = std::min(end, utf8_length); | |
| 668 if (start >= end) | |
| 669 continue; | |
| 670 | |
| 671 start = g_utf8_pointer_to_offset(utf8_text, utf8_text + start); | |
| 672 end = g_utf8_pointer_to_offset(utf8_text, utf8_text + end); | |
| 673 | |
| 674 // Double check, in case |utf8_text| is not a valid utf-8 string. | |
| 675 start = std::min(start, char_length); | |
| 676 end = std::min(end, char_length); | |
| 677 if (start >= end) | |
| 678 continue; | |
| 679 | |
| 680 PangoAttribute* background_attr = | |
| 681 pango_attr_iterator_get(iter, PANGO_ATTR_BACKGROUND); | |
| 682 PangoAttribute* underline_attr = | |
| 683 pango_attr_iterator_get(iter, PANGO_ATTR_UNDERLINE); | |
| 684 | |
| 685 if (background_attr || underline_attr) { | |
| 686 // Use a black thin underline by default. | |
| 687 WebKit::WebCompositionUnderline underline( | |
| 688 char16_offsets[start], char16_offsets[end], SK_ColorBLACK, false); | |
| 689 | |
| 690 // Always use thick underline for a range with background color, which | |
| 691 // is usually the selection range. | |
| 692 if (background_attr) | |
| 693 underline.thick = true; | |
| 694 if (underline_attr) { | |
| 695 int type = reinterpret_cast<PangoAttrInt*>(underline_attr)->value; | |
| 696 if (type == PANGO_UNDERLINE_DOUBLE) | |
| 697 underline.thick = true; | |
| 698 else if (type == PANGO_UNDERLINE_ERROR) | |
| 699 underline.color = SK_ColorRED; | |
| 700 } | |
| 701 underlines->push_back(underline); | |
| 702 } | |
| 703 } while (pango_attr_iterator_next(iter)); | |
| 704 pango_attr_iterator_destroy(iter); | |
| 705 } | |
| 706 | |
| 707 // Use a black thin underline by default. | |
| 708 if (underlines->empty()) { | |
| 709 underlines->push_back( | |
| 710 WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false)); | |
| 711 } | |
| 712 } | |
| OLD | NEW |