Home | History | Annotate | Download | only in textfield
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/views/controls/textfield/native_textfield_win.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/i18n/case_conversion.h"
     10 #include "base/i18n/rtl.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/win/metro.h"
     14 #include "base/win/windows_version.h"
     15 #include "grit/ui_strings.h"
     16 #include "skia/ext/skia_utils_win.h"
     17 #include "ui/base/accessibility/accessible_view_state.h"
     18 #include "ui/base/clipboard/clipboard.h"
     19 #include "ui/base/clipboard/scoped_clipboard_writer.h"
     20 #include "ui/base/events/event.h"
     21 #include "ui/base/ime/win/tsf_bridge.h"
     22 #include "ui/base/keycodes/keyboard_codes.h"
     23 #include "ui/base/l10n/l10n_util.h"
     24 #include "ui/base/l10n/l10n_util_win.h"
     25 #include "ui/base/range/range.h"
     26 #include "ui/base/win/hwnd_util.h"
     27 #include "ui/base/win/mouse_wheel_util.h"
     28 #include "ui/native_theme/native_theme_win.h"
     29 #include "ui/views/controls/label.h"
     30 #include "ui/views/controls/menu/menu_item_view.h"
     31 #include "ui/views/controls/menu/menu_model_adapter.h"
     32 #include "ui/views/controls/menu/menu_runner.h"
     33 #include "ui/views/controls/native/native_view_host.h"
     34 #include "ui/views/controls/textfield/textfield.h"
     35 #include "ui/views/controls/textfield/textfield_controller.h"
     36 #include "ui/views/focus/focus_manager.h"
     37 #include "ui/views/metrics.h"
     38 #include "ui/views/widget/widget.h"
     39 
     40 namespace views {
     41 
     42 ///////////////////////////////////////////////////////////////////////////////
     43 // Helper classes
     44 
     45 NativeTextfieldWin::ScopedFreeze::ScopedFreeze(NativeTextfieldWin* edit,
     46                                                ITextDocument* text_object_model)
     47     : edit_(edit),
     48       text_object_model_(text_object_model) {
     49   // Freeze the screen.
     50   if (text_object_model_) {
     51     long count;
     52     text_object_model_->Freeze(&count);
     53   }
     54 }
     55 
     56 NativeTextfieldWin::ScopedFreeze::~ScopedFreeze() {
     57   // Unfreeze the screen.
     58   if (text_object_model_) {
     59     long count;
     60     text_object_model_->Unfreeze(&count);
     61     if (count == 0) {
     62       // We need to UpdateWindow() here instead of InvalidateRect() because, as
     63       // far as I can tell, the edit likes to synchronously erase its background
     64       // when unfreezing, thus requiring us to synchronously redraw if we don't
     65       // want flicker.
     66       edit_->UpdateWindow();
     67     }
     68   }
     69 }
     70 
     71 NativeTextfieldWin::ScopedSuspendUndo::ScopedSuspendUndo(
     72     ITextDocument* text_object_model)
     73     : text_object_model_(text_object_model) {
     74   // Suspend Undo processing.
     75   if (text_object_model_)
     76     text_object_model_->Undo(tomSuspend, NULL);
     77 }
     78 
     79 NativeTextfieldWin::ScopedSuspendUndo::~ScopedSuspendUndo() {
     80   // Resume Undo processing.
     81   if (text_object_model_)
     82     text_object_model_->Undo(tomResume, NULL);
     83 }
     84 
     85 ///////////////////////////////////////////////////////////////////////////////
     86 // NativeTextfieldWin
     87 
     88 HMODULE NativeTextfieldWin::loaded_libarary_module_ = false;
     89 
     90 NativeTextfieldWin::NativeTextfieldWin(Textfield* textfield)
     91     : textfield_(textfield),
     92       tracking_double_click_(false),
     93       double_click_time_(0),
     94       can_discard_mousemove_(false),
     95       contains_mouse_(false),
     96       ime_discard_composition_(false),
     97       ime_composition_start_(0),
     98       ime_composition_length_(0),
     99       container_view_(new NativeViewHost),
    100       bg_color_(0),
    101       tsf_event_router_(base::win::IsTSFAwareRequired() ?
    102           new ui::TSFEventRouter(this) : NULL) {
    103   if (!loaded_libarary_module_) {
    104     // msftedit.dll is RichEdit ver 4.1.
    105     // This version is available from WinXP SP1 and has TSF support.
    106     loaded_libarary_module_ = LoadLibrary(L"msftedit.dll");
    107   }
    108 
    109   DWORD style = kDefaultEditStyle | ES_AUTOHSCROLL;
    110   if (textfield_->style() & Textfield::STYLE_OBSCURED)
    111     style |= ES_PASSWORD;
    112 
    113   if (textfield_->read_only())
    114     style |= ES_READONLY;
    115 
    116   // Make sure we apply RTL related extended window styles if necessary.
    117   DWORD ex_style = l10n_util::GetExtendedStyles();
    118 
    119   RECT r = {0, 0, textfield_->width(), textfield_->height()};
    120   Create(textfield_->GetWidget()->GetNativeView(), r, NULL, style, ex_style);
    121 
    122   if (textfield_->style() & Textfield::STYLE_LOWERCASE) {
    123     DCHECK((textfield_->style() & Textfield::STYLE_OBSCURED) == 0);
    124     SetEditStyle(SES_LOWERCASE, SES_LOWERCASE);
    125   }
    126 
    127   // Disable auto font changing. Otherwise, characters can be rendered with
    128   // multiple fonts. See http://crbug.com/168480 for details.
    129   const LRESULT lang_option = SendMessage(m_hWnd, EM_GETLANGOPTIONS, 0, 0);
    130   SendMessage(EM_SETLANGOPTIONS, 0, lang_option & ~IMF_AUTOFONT);
    131 
    132   // Set up the text_object_model_.
    133   base::win::ScopedComPtr<IRichEditOle, &IID_IRichEditOle> ole_interface;
    134   ole_interface.Attach(GetOleInterface());
    135   if (ole_interface)
    136     text_object_model_.QueryFrom(ole_interface);
    137 
    138   InitializeAccessibilityInfo();
    139 }
    140 
    141 NativeTextfieldWin::~NativeTextfieldWin() {
    142   if (IsWindow())
    143     DestroyWindow();
    144 }
    145 
    146 // static
    147 bool NativeTextfieldWin::IsDoubleClick(const POINT& origin,
    148                                        const POINT& current,
    149                                        DWORD elapsed_time) {
    150   // The CXDOUBLECLK and CYDOUBLECLK system metrics describe the width and
    151   // height of a rectangle around the origin position, inside of which clicks
    152   // within the double click time are considered double clicks.
    153   return (elapsed_time <= GetDoubleClickTime()) &&
    154       (abs(current.x - origin.x) <= (GetSystemMetrics(SM_CXDOUBLECLK) / 2)) &&
    155       (abs(current.y - origin.y) <= (GetSystemMetrics(SM_CYDOUBLECLK) / 2));
    156 }
    157 
    158 void NativeTextfieldWin::AttachHack() {
    159   // See the code in textfield.cc that calls this for why this is here.
    160   container_view_->set_focus_view(textfield_);
    161   container_view_->Attach(m_hWnd);
    162 }
    163 
    164 ////////////////////////////////////////////////////////////////////////////////
    165 // NativeTextfieldWin, NativeTextfieldWrapper implementation:
    166 
    167 string16 NativeTextfieldWin::GetText() const {
    168   int len = GetTextLength() + 1;
    169   string16 str;
    170   if (len > 1)
    171     GetWindowText(WriteInto(&str, len), len);
    172   // The text get from GetWindowText() might be wrapped with explicit bidi
    173   // control characters. Refer to UpdateText() for detail. Without such
    174   // wrapping, in RTL chrome, a pure LTR string ending with parenthesis will
    175   // not be displayed correctly in a textfield. For example, "Yahoo!" will be
    176   // displayed as "!Yahoo", and "Google (by default)" will be displayed as
    177   // "(Google (by default".
    178   return base::i18n::StripWrappingBidiControlCharacters(str);
    179 }
    180 
    181 void NativeTextfieldWin::UpdateText() {
    182   string16 text = textfield_->text();
    183   // Adjusting the string direction before setting the text in order to make
    184   // sure both RTL and LTR strings are displayed properly.
    185   base::i18n::AdjustStringForLocaleDirection(&text);
    186   if (textfield_->style() & Textfield::STYLE_LOWERCASE)
    187     text = base::i18n::ToLower(text);
    188   SetWindowText(text.c_str());
    189   UpdateAccessibleValue(text);
    190 }
    191 
    192 void NativeTextfieldWin::AppendText(const string16& text) {
    193   int text_length = GetWindowTextLength();
    194   ::SendMessage(m_hWnd, TBM_SETSEL, true, MAKELPARAM(text_length, text_length));
    195   ::SendMessage(m_hWnd, EM_REPLACESEL, false,
    196                 reinterpret_cast<LPARAM>(text.c_str()));
    197 }
    198 
    199 void NativeTextfieldWin::InsertOrReplaceText(const string16& text) {
    200   // Currently not needed.
    201   NOTIMPLEMENTED();
    202 }
    203 
    204 base::i18n::TextDirection NativeTextfieldWin::GetTextDirection() const {
    205   NOTIMPLEMENTED();
    206   return base::i18n::UNKNOWN_DIRECTION;
    207 }
    208 
    209 string16 NativeTextfieldWin::GetSelectedText() const {
    210   CHARRANGE sel;
    211   GetSel(sel);
    212   string16 str;
    213   if (sel.cpMin != sel.cpMax)
    214     GetSelText(WriteInto(&str, sel.cpMax - sel.cpMin + 1));
    215   return str;
    216 }
    217 
    218 void NativeTextfieldWin::SelectAll(bool reversed) {
    219   if (reversed)
    220     SetSel(GetTextLength(), 0);
    221   else
    222     SetSel(0, GetTextLength());
    223 }
    224 
    225 void NativeTextfieldWin::ClearSelection() {
    226   SetSel(GetTextLength(), GetTextLength());
    227 }
    228 
    229 void NativeTextfieldWin::UpdateBorder() {
    230   SetWindowPos(NULL, 0, 0, 0, 0,
    231                SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOACTIVATE |
    232                SWP_NOOWNERZORDER | SWP_NOSIZE);
    233 }
    234 
    235 void NativeTextfieldWin::UpdateTextColor() {
    236   CHARFORMAT cf = {0};
    237   cf.dwMask = CFM_COLOR;
    238   cf.crTextColor = skia::SkColorToCOLORREF(textfield_->GetTextColor());
    239   CRichEditCtrl::SetDefaultCharFormat(cf);
    240 }
    241 
    242 void NativeTextfieldWin::UpdateBackgroundColor() {
    243   CRichEditCtrl::SetBackgroundColor(
    244       skia::SkColorToCOLORREF(textfield_->GetBackgroundColor()));
    245 }
    246 
    247 void NativeTextfieldWin::UpdateReadOnly() {
    248   SendMessage(m_hWnd, EM_SETREADONLY, textfield_->read_only(), 0);
    249   UpdateAccessibleState(STATE_SYSTEM_READONLY, textfield_->read_only());
    250 }
    251 
    252 void NativeTextfieldWin::UpdateFont() {
    253   SendMessage(m_hWnd, WM_SETFONT,
    254               reinterpret_cast<WPARAM>(
    255                   textfield_->GetPrimaryFont().GetNativeFont()),
    256               TRUE);
    257   // Setting the font blows away any text color we've set, so reset it.
    258   UpdateTextColor();
    259 }
    260 
    261 void NativeTextfieldWin::UpdateIsObscured() {
    262   // TODO: Need to implement for Windows.
    263   UpdateAccessibleState(STATE_SYSTEM_PROTECTED, textfield_->IsObscured());
    264 }
    265 
    266 void NativeTextfieldWin::UpdateEnabled() {
    267   SendMessage(m_hWnd, WM_ENABLE, textfield_->enabled(), 0);
    268   UpdateAccessibleState(STATE_SYSTEM_UNAVAILABLE, !textfield_->enabled());
    269 }
    270 
    271 gfx::Insets NativeTextfieldWin::CalculateInsets() {
    272   // NOTE: One would think GetThemeMargins would return the insets we should
    273   // use, but it doesn't. The margins returned by GetThemeMargins are always
    274   // 0.
    275 
    276   // This appears to be the insets used by Windows.
    277   return gfx::Insets(3, 3, 3, 3);
    278 }
    279 
    280 void NativeTextfieldWin::UpdateHorizontalMargins() {
    281   int left, right;
    282   if (!textfield_->GetHorizontalMargins(&left, &right))
    283     return;
    284 
    285   // SendMessage expects the two values to be packed into one using MAKELONG
    286   // so we truncate to 16 bits if necessary.
    287   SendMessage(m_hWnd, EM_SETMARGINS,
    288               EC_LEFTMARGIN | EC_RIGHTMARGIN,
    289               MAKELONG(left  & 0xFFFF, right & 0xFFFF));
    290 }
    291 
    292 void NativeTextfieldWin::UpdateVerticalMargins() {
    293   int top, bottom;
    294   if (!textfield_->GetVerticalMargins(&top, &bottom))
    295     return;
    296 
    297   if (top == 0 && bottom == 0) {
    298     // Do nothing, default margins are 0 already.
    299     return;
    300   }
    301   // Non-zero margins case.
    302   NOTIMPLEMENTED();
    303 }
    304 
    305 void NativeTextfieldWin::UpdateVerticalAlignment() {
    306   // Default alignment is vertically centered.
    307   if (textfield_->vertical_alignment() != gfx::ALIGN_VCENTER)
    308     NOTIMPLEMENTED();
    309 }
    310 
    311 bool NativeTextfieldWin::SetFocus() {
    312   // Focus the associated HWND.
    313   //container_view_->Focus();
    314   ::SetFocus(m_hWnd);
    315   return true;
    316 }
    317 
    318 View* NativeTextfieldWin::GetView() {
    319   return container_view_;
    320 }
    321 
    322 gfx::NativeView NativeTextfieldWin::GetTestingHandle() const {
    323   return m_hWnd;
    324 }
    325 
    326 bool NativeTextfieldWin::IsIMEComposing() const {
    327   // Retrieve the length of the composition string to check if an IME is
    328   // composing text. (If this length is > 0 then an IME is being used to compose
    329   // text.)
    330   if (base::win::IsTSFAwareRequired())
    331     return tsf_event_router_->IsImeComposing();
    332 
    333   HIMC imm_context = ImmGetContext(m_hWnd);
    334   if (!imm_context)
    335     return false;
    336 
    337   const int composition_size = ImmGetCompositionString(imm_context, GCS_COMPSTR,
    338                                                        NULL, 0);
    339   ImmReleaseContext(m_hWnd, imm_context);
    340   return composition_size > 0;
    341 }
    342 
    343 ui::Range NativeTextfieldWin::GetSelectedRange() const {
    344   // TODO(tommi): Implement.
    345   NOTIMPLEMENTED();
    346   return ui::Range();
    347 }
    348 
    349 void NativeTextfieldWin::SelectRange(const ui::Range& range) {
    350   // TODO(tommi): Implement.
    351   NOTIMPLEMENTED();
    352 }
    353 
    354 gfx::SelectionModel NativeTextfieldWin::GetSelectionModel() const {
    355   // TODO(tommi): Implement.
    356   NOTIMPLEMENTED();
    357   return gfx::SelectionModel();
    358 }
    359 
    360 void NativeTextfieldWin::SelectSelectionModel(const gfx::SelectionModel& sel) {
    361   // TODO(tommi): Implement.
    362   NOTIMPLEMENTED();
    363 }
    364 
    365 size_t NativeTextfieldWin::GetCursorPosition() const {
    366   // TODO(tommi): Implement.
    367   NOTIMPLEMENTED();
    368   return 0U;
    369 }
    370 
    371 bool NativeTextfieldWin::GetCursorEnabled() const {
    372   // TODO(msw): Implement.
    373   NOTIMPLEMENTED();
    374   return true;
    375 }
    376 
    377 void NativeTextfieldWin::SetCursorEnabled(bool enabled) {
    378   // TODO(msw): Implement.
    379   NOTIMPLEMENTED();
    380 }
    381 
    382 bool NativeTextfieldWin::HandleKeyPressed(const ui::KeyEvent& event) {
    383   return false;
    384 }
    385 
    386 bool NativeTextfieldWin::HandleKeyReleased(const ui::KeyEvent& event) {
    387   return false;
    388 }
    389 
    390 void NativeTextfieldWin::HandleFocus() {
    391 }
    392 
    393 void NativeTextfieldWin::HandleBlur() {
    394 }
    395 
    396 ui::TextInputClient* NativeTextfieldWin::GetTextInputClient() {
    397   return NULL;
    398 }
    399 
    400 void NativeTextfieldWin::SetColor(SkColor value) {
    401   NOTREACHED();
    402 }
    403 
    404 void NativeTextfieldWin::ApplyColor(SkColor value, const ui::Range& range) {
    405   NOTREACHED();
    406 }
    407 
    408 void NativeTextfieldWin::SetStyle(gfx::TextStyle style, bool value) {
    409   NOTREACHED();
    410 }
    411 
    412 void NativeTextfieldWin::ApplyStyle(gfx::TextStyle style,
    413                                     bool value,
    414                                     const ui::Range& range) {
    415   NOTREACHED();
    416 }
    417 
    418 void NativeTextfieldWin::ClearEditHistory() {
    419   NOTREACHED();
    420 }
    421 
    422 int NativeTextfieldWin::GetFontHeight() {
    423   return textfield_->font_list().GetHeight();
    424 }
    425 
    426 int NativeTextfieldWin::GetTextfieldBaseline() const {
    427   return textfield_->font_list().GetBaseline();
    428 }
    429 
    430 int NativeTextfieldWin::GetWidthNeededForText() const {
    431   NOTIMPLEMENTED();
    432   return 0;
    433 }
    434 
    435 void NativeTextfieldWin::ExecuteTextCommand(int command_id) {
    436   ExecuteCommand(command_id, 0);
    437 }
    438 
    439 bool NativeTextfieldWin::HasTextBeingDragged() {
    440   NOTIMPLEMENTED();
    441   return false;
    442 }
    443 
    444 ////////////////////////////////////////////////////////////////////////////////
    445 // NativeTextfieldWin, ui::SimpleMenuModel::Delegate implementation:
    446 
    447 bool NativeTextfieldWin::IsCommandIdChecked(int command_id) const {
    448   return false;
    449 }
    450 
    451 bool NativeTextfieldWin::IsCommandIdEnabled(int command_id) const {
    452   switch (command_id) {
    453     case IDS_APP_UNDO:       return !textfield_->read_only() && !!CanUndo();
    454     case IDS_APP_CUT:        return !textfield_->read_only() &&
    455                                     !textfield_->IsObscured() && !!CanCut();
    456     case IDS_APP_COPY:       return !!CanCopy() && !textfield_->IsObscured();
    457     case IDS_APP_PASTE:      return !textfield_->read_only() && !!CanPaste();
    458     case IDS_APP_SELECT_ALL: return !!CanSelectAll();
    459     default:                 NOTREACHED();
    460                              return false;
    461   }
    462 }
    463 
    464 bool NativeTextfieldWin::GetAcceleratorForCommandId(int command_id,
    465     ui::Accelerator* accelerator) {
    466   // The standard Ctrl-X, Ctrl-V and Ctrl-C are not defined as accelerators
    467   // anywhere so we need to check for them explicitly here.
    468   switch (command_id) {
    469     case IDS_APP_CUT:
    470       *accelerator = ui::Accelerator(ui::VKEY_X, ui::EF_CONTROL_DOWN);
    471       return true;
    472     case IDS_APP_COPY:
    473       *accelerator = ui::Accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
    474       return true;
    475     case IDS_APP_PASTE:
    476       *accelerator = ui::Accelerator(ui::VKEY_V, ui::EF_CONTROL_DOWN);
    477       return true;
    478   }
    479   return container_view_->GetWidget()->GetAccelerator(command_id, accelerator);
    480 }
    481 
    482 void NativeTextfieldWin::ExecuteCommand(int command_id, int event_flags) {
    483   ScopedFreeze freeze(this, GetTextObjectModel());
    484   OnBeforePossibleChange();
    485   switch (command_id) {
    486     case IDS_APP_UNDO:       Undo();           break;
    487     case IDS_APP_CUT:        Cut();            break;
    488     case IDS_APP_COPY:       Copy();           break;
    489     case IDS_APP_PASTE:      Paste();          break;
    490     case IDS_APP_SELECT_ALL: SelectAll(false); break;
    491     default:                 NOTREACHED();     break;
    492   }
    493   OnAfterPossibleChange(true);
    494 }
    495 
    496 void NativeTextfieldWin::OnTextUpdated(const ui::Range& composition_range) {
    497   if (ime_discard_composition_) {
    498     ime_composition_start_ = composition_range.start();
    499     ime_composition_length_ = composition_range.length();
    500   } else {
    501     ime_composition_start_ = 0;
    502     ime_composition_length_ = 0;
    503   }
    504   OnAfterPossibleChange(false);
    505   text_before_change_.clear();
    506 }
    507 
    508 void NativeTextfieldWin::OnImeStartCompositionInternal() {
    509   // Users may press alt+shift or control+shift keys to change their keyboard
    510   // layouts. So, we retrieve the input locale identifier everytime we start
    511   // an IME composition.
    512   int language_id = PRIMARYLANGID(GetKeyboardLayout(0));
    513   ime_discard_composition_ =
    514       language_id == LANG_JAPANESE || language_id == LANG_CHINESE;
    515   ime_composition_start_ = 0;
    516   ime_composition_length_ = 0;
    517 }
    518 
    519 void NativeTextfieldWin::OnImeEndCompositionInternal() {
    520   // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without
    521   // sending any WM_IME_COMPOSITION messages when a user deletes all
    522   // composition characters, i.e. a composition string becomes empty. To handle
    523   // this case, we need to update the find results when a composition is
    524   // finished or canceled.
    525   textfield_->SyncText();
    526 }
    527 
    528 void NativeTextfieldWin::OnTSFStartComposition() {
    529   OnImeStartCompositionInternal();
    530 }
    531 
    532 void NativeTextfieldWin::OnTSFEndComposition() {
    533   OnImeEndCompositionInternal();
    534 }
    535 
    536 void NativeTextfieldWin::InitializeAccessibilityInfo() {
    537   // Set the accessible state.
    538   accessibility_state_ = 0;
    539 
    540   base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
    541   HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
    542       IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
    543   if (!SUCCEEDED(hr))
    544     return;
    545 
    546   VARIANT var;
    547 
    548   // Set the accessible role.
    549   var.vt = VT_I4;
    550   var.lVal = ROLE_SYSTEM_TEXT;
    551   hr = pAccPropServices->SetHwndProp(m_hWnd, OBJID_CLIENT,
    552       CHILDID_SELF, PROPID_ACC_ROLE, var);
    553 
    554   // Set the accessible name by getting the label text.
    555   View* parent = textfield_->parent();
    556   int label_index = parent->GetIndexOf(textfield_) - 1;
    557   if (label_index >= 0) {
    558     // Try to find the name of this text field.
    559     // We expect it to be a Label preceeding this view (if it exists).
    560     string16 name;
    561     View* label_view = parent->child_at(label_index);
    562     if (!strcmp(label_view->GetClassName(), Label::kViewClassName)) {
    563       ui::AccessibleViewState state;
    564       label_view->GetAccessibleState(&state);
    565       hr = pAccPropServices->SetHwndPropStr(m_hWnd, OBJID_CLIENT,
    566           CHILDID_SELF, PROPID_ACC_NAME, state.name.c_str());
    567     }
    568   }
    569 }
    570 
    571 void NativeTextfieldWin::UpdateAccessibleState(uint32 state_flag,
    572                                                bool set_value) {
    573   base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
    574   HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
    575       IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
    576   if (!SUCCEEDED(hr))
    577     return;
    578 
    579   VARIANT var;
    580   var.vt = VT_I4;
    581   var.lVal = set_value ? accessibility_state_ | state_flag
    582       : accessibility_state_ & ~state_flag;
    583   hr = pAccPropServices->SetHwndProp(m_hWnd, OBJID_CLIENT,
    584       CHILDID_SELF, PROPID_ACC_STATE, var);
    585 
    586   ::NotifyWinEvent(EVENT_OBJECT_STATECHANGE, m_hWnd, OBJID_CLIENT,
    587                    CHILDID_SELF);
    588 }
    589 
    590 void NativeTextfieldWin::UpdateAccessibleValue(const string16& value) {
    591   base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
    592   HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
    593       IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
    594   if (!SUCCEEDED(hr))
    595     return;
    596 
    597   hr = pAccPropServices->SetHwndPropStr(m_hWnd, OBJID_CLIENT,
    598       CHILDID_SELF, PROPID_ACC_VALUE, value.c_str());
    599 
    600   ::NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, m_hWnd, OBJID_CLIENT,
    601                    CHILDID_SELF);
    602 }
    603 
    604 ////////////////////////////////////////////////////////////////////////////////
    605 // NativeTextfieldWin, private:
    606 
    607 void NativeTextfieldWin::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
    608   HandleKeystroke();
    609 }
    610 
    611 void NativeTextfieldWin::OnContextMenu(HWND window, const POINT& point) {
    612   POINT p(point);
    613   ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
    614   if (point.x == -1 || point.y == -1) {
    615     source_type = ui::MENU_SOURCE_KEYBOARD;
    616     GetCaretPos(&p);
    617     MapWindowPoints(HWND_DESKTOP, &p, 1);
    618   }
    619   BuildContextMenu();
    620 
    621   MenuModelAdapter adapter(context_menu_contents_.get());
    622   context_menu_runner_.reset(new MenuRunner(adapter.CreateMenu()));
    623 
    624   ignore_result(context_menu_runner_->RunMenuAt(textfield_->GetWidget(), NULL,
    625       gfx::Rect(gfx::Point(p), gfx::Size()), MenuItemView::TOPLEFT,
    626       source_type, MenuRunner::HAS_MNEMONICS));
    627 }
    628 
    629 void NativeTextfieldWin::OnCopy() {
    630   if (textfield_->IsObscured())
    631     return;
    632 
    633   const string16 text(GetSelectedText());
    634   if (!text.empty()) {
    635     ui::ScopedClipboardWriter(
    636         ui::Clipboard::GetForCurrentThread(),
    637         ui::Clipboard::BUFFER_STANDARD).WriteText(text);
    638     if (TextfieldController* controller = textfield_->GetController())
    639       controller->OnAfterCutOrCopy();
    640   }
    641 }
    642 
    643 LRESULT NativeTextfieldWin::OnCreate(const CREATESTRUCTW* /*create_struct*/) {
    644   if (base::win::IsTSFAwareRequired()) {
    645     // Enable TSF support of RichEdit.
    646     SetEditStyle(SES_USECTF, SES_USECTF);
    647 
    648     // When TSF is enabled, OnTextUpdated() may be called without any previous
    649     // call that would have indicated the start of an editing session.  In order
    650     // to guarantee we've always called OnBeforePossibleChange() before
    651     // OnAfterPossibleChange(), we therefore call that here.  Note that multiple
    652     // (i.e. unmatched) calls to this function in a row are safe.
    653     OnBeforePossibleChange();
    654   }
    655   SetMsgHandled(FALSE);
    656   return 0;
    657 }
    658 
    659 void NativeTextfieldWin::OnCut() {
    660   if (textfield_->read_only() || textfield_->IsObscured())
    661     return;
    662 
    663   OnCopy();
    664 
    665   // This replace selection will have no effect (even on the undo stack) if the
    666   // current selection is empty.
    667   ReplaceSel(L"", true);
    668 }
    669 
    670 LRESULT NativeTextfieldWin::OnGetObject(UINT message,
    671                                         WPARAM wparam,
    672                                         LPARAM lparam) {
    673   LRESULT ret = 0;
    674   if (lparam == OBJID_CLIENT) {
    675     ret = LresultFromObject(IID_IAccessible, wparam,
    676         textfield_->GetNativeViewAccessible());
    677   }
    678   return ret;
    679 }
    680 
    681 LRESULT NativeTextfieldWin::OnImeChar(UINT message,
    682                                       WPARAM wparam,
    683                                       LPARAM lparam) {
    684   // http://crbug.com/7707: a rich-edit control may crash when it receives a
    685   // WM_IME_CHAR message while it is processing a WM_IME_COMPOSITION message.
    686   // Since view controls don't need WM_IME_CHAR messages, we prevent WM_IME_CHAR
    687   // messages from being dispatched to view controls via the CallWindowProc()
    688   // call.
    689   return 0;
    690 }
    691 
    692 LRESULT NativeTextfieldWin::OnImeStartComposition(UINT message,
    693                                                   WPARAM wparam,
    694                                                   LPARAM lparam) {
    695   OnImeStartCompositionInternal();
    696   return DefWindowProc(message, wparam, lparam);
    697 }
    698 
    699 LRESULT NativeTextfieldWin::OnImeComposition(UINT message,
    700                                              WPARAM wparam,
    701                                              LPARAM lparam) {
    702   text_before_change_.clear();
    703   LRESULT result = DefWindowProc(message, wparam, lparam);
    704 
    705   ime_composition_start_ = 0;
    706   ime_composition_length_ = 0;
    707   if (ime_discard_composition_) {
    708     // Call IMM32 functions to retrieve the position and the length of the
    709     // ongoing composition string and notify the OnAfterPossibleChange()
    710     // function that it should discard the composition string from a search
    711     // string. We should not call IMM32 functions in the function because it
    712     // is called when an IME is not composing a string.
    713     HIMC imm_context = ImmGetContext(m_hWnd);
    714     if (imm_context) {
    715       CHARRANGE selection;
    716       GetSel(selection);
    717       const int cursor_position =
    718           ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0);
    719       if (cursor_position >= 0)
    720         ime_composition_start_ = selection.cpMin - cursor_position;
    721 
    722       const int composition_size =
    723           ImmGetCompositionString(imm_context, GCS_COMPSTR, NULL, 0);
    724       if (composition_size >= 0)
    725         ime_composition_length_ = composition_size / sizeof(wchar_t);
    726 
    727       ImmReleaseContext(m_hWnd, imm_context);
    728     }
    729   }
    730 
    731   // If we allow OnAfterPossibleChange() to redraw the text, it will do this by
    732   // setting the edit's text directly, which can cancel the current IME
    733   // composition or cause other adverse affects. So we set |should_redraw_text|
    734   // to false.
    735   OnAfterPossibleChange(false);
    736   return result;
    737 }
    738 
    739 LRESULT NativeTextfieldWin::OnImeEndComposition(UINT message,
    740                                                 WPARAM wparam,
    741                                                 LPARAM lparam) {
    742   OnImeEndCompositionInternal();
    743   return DefWindowProc(message, wparam, lparam);
    744 }
    745 
    746 LRESULT NativeTextfieldWin::OnPointerDown(UINT message, WPARAM wparam,
    747                                           LPARAM lparam) {
    748   SetFocus();
    749   SetMsgHandled(FALSE);
    750   return 0;
    751 }
    752 
    753 void NativeTextfieldWin::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) {
    754   // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than
    755   // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places
    756   // in this function even with a WM_SYSKEYDOWN handler.
    757 
    758   switch (key) {
    759 
    760     // Ignore Return
    761     case VK_RETURN:
    762       return;
    763 
    764     // Hijacking Editing Commands
    765     //
    766     // We hijack the keyboard short-cuts for Cut, Copy, and Paste here so that
    767     // they go through our clipboard routines.  This allows us to be smarter
    768     // about how we interact with the clipboard and avoid bugs in the
    769     // CRichEditCtrl.  If we didn't hijack here, the edit control would handle
    770     // these internally with sending the WM_CUT, WM_COPY, or WM_PASTE messages.
    771     //
    772     // Cut:   Shift-Delete and Ctrl-x are treated as cut.  Ctrl-Shift-Delete and
    773     //        Ctrl-Shift-x are not treated as cut even though the underlying
    774     //        CRichTextEdit would treat them as such.
    775     // Copy:  Ctrl-c is treated as copy.  Shift-Ctrl-c is not.
    776     // Paste: Shift-Insert and Ctrl-v are tread as paste.  Ctrl-Shift-Insert and
    777     //        Ctrl-Shift-v are not.
    778     //
    779     // This behavior matches most, but not all Windows programs, and largely
    780     // conforms to what users expect.
    781 
    782     case VK_DELETE:
    783     case 'X':
    784       if ((flags & KF_ALTDOWN) ||
    785           (GetKeyState((key == 'X') ? VK_CONTROL : VK_SHIFT) >= 0))
    786         break;
    787       if (GetKeyState((key == 'X') ? VK_SHIFT : VK_CONTROL) >= 0) {
    788         ScopedFreeze freeze(this, GetTextObjectModel());
    789         OnBeforePossibleChange();
    790         Cut();
    791         OnAfterPossibleChange(true);
    792       }
    793       return;
    794 
    795     case 'C':
    796       if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
    797         break;
    798       if (GetKeyState(VK_SHIFT) >= 0)
    799         Copy();
    800       return;
    801 
    802     case VK_INSERT:
    803       // Ignore insert by itself, so we don't turn overtype mode on/off.
    804       if (!(flags & KF_ALTDOWN) && (GetKeyState(VK_SHIFT) >= 0) &&
    805           (GetKeyState(VK_CONTROL) >= 0))
    806         return;
    807       // Fall through to the next case (ie. Shift-Insert == Ctrl-V).
    808     case 'V':
    809       if ((flags & KF_ALTDOWN) ||
    810           (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0))
    811         break;
    812       if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) {
    813         ScopedFreeze freeze(this, GetTextObjectModel());
    814         OnBeforePossibleChange();
    815         Paste();
    816         OnAfterPossibleChange(true);
    817       }
    818       return;
    819 
    820     case 0xbb:  // Ctrl-'='.  Triggers subscripting, even in plain text mode.
    821                 // We don't use VK_OEM_PLUS in case the macro isn't defined.
    822                 // (e.g., we don't have this symbol in embeded environment).
    823       return;
    824 
    825     case VK_PROCESSKEY:
    826       // This key event is consumed by an IME.
    827       // We ignore this event because an IME sends WM_IME_COMPOSITION messages
    828       // when it updates the CRichEditCtrl text.
    829       return;
    830   }
    831 
    832   // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many
    833   // different keys (backspace, ctrl-v, ...), so we call this in both cases.
    834   HandleKeystroke();
    835 }
    836 
    837 void NativeTextfieldWin::OnLButtonDblClk(UINT keys, const CPoint& point) {
    838   // Save the double click info for later triple-click detection.
    839   tracking_double_click_ = true;
    840   double_click_point_ = point;
    841   double_click_time_ = GetCurrentMessage()->time;
    842 
    843   if (!ShouldProcessMouseEvent())
    844     return;
    845 
    846   ScopedFreeze freeze(this, GetTextObjectModel());
    847   OnBeforePossibleChange();
    848   DefWindowProc(WM_LBUTTONDBLCLK, keys,
    849                 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
    850   OnAfterPossibleChange(true);
    851 }
    852 
    853 void NativeTextfieldWin::OnLButtonDown(UINT keys, const CPoint& point) {
    854   // Check for triple click, then reset tracker.  Should be safe to subtract
    855   // double_click_time_ from the current message's time even if the timer has
    856   // wrapped in between.
    857   const bool is_triple_click = tracking_double_click_ &&
    858       IsDoubleClick(double_click_point_, point,
    859                     GetCurrentMessage()->time - double_click_time_);
    860   tracking_double_click_ = false;
    861 
    862   if (!ShouldProcessMouseEvent())
    863     return;
    864 
    865   ScopedFreeze freeze(this, GetTextObjectModel());
    866   OnBeforePossibleChange();
    867   DefWindowProc(WM_LBUTTONDOWN, keys,
    868                 MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click),
    869                            point.y));
    870   OnAfterPossibleChange(true);
    871 }
    872 
    873 void NativeTextfieldWin::OnLButtonUp(UINT keys, const CPoint& point) {
    874   ScopedFreeze freeze(this, GetTextObjectModel());
    875   OnBeforePossibleChange();
    876   DefWindowProc(WM_LBUTTONUP, keys,
    877                 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
    878   OnAfterPossibleChange(true);
    879 }
    880 
    881 void NativeTextfieldWin::OnMouseLeave() {
    882   SetContainsMouse(false);
    883 }
    884 
    885 LRESULT NativeTextfieldWin::OnMouseWheel(UINT message,
    886                                          WPARAM w_param,
    887                                          LPARAM l_param) {
    888   // Reroute the mouse-wheel to the window under the mouse pointer if
    889   // applicable.
    890   if (ui::RerouteMouseWheel(m_hWnd, w_param, l_param))
    891     return 0;
    892   return DefWindowProc(message, w_param, l_param);
    893 }
    894 
    895 void NativeTextfieldWin::OnMouseMove(UINT keys, const CPoint& point) {
    896   SetContainsMouse(true);
    897   // Clamp the selection to the visible text so the user can't drag to select
    898   // the "phantom newline".  In theory we could achieve this by clipping the X
    899   // coordinate, but in practice the edit seems to behave nondeterministically
    900   // with similar sequences of clipped input coordinates fed to it.  Maybe it's
    901   // reading the mouse cursor position directly?
    902   //
    903   // This solution has a minor visual flaw, however: if there's a visible
    904   // cursor at the edge of the text (only true when there's no selection),
    905   // dragging the mouse around outside that edge repaints the cursor on every
    906   // WM_MOUSEMOVE instead of allowing it to blink normally.  To fix this, we
    907   // special-case this exact case and discard the WM_MOUSEMOVE messages instead
    908   // of passing them along.
    909   //
    910   // But even this solution has a flaw!  (Argh.)  In the case where the user
    911   // has a selection that starts at the edge of the edit, and proceeds to the
    912   // middle of the edit, and the user is dragging back past the start edge to
    913   // remove the selection, there's a redraw problem where the change between
    914   // having the last few bits of text still selected and having nothing
    915   // selected can be slow to repaint (which feels noticeably strange).  This
    916   // occurs if you only let the edit receive a single WM_MOUSEMOVE past the
    917   // edge of the text.  I think on each WM_MOUSEMOVE the edit is repainting its
    918   // previous state, then updating its internal variables to the new state but
    919   // not repainting.  To fix this, we allow one more WM_MOUSEMOVE through after
    920   // the selection has supposedly been shrunk to nothing; this makes the edit
    921   // redraw the selection quickly so it feels smooth.
    922   CHARRANGE selection;
    923   GetSel(selection);
    924   const bool possibly_can_discard_mousemove =
    925       (selection.cpMin == selection.cpMax) &&
    926       (((selection.cpMin == 0) &&
    927         (ClipXCoordToVisibleText(point.x, false) > point.x)) ||
    928        ((selection.cpMin == GetTextLength()) &&
    929         (ClipXCoordToVisibleText(point.x, false) < point.x)));
    930   if (!can_discard_mousemove_ || !possibly_can_discard_mousemove) {
    931     can_discard_mousemove_ = possibly_can_discard_mousemove;
    932     ScopedFreeze freeze(this, GetTextObjectModel());
    933     OnBeforePossibleChange();
    934     // Force the Y coordinate to the center of the clip rect.  The edit
    935     // behaves strangely when the cursor is dragged vertically: if the cursor
    936     // is in the middle of the text, drags inside the clip rect do nothing,
    937     // and drags outside the clip rect act as if the cursor jumped to the
    938     // left edge of the text.  When the cursor is at the right edge, drags of
    939     // just a few pixels vertically end up selecting the "phantom newline"...
    940     // sometimes.
    941     RECT r;
    942     GetRect(&r);
    943     DefWindowProc(WM_MOUSEMOVE, keys,
    944                   MAKELPARAM(point.x, (r.bottom - r.top) / 2));
    945     OnAfterPossibleChange(true);
    946   }
    947 }
    948 
    949 int NativeTextfieldWin::OnNCCalcSize(BOOL w_param, LPARAM l_param) {
    950   content_insets_.Set(0, 0, 0, 0);
    951   if (textfield_->draw_border())
    952     content_insets_ = CalculateInsets();
    953   if (w_param) {
    954     NCCALCSIZE_PARAMS* nc_params =
    955         reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param);
    956     nc_params->rgrc[0].left += content_insets_.left();
    957     nc_params->rgrc[0].right -= content_insets_.right();
    958     nc_params->rgrc[0].top += content_insets_.top();
    959     nc_params->rgrc[0].bottom -= content_insets_.bottom();
    960   } else {
    961     RECT* rect = reinterpret_cast<RECT*>(l_param);
    962     rect->left += content_insets_.left();
    963     rect->right -= content_insets_.right();
    964     rect->top += content_insets_.top();
    965     rect->bottom -= content_insets_.bottom();
    966   }
    967   return 0;
    968 }
    969 
    970 void NativeTextfieldWin::OnNCPaint(HRGN region) {
    971   if (!textfield_->draw_border())
    972     return;
    973 
    974   HDC hdc = GetWindowDC();
    975 
    976   CRect window_rect;
    977   GetWindowRect(&window_rect);
    978   // Convert to be relative to 0x0.
    979   window_rect.MoveToXY(0, 0);
    980 
    981   ExcludeClipRect(hdc,
    982                   window_rect.left + content_insets_.left(),
    983                   window_rect.top + content_insets_.top(),
    984                   window_rect.right - content_insets_.right(),
    985                   window_rect.bottom - content_insets_.bottom());
    986 
    987   HBRUSH brush = CreateSolidBrush(bg_color_);
    988   FillRect(hdc, &window_rect, brush);
    989   DeleteObject(brush);
    990 
    991   int part;
    992   int state;
    993 
    994   if (base::win::GetVersion() < base::win::VERSION_VISTA) {
    995     part = EP_EDITTEXT;
    996 
    997     if (!textfield_->enabled())
    998       state = ETS_DISABLED;
    999     else if (textfield_->read_only())
   1000       state = ETS_READONLY;
   1001     else if (!contains_mouse_)
   1002       state = ETS_NORMAL;
   1003     else
   1004       state = ETS_HOT;
   1005   } else {
   1006     part = EP_EDITBORDER_HVSCROLL;
   1007 
   1008     if (!textfield_->enabled())
   1009       state = EPSHV_DISABLED;
   1010     else if (GetFocus() == m_hWnd)
   1011       state = EPSHV_FOCUSED;
   1012     else if (contains_mouse_)
   1013       state = EPSHV_HOT;
   1014     else
   1015       state = EPSHV_NORMAL;
   1016     // Vista doesn't appear to have a unique state for readonly.
   1017   }
   1018 
   1019   int classic_state =
   1020       (!textfield_->enabled() || textfield_->read_only()) ? DFCS_INACTIVE : 0;
   1021 
   1022   ui::NativeThemeWin::instance()->PaintTextField(hdc, part, state,
   1023                                                   classic_state, &window_rect,
   1024                                                   bg_color_, false, true);
   1025 
   1026   // NOTE: I tried checking the transparent property of the theme and invoking
   1027   // drawParentBackground, but it didn't seem to make a difference.
   1028 
   1029   ReleaseDC(hdc);
   1030 }
   1031 
   1032 void NativeTextfieldWin::OnNonLButtonDown(UINT keys, const CPoint& point) {
   1033   // Interestingly, the edit doesn't seem to cancel triple clicking when the
   1034   // x-buttons (which usually means "thumb buttons") are pressed, so we only
   1035   // call this for M and R down.
   1036   tracking_double_click_ = false;
   1037 
   1038   if (!ShouldProcessMouseEvent())
   1039     return;
   1040 
   1041   SetMsgHandled(false);
   1042 }
   1043 
   1044 void NativeTextfieldWin::OnPaste() {
   1045   if (textfield_->read_only())
   1046     return;
   1047 
   1048   ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
   1049   if (!clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(),
   1050                                     ui::Clipboard::BUFFER_STANDARD))
   1051     return;
   1052 
   1053   string16 clipboard_str;
   1054   clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_str);
   1055   if (!clipboard_str.empty()) {
   1056     string16 collapsed(CollapseWhitespace(clipboard_str, false));
   1057     if (textfield_->style() & Textfield::STYLE_LOWERCASE)
   1058       collapsed = base::i18n::ToLower(collapsed);
   1059     // Force a Paste operation to trigger ContentsChanged, even if identical
   1060     // contents are pasted into the text box. See http://crbug.com/79002
   1061     ReplaceSel(L"", false);
   1062     textfield_->SyncText();
   1063     text_before_change_.clear();
   1064     ReplaceSel(collapsed.c_str(), true);
   1065     if (TextfieldController* controller = textfield_->GetController())
   1066       controller->OnAfterPaste();
   1067   }
   1068 }
   1069 
   1070 void NativeTextfieldWin::OnSetFocus(HWND hwnd) {
   1071   SetMsgHandled(FALSE);  // We still want the default processing of the message.
   1072 
   1073   views::FocusManager* focus_manager = textfield_->GetFocusManager();
   1074   if (!focus_manager) {
   1075     NOTREACHED();
   1076     return;
   1077   }
   1078   focus_manager->SetFocusedView(textfield_);
   1079 
   1080   if (!base::win::IsTSFAwareRequired()) {
   1081     return;
   1082   }
   1083 
   1084   DefWindowProc();
   1085 
   1086   // Document manager created by RichEdit can be obtained only after
   1087   // WM_SET_FOCUS event is handled.
   1088   tsf_event_router_->SetManager(
   1089       ui::TSFBridge::GetInstance()->GetThreadManager());
   1090   SetMsgHandled(TRUE);
   1091 }
   1092 
   1093 void NativeTextfieldWin::OnKillFocus(HWND hwnd) {
   1094   if(tsf_event_router_)
   1095     tsf_event_router_->SetManager(NULL);
   1096   SetMsgHandled(FALSE);
   1097 }
   1098 
   1099 void NativeTextfieldWin::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) {
   1100   DCHECK(flags & KF_ALTDOWN);
   1101   // Explicitly show the system menu at a good location on [Alt]+[Space].
   1102   // Nearly all other [Alt]+<xxx> combos result in beeping rather than doing
   1103   // something useful, so discard those. Note that [Ctrl]+[Alt]+<xxx> generates
   1104   // WM_CHAR instead of WM_SYSCHAR, so it is not handled here.
   1105   if (ch == VK_SPACE) {
   1106     ui::ShowSystemMenu(
   1107         container_view_->GetWidget()->GetTopLevelWidget()->GetNativeWindow());
   1108   }
   1109 }
   1110 
   1111 void NativeTextfieldWin::OnFinalMessage(HWND hwnd) {
   1112   delete this;
   1113 }
   1114 
   1115 void NativeTextfieldWin::HandleKeystroke() {
   1116   const MSG* msg = GetCurrentMessage();
   1117   ui::KeyEvent event(*msg, msg->message == WM_CHAR);
   1118   ScopedFreeze freeze(this, GetTextObjectModel());
   1119 
   1120   TextfieldController* controller = textfield_->GetController();
   1121   if (!controller || !controller->HandleKeyEvent(textfield_, event)) {
   1122     OnBeforePossibleChange();
   1123 
   1124     if (msg->wParam == ui::VKEY_HOME || msg->wParam == ui::VKEY_END) {
   1125       // DefWindowProc() might reset the keyboard layout when it receives a
   1126       // keydown event for VKEY_HOME or VKEY_END. When the window was created
   1127       // with WS_EX_LAYOUTRTL and the current keyboard layout is not a RTL one,
   1128       // if the input text is pure LTR text, the layout changes to the first RTL
   1129       // keyboard layout in keyboard layout queue; if the input text is
   1130       // bidirectional text, the layout changes to the keyboard layout of the
   1131       // first RTL character in input text. When the window was created without
   1132       // WS_EX_LAYOUTRTL and the current keyboard layout is not a LTR one, if
   1133       // the input text is pure RTL text, the layout changes to English; if the
   1134       // input text is bidirectional text, the layout changes to the keyboard
   1135       // layout of the first LTR character in input text. Such keyboard layout
   1136       // change behavior is surprising and inconsistent with keyboard behavior
   1137       // elsewhere, so reset the layout in this case.
   1138       HKL layout = GetKeyboardLayout(0);
   1139       DefWindowProc(msg->message, msg->wParam, msg->lParam);
   1140       ActivateKeyboardLayout(layout, KLF_REORDER);
   1141     } else {
   1142       DefWindowProc(msg->message, msg->wParam, msg->lParam);
   1143     }
   1144 
   1145     // CRichEditCtrl automatically turns on IMF_AUTOKEYBOARD when the user
   1146     // inputs an RTL character, making it difficult for the user to control
   1147     // what language is set as they type. Force this off to make the edit's
   1148     // behavior more stable.
   1149     const int lang_options = SendMessage(EM_GETLANGOPTIONS, 0, 0);
   1150     if (lang_options & IMF_AUTOKEYBOARD)
   1151       SendMessage(EM_SETLANGOPTIONS, 0, lang_options & ~IMF_AUTOKEYBOARD);
   1152 
   1153     OnAfterPossibleChange(true);
   1154   }
   1155 }
   1156 
   1157 void NativeTextfieldWin::OnBeforePossibleChange() {
   1158   // Record our state.
   1159   text_before_change_ = GetText();
   1160 }
   1161 
   1162 void NativeTextfieldWin::OnAfterPossibleChange(bool should_redraw_text) {
   1163   // Prevent the user from selecting the "phantom newline" at the end of the
   1164   // edit.  If they try, we just silently move the end of the selection back to
   1165   // the end of the real text.
   1166   CHARRANGE new_sel;
   1167   GetSel(new_sel);
   1168   const int length = GetTextLength();
   1169   if (new_sel.cpMax > length) {
   1170     new_sel.cpMax = length;
   1171     if (new_sel.cpMin > length)
   1172       new_sel.cpMin = length;
   1173     SetSel(new_sel);
   1174   }
   1175 
   1176   string16 new_text(GetText());
   1177   if (new_text != text_before_change_) {
   1178     if (ime_discard_composition_ && ime_composition_start_ >= 0 &&
   1179         ime_composition_length_ > 0) {
   1180       // A string retrieved with a GetText() call contains a string being
   1181       // composed by an IME. We remove the composition string from this search
   1182       // string.
   1183       new_text.erase(ime_composition_start_, ime_composition_length_);
   1184       ime_composition_start_ = 0;
   1185       ime_composition_length_ = 0;
   1186       if (new_text.empty())
   1187         return;
   1188     }
   1189     textfield_->SyncText();
   1190     UpdateAccessibleValue(textfield_->text());
   1191 
   1192     if (should_redraw_text) {
   1193       CHARRANGE original_sel;
   1194       GetSel(original_sel);
   1195       string16 text(GetText());
   1196       ScopedSuspendUndo suspend_undo(GetTextObjectModel());
   1197 
   1198       SelectAll(true);
   1199       ReplaceSel(reinterpret_cast<LPCTSTR>(text.c_str()), true);
   1200       SetSel(original_sel);
   1201     }
   1202   }
   1203 }
   1204 
   1205 LONG NativeTextfieldWin::ClipXCoordToVisibleText(LONG x,
   1206                                                  bool is_triple_click) const {
   1207   // Clip the X coordinate to the left edge of the text.  Careful:
   1208   // PosFromChar(0) may return a negative X coordinate if the beginning of the
   1209   // text has scrolled off the edit, so don't go past the clip rect's edge.
   1210   PARAFORMAT2 pf2;
   1211   GetParaFormat(pf2);
   1212   // Calculation of the clipped coordinate is more complicated if the paragraph
   1213   // layout is RTL layout, or if there is RTL characters inside the LTR layout
   1214   // paragraph.
   1215   bool ltr_text_in_ltr_layout = true;
   1216   if ((pf2.wEffects & PFE_RTLPARA) ||
   1217       base::i18n::StringContainsStrongRTLChars(GetText())) {
   1218     ltr_text_in_ltr_layout = false;
   1219   }
   1220   const int length = GetTextLength();
   1221   RECT r;
   1222   GetRect(&r);
   1223   // The values returned by PosFromChar() seem to refer always
   1224   // to the left edge of the character's bounding box.
   1225   const LONG first_position_x = PosFromChar(0).x;
   1226   LONG min_x = first_position_x;
   1227   if (!ltr_text_in_ltr_layout) {
   1228     for (int i = 1; i < length; ++i)
   1229       min_x = std::min(min_x, PosFromChar(i).x);
   1230   }
   1231   const LONG left_bound = std::max(r.left, min_x);
   1232 
   1233   // PosFromChar(length) is a phantom character past the end of the text. It is
   1234   // not necessarily a right bound; in RTL controls it may be a left bound. So
   1235   // treat it as a right bound only if it is to the right of the first
   1236   // character.
   1237   LONG right_bound = r.right;
   1238   LONG end_position_x = PosFromChar(length).x;
   1239   if (end_position_x >= first_position_x) {
   1240     right_bound = std::min(right_bound, end_position_x);  // LTR case.
   1241   }
   1242   // For trailing characters that are 2 pixels wide of less (like "l" in some
   1243   // fonts), we have a problem:
   1244   //   * Clicks on any pixel within the character will place the cursor before
   1245   //     the character.
   1246   //   * Clicks on the pixel just after the character will not allow triple-
   1247   //     click to work properly (true for any last character width).
   1248   // So, we move to the last pixel of the character when this is a
   1249   // triple-click, and moving to one past the last pixel in all other
   1250   // scenarios.  This way, all clicks that can move the cursor will place it at
   1251   // the end of the text, but triple-click will still work.
   1252   if (x < left_bound) {
   1253     return (is_triple_click && ltr_text_in_ltr_layout) ? left_bound - 1 :
   1254                                                          left_bound;
   1255   }
   1256   if ((length == 0) || (x < right_bound))
   1257     return x;
   1258   return is_triple_click ? (right_bound - 1) : right_bound;
   1259 }
   1260 
   1261 void NativeTextfieldWin::SetContainsMouse(bool contains_mouse) {
   1262   if (contains_mouse == contains_mouse_)
   1263     return;
   1264 
   1265   contains_mouse_ = contains_mouse;
   1266 
   1267   if (!textfield_->draw_border())
   1268     return;
   1269 
   1270   if (contains_mouse_) {
   1271     // Register for notification when the mouse leaves. Need to do this so
   1272     // that we can reset contains mouse properly.
   1273     TRACKMOUSEEVENT tme;
   1274     tme.cbSize = sizeof(tme);
   1275     tme.dwFlags = TME_LEAVE;
   1276     tme.hwndTrack = m_hWnd;
   1277     tme.dwHoverTime = 0;
   1278     TrackMouseEvent(&tme);
   1279   }
   1280   RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME);
   1281 }
   1282 
   1283 ITextDocument* NativeTextfieldWin::GetTextObjectModel() const {
   1284   if (!text_object_model_) {
   1285     base::win::ScopedComPtr<IRichEditOle, &IID_IRichEditOle> ole_interface;
   1286     ole_interface.Attach(GetOleInterface());
   1287     if (ole_interface)
   1288       text_object_model_.QueryFrom(ole_interface);
   1289   }
   1290   return text_object_model_;
   1291 }
   1292 
   1293 void NativeTextfieldWin::BuildContextMenu() {
   1294   if (context_menu_contents_.get())
   1295     return;
   1296   context_menu_contents_.reset(new ui::SimpleMenuModel(this));
   1297   context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
   1298   context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
   1299   context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
   1300   context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
   1301   context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
   1302   context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
   1303   context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL,
   1304                                               IDS_APP_SELECT_ALL);
   1305 }
   1306 
   1307 bool NativeTextfieldWin::ShouldProcessMouseEvent() {
   1308   TextfieldController* controller = textfield_->GetController();
   1309   if (!controller)
   1310     return true;
   1311   MSG msg(*GetCurrentMessage());
   1312   // ATL doesn't set the |time| field.
   1313   if (!msg.time)
   1314     msg.time = GetMessageTime();
   1315   ui::MouseEvent mouse_event(msg);
   1316   return !controller->HandleMouseEvent(textfield_, mouse_event);
   1317 }
   1318 
   1319 }  // namespace views
   1320