omnibox_view_win.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1// Copyright 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 "chrome/browser/ui/views/omnibox/omnibox_view_win.h"
6
7#include <algorithm>
8#include <locale>
9#include <string>
10
11#include <richedit.h>
12#include <textserv.h>
13
14#include "base/auto_reset.h"
15#include "base/basictypes.h"
16#include "base/bind.h"
17#include "base/i18n/rtl.h"
18#include "base/lazy_instance.h"
19#include "base/memory/ref_counted.h"
20#include "base/strings/string_util.h"
21#include "base/strings/utf_string_conversions.h"
22#include "base/win/iat_patch_function.h"
23#include "base/win/metro.h"
24#include "base/win/scoped_hdc.h"
25#include "base/win/scoped_select_object.h"
26#include "base/win/windows_version.h"
27#include "chrome/app/chrome_command_ids.h"
28#include "chrome/browser/autocomplete/autocomplete_input.h"
29#include "chrome/browser/autocomplete/autocomplete_match.h"
30#include "chrome/browser/autocomplete/keyword_provider.h"
31#include "chrome/browser/bookmarks/bookmark_node_data.h"
32#include "chrome/browser/command_updater.h"
33#include "chrome/browser/net/url_fixer_upper.h"
34#include "chrome/browser/profiles/profile.h"
35#include "chrome/browser/search/search.h"
36#include "chrome/browser/ui/browser.h"
37#include "chrome/browser/ui/omnibox/omnibox_edit_controller.h"
38#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
39#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
40#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
41#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
42#include "chrome/common/chrome_notification_types.h"
43#include "content/public/browser/user_metrics.h"
44#include "content/public/browser/web_contents.h"
45#include "extensions/common/constants.h"
46#include "googleurl/src/url_util.h"
47#include "grit/generated_resources.h"
48#include "net/base/escape.h"
49#include "skia/ext/skia_utils_win.h"
50#include "ui/base/accessibility/accessible_view_state.h"
51#include "ui/base/clipboard/clipboard.h"
52#include "ui/base/clipboard/scoped_clipboard_writer.h"
53#include "ui/base/dragdrop/drag_drop_types.h"
54#include "ui/base/dragdrop/drag_source_win.h"
55#include "ui/base/dragdrop/drop_target_win.h"
56#include "ui/base/dragdrop/os_exchange_data.h"
57#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
58#include "ui/base/events/event.h"
59#include "ui/base/events/event_constants.h"
60#include "ui/base/ime/win/tsf_bridge.h"
61#include "ui/base/ime/win/tsf_event_router.h"
62#include "ui/base/keycodes/keyboard_codes.h"
63#include "ui/base/l10n/l10n_util.h"
64#include "ui/base/l10n/l10n_util_win.h"
65#include "ui/base/win/hwnd_util.h"
66#include "ui/base/win/mouse_wheel_util.h"
67#include "ui/base/win/touch_input.h"
68#include "ui/gfx/canvas.h"
69#include "ui/gfx/image/image.h"
70#include "ui/views/button_drag_utils.h"
71#include "ui/views/controls/menu/menu_item_view.h"
72#include "ui/views/controls/menu/menu_runner.h"
73#include "ui/views/controls/textfield/native_textfield_win.h"
74#include "ui/views/widget/widget.h"
75#include "win8/util/win8_util.h"
76
77#pragma comment(lib, "oleacc.lib")  // Needed for accessibility support.
78
79using content::UserMetricsAction;
80using content::WebContents;
81
82namespace {
83
84const char kAutocompleteEditStateKey[] = "AutocompleteEditState";
85
86// msftedit.dll is RichEdit ver 4.1.
87// This version is available from WinXP SP1 and has TSF support.
88const wchar_t* kRichEditDLLName = L"msftedit.dll";
89
90// A helper method for determining a valid DROPEFFECT given the allowed
91// DROPEFFECTS.  We prefer copy over link.
92DWORD CopyOrLinkDropEffect(DWORD effect) {
93  if (effect & DROPEFFECT_COPY)
94    return DROPEFFECT_COPY;
95  if (effect & DROPEFFECT_LINK)
96    return DROPEFFECT_LINK;
97  return DROPEFFECT_NONE;
98}
99
100// A helper method for determining a valid drag operation given the allowed
101// operation.  We prefer copy over link.
102int CopyOrLinkDragOperation(int drag_operation) {
103  if (drag_operation & ui::DragDropTypes::DRAG_COPY)
104    return ui::DragDropTypes::DRAG_COPY;
105  if (drag_operation & ui::DragDropTypes::DRAG_LINK)
106    return ui::DragDropTypes::DRAG_LINK;
107  return ui::DragDropTypes::DRAG_NONE;
108}
109
110// The AutocompleteEditState struct contains enough information about the
111// OmniboxEditModel and OmniboxViewWin to save/restore a user's
112// typing, caret position, etc. across tab changes.  We explicitly don't
113// preserve things like whether the popup was open as this might be weird.
114struct AutocompleteEditState : public base::SupportsUserData::Data {
115  AutocompleteEditState(const OmniboxEditModel::State& model_state,
116                        const OmniboxViewWin::State& view_state)
117      : model_state(model_state),
118        view_state(view_state) {
119  }
120  virtual ~AutocompleteEditState() {}
121
122  const OmniboxEditModel::State model_state;
123  const OmniboxViewWin::State view_state;
124};
125
126// Returns true if the current point is far enough from the origin that it
127// would be considered a drag.
128bool IsDrag(const POINT& origin, const POINT& current) {
129  return views::View::ExceededDragThreshold(
130      gfx::Point(current) - gfx::Point(origin));
131}
132
133// Copies |selected_text| as text to the primary clipboard.
134void DoCopyText(const string16& selected_text) {
135  ui::ScopedClipboardWriter scw(ui::Clipboard::GetForCurrentThread(),
136                                ui::Clipboard::BUFFER_STANDARD);
137  scw.WriteText(selected_text);
138}
139
140// Writes |url| and |text| to the clipboard as a well-formed URL.
141void DoCopyURL(const GURL& url, const string16& text) {
142  BookmarkNodeData data;
143  data.ReadFromTuple(url, text);
144  data.WriteToClipboard();
145}
146
147}  // namespace
148
149// EditDropTarget is the IDropTarget implementation installed on
150// OmniboxViewWin. EditDropTarget prefers URL over plain text. A drop
151// of a URL replaces all the text of the edit and navigates immediately to the
152// URL. A drop of plain text from the same edit either copies or moves the
153// selected text, and a drop of plain text from a source other than the edit
154// does a paste and go.
155class OmniboxViewWin::EditDropTarget : public ui::DropTargetWin {
156 public:
157  explicit EditDropTarget(OmniboxViewWin* edit);
158
159 protected:
160  virtual DWORD OnDragEnter(IDataObject* data_object,
161                            DWORD key_state,
162                            POINT cursor_position,
163                            DWORD effect);
164  virtual DWORD OnDragOver(IDataObject* data_object,
165                           DWORD key_state,
166                           POINT cursor_position,
167                           DWORD effect);
168  virtual void OnDragLeave(IDataObject* data_object);
169  virtual DWORD OnDrop(IDataObject* data_object,
170                       DWORD key_state,
171                       POINT cursor_position,
172                       DWORD effect);
173
174 private:
175  // If dragging a string, the drop highlight position of the edit is reset
176  // based on the mouse position.
177  void UpdateDropHighlightPosition(const POINT& cursor_screen_position);
178
179  // Resets the visual drop indicates we install on the edit.
180  void ResetDropHighlights();
181
182  // The edit we're the drop target for.
183  OmniboxViewWin* edit_;
184
185  // If true, the drag session contains a URL.
186  bool drag_has_url_;
187
188  // If true, the drag session contains a string. If drag_has_url_ is true,
189  // this is false regardless of whether the clipboard has a string.
190  bool drag_has_string_;
191
192  DISALLOW_COPY_AND_ASSIGN(EditDropTarget);
193};
194
195OmniboxViewWin::EditDropTarget::EditDropTarget(OmniboxViewWin* edit)
196    : ui::DropTargetWin(edit->m_hWnd),
197      edit_(edit),
198      drag_has_url_(false),
199      drag_has_string_(false) {
200}
201
202DWORD OmniboxViewWin::EditDropTarget::OnDragEnter(IDataObject* data_object,
203                                                  DWORD key_state,
204                                                  POINT cursor_position,
205                                                  DWORD effect) {
206  ui::OSExchangeData os_data(new ui::OSExchangeDataProviderWin(data_object));
207  drag_has_url_ = os_data.HasURL();
208  drag_has_string_ = !drag_has_url_ && os_data.HasString();
209  if (drag_has_url_) {
210    if (edit_->in_drag()) {
211      // The edit we're associated with originated the drag. No point in
212      // allowing the user to drop back on us.
213      drag_has_url_ = false;
214    }
215    // NOTE: it would be nice to visually show all the text is going to
216    // be replaced by selecting all, but this caused painting problems. In
217    // particular the flashing caret would appear outside the edit! For now
218    // we stick with no visual indicator other than that shown own the mouse
219    // cursor.
220  }
221  return OnDragOver(data_object, key_state, cursor_position, effect);
222}
223
224DWORD OmniboxViewWin::EditDropTarget::OnDragOver(IDataObject* data_object,
225                                                 DWORD key_state,
226                                                 POINT cursor_position,
227                                                 DWORD effect) {
228  if (drag_has_url_)
229    return CopyOrLinkDropEffect(effect);
230
231  if (drag_has_string_) {
232    UpdateDropHighlightPosition(cursor_position);
233    if (edit_->drop_highlight_position() == -1 && edit_->in_drag())
234      return DROPEFFECT_NONE;
235    if (edit_->in_drag()) {
236      // The edit we're associated with originated the drag.  Do the normal drag
237      // behavior.
238      DCHECK((effect & DROPEFFECT_COPY) && (effect & DROPEFFECT_MOVE));
239      return (key_state & MK_CONTROL) ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
240    }
241    // Our edit didn't originate the drag, only allow link or copy.
242    return CopyOrLinkDropEffect(effect);
243  }
244
245  return DROPEFFECT_NONE;
246}
247
248void OmniboxViewWin::EditDropTarget::OnDragLeave(IDataObject* data_object) {
249  ResetDropHighlights();
250}
251
252DWORD OmniboxViewWin::EditDropTarget::OnDrop(IDataObject* data_object,
253                                             DWORD key_state,
254                                             POINT cursor_position,
255                                             DWORD effect) {
256  effect = OnDragOver(data_object, key_state, cursor_position, effect);
257
258  ui::OSExchangeData os_data(new ui::OSExchangeDataProviderWin(data_object));
259  gfx::Point point(cursor_position.x, cursor_position.y);
260  ui::DropTargetEvent event(
261      os_data, point, point,
262      ui::DragDropTypes::DropEffectToDragOperation(effect));
263
264  int drag_operation = edit_->OnPerformDropImpl(event, edit_->in_drag());
265
266  if (!drag_has_url_)
267    ResetDropHighlights();
268
269  return ui::DragDropTypes::DragOperationToDropEffect(drag_operation);
270}
271
272void OmniboxViewWin::EditDropTarget::UpdateDropHighlightPosition(
273    const POINT& cursor_screen_position) {
274  if (drag_has_string_) {
275    POINT client_position = cursor_screen_position;
276    ::ScreenToClient(edit_->m_hWnd, &client_position);
277    int drop_position = edit_->CharFromPos(client_position);
278    if (edit_->in_drag()) {
279      // Our edit originated the drag, don't allow a drop if over the selected
280      // region.
281      LONG sel_start, sel_end;
282      edit_->GetSel(sel_start, sel_end);
283      if ((sel_start != sel_end) && (drop_position >= sel_start) &&
284          (drop_position <= sel_end))
285        drop_position = -1;
286    } else {
287      // A drop from a source other than the edit replaces all the text, so
288      // we don't show the drop location. See comment in OnDragEnter as to why
289      // we don't try and select all here.
290      drop_position = -1;
291    }
292    edit_->SetDropHighlightPosition(drop_position);
293  }
294}
295
296void OmniboxViewWin::EditDropTarget::ResetDropHighlights() {
297  if (drag_has_string_)
298    edit_->SetDropHighlightPosition(-1);
299}
300
301///////////////////////////////////////////////////////////////////////////////
302// Helper classes
303
304OmniboxViewWin::ScopedFreeze::ScopedFreeze(OmniboxViewWin* edit,
305                                           ITextDocument* text_object_model)
306    : edit_(edit),
307      text_object_model_(text_object_model) {
308  // Freeze the screen.
309  if (text_object_model_) {
310    long count;
311    text_object_model_->Freeze(&count);
312  }
313}
314
315OmniboxViewWin::ScopedFreeze::~ScopedFreeze() {
316  // Unfreeze the screen.
317  // NOTE: If this destructor is reached while the edit is being destroyed (for
318  // example, because we double-clicked the edit of a popup and caused it to
319  // transform to an unconstrained window), it will no longer have an HWND, and
320  // text_object_model_ may point to a destroyed object, so do nothing here.
321  if (edit_->IsWindow() && text_object_model_) {
322    long count;
323    text_object_model_->Unfreeze(&count);
324    if (count == 0) {
325      // We need to UpdateWindow() here in addition to InvalidateRect() because,
326      // as far as I can tell, the edit likes to synchronously erase its
327      // background when unfreezing, thus requiring us to synchronously redraw
328      // if we don't want flicker.
329      edit_->InvalidateRect(NULL, false);
330      edit_->UpdateWindow();
331    }
332  }
333}
334
335OmniboxViewWin::ScopedSuspendUndo::ScopedSuspendUndo(
336    ITextDocument* text_object_model)
337    : text_object_model_(text_object_model) {
338  // Suspend Undo processing.
339  if (text_object_model_)
340    text_object_model_->Undo(tomSuspend, NULL);
341}
342
343OmniboxViewWin::ScopedSuspendUndo::~ScopedSuspendUndo() {
344  // Resume Undo processing.
345  if (text_object_model_)
346    text_object_model_->Undo(tomResume, NULL);
347}
348
349// A subclass of NativeViewHost that provides accessibility info for the
350// underlying Omnibox view.
351class OmniboxViewWrapper : public views::NativeViewHost {
352 public:
353  explicit OmniboxViewWrapper(OmniboxViewWin* omnibox_view_win)
354      : omnibox_view_win_(omnibox_view_win) {}
355
356  gfx::NativeViewAccessible GetNativeViewAccessible() {
357    // This forces it to use NativeViewAccessibilityWin rather than
358    // any accessibility provided natively by the HWND.
359    return View::GetNativeViewAccessible();
360  }
361
362  // views::View
363  virtual void GetAccessibleState(ui::AccessibleViewState* state) {
364    views::NativeViewHost::GetAccessibleState(state);
365    state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_LOCATION);
366    state->role = ui::AccessibilityTypes::ROLE_TEXT;
367    state->value = omnibox_view_win_->GetText();
368    state->state = ui::AccessibilityTypes::STATE_EDITABLE;
369    size_t sel_start;
370    size_t sel_end;
371    omnibox_view_win_->GetSelectionBounds(&sel_start, &sel_end);
372    state->selection_start = sel_start;
373    state->selection_end = sel_end;
374  }
375
376 private:
377  OmniboxViewWin* omnibox_view_win_;
378
379  DISALLOW_COPY_AND_ASSIGN(OmniboxViewWrapper);
380};
381
382///////////////////////////////////////////////////////////////////////////////
383// OmniboxViewWin
384
385namespace {
386
387// These are used to hook the CRichEditCtrl's calls to BeginPaint() and
388// EndPaint() and provide a memory DC instead.  See OnPaint().
389HWND edit_hwnd = NULL;
390PAINTSTRUCT paint_struct;
391
392// Intercepted method for BeginPaint(). Must use __stdcall convention.
393HDC WINAPI BeginPaintIntercept(HWND hWnd, LPPAINTSTRUCT lpPaint) {
394  if (!edit_hwnd || (hWnd != edit_hwnd))
395    return ::BeginPaint(hWnd, lpPaint);
396
397  *lpPaint = paint_struct;
398  return paint_struct.hdc;
399}
400
401// Intercepted method for EndPaint(). Must use __stdcall convention.
402BOOL WINAPI EndPaintIntercept(HWND hWnd, const PAINTSTRUCT* lpPaint) {
403  return (edit_hwnd && (hWnd == edit_hwnd)) || ::EndPaint(hWnd, lpPaint);
404}
405
406class PaintPatcher {
407 public:
408  PaintPatcher();
409  ~PaintPatcher();
410
411  void RefPatch();
412  void DerefPatch();
413
414 private:
415  size_t refcount_;
416  base::win::IATPatchFunction begin_paint_;
417  base::win::IATPatchFunction end_paint_;
418
419  DISALLOW_COPY_AND_ASSIGN(PaintPatcher);
420};
421
422PaintPatcher::PaintPatcher() : refcount_(0) {
423}
424
425PaintPatcher::~PaintPatcher() {
426  DCHECK_EQ(0U, refcount_);
427}
428
429void PaintPatcher::RefPatch() {
430  if (refcount_ == 0) {
431    DCHECK(!begin_paint_.is_patched());
432    DCHECK(!end_paint_.is_patched());
433    begin_paint_.Patch(kRichEditDLLName, "user32.dll", "BeginPaint",
434                       &BeginPaintIntercept);
435    end_paint_.Patch(kRichEditDLLName, "user32.dll", "EndPaint",
436                     &EndPaintIntercept);
437  }
438  ++refcount_;
439}
440
441void PaintPatcher::DerefPatch() {
442  DCHECK(begin_paint_.is_patched());
443  DCHECK(end_paint_.is_patched());
444  --refcount_;
445  if (refcount_ == 0) {
446    begin_paint_.Unpatch();
447    end_paint_.Unpatch();
448  }
449}
450
451base::LazyInstance<PaintPatcher> g_paint_patcher = LAZY_INSTANCE_INITIALIZER;
452
453// twips are a unit of type measurement, and RichEdit controls use them
454// to set offsets.
455const int kTwipsPerInch = 1440;
456
457}  // namespace
458
459HMODULE OmniboxViewWin::loaded_library_module_ = NULL;
460
461OmniboxViewWin::OmniboxViewWin(OmniboxEditController* controller,
462                               ToolbarModel* toolbar_model,
463                               LocationBarView* location_bar,
464                               CommandUpdater* command_updater,
465                               bool popup_window_mode,
466                               const gfx::Font& font,
467                               int font_y_offset)
468    : OmniboxView(location_bar->profile(), controller, toolbar_model,
469                  command_updater),
470      popup_view_(
471          OmniboxPopupContentsView::Create(font, this, model(), location_bar)),
472      location_bar_(location_bar),
473      popup_window_mode_(popup_window_mode),
474      force_hidden_(false),
475      tracking_click_(),
476      tracking_double_click_(false),
477      double_click_time_(0),
478      can_discard_mousemove_(false),
479      ignore_ime_messages_(false),
480      delete_at_end_pressed_(false),
481      font_(font),
482      font_y_adjustment_(font_y_offset),
483      possible_drag_(false),
484      in_drag_(false),
485      initiated_drag_(false),
486      drop_highlight_position_(-1),
487      ime_candidate_window_open_(false),
488      background_color_(skia::SkColorToCOLORREF(location_bar->GetColor(
489          ToolbarModel::NONE, LocationBarView::BACKGROUND))),
490      security_level_(ToolbarModel::NONE),
491      text_object_model_(NULL),
492      tsf_event_router_(base::win::IsTSFAwareRequired() ?
493          new ui::TSFEventRouter(this) : NULL) {
494  if (!loaded_library_module_)
495    loaded_library_module_ = LoadLibrary(kRichEditDLLName);
496  // RichEdit should be available; rare exceptions should use the Views omnibox.
497  DCHECK(loaded_library_module_);
498
499  saved_selection_for_focus_change_.cpMin = -1;
500
501  g_paint_patcher.Pointer()->RefPatch();
502
503  Create(location_bar->GetWidget()->GetNativeView(), 0, 0, 0,
504         l10n_util::GetExtendedStyles());
505  SetReadOnly(popup_window_mode_);
506  gfx::NativeFont native_font(font_.GetNativeFont());
507  SetFont(native_font);
508
509  // IMF_DUALFONT (on by default) is supposed to use one font for ASCII text
510  // and a different one for Asian text.  In some cases, ASCII characters may
511  // be mis-marked as Asian, e.g. because input from the keyboard may be
512  // auto-stamped with the keyboard language.  As a result adjacent characters
513  // can render in different fonts, which looks bizarre.  To fix this we
514  // disable dual-font mode, which forces the control to use a single font for
515  // everything.
516  // Note that we should not disable the very similar IMF_AUTOFONT flag, which
517  // allows the control to hunt for fonts that can display all the current
518  // characters; doing this results in "missing glyph" boxes when the user
519  // enters characters not available in the currently-chosen font.
520  const LRESULT lang_options = SendMessage(m_hWnd, EM_GETLANGOPTIONS, 0, 0);
521  SendMessage(m_hWnd, EM_SETLANGOPTIONS, 0, lang_options & ~IMF_DUALFONT);
522
523  // NOTE: Do not use SetWordBreakProcEx() here, that is no longer supported as
524  // of Rich Edit 2.0 onward.
525  SendMessage(m_hWnd, EM_SETWORDBREAKPROC, 0,
526              reinterpret_cast<LPARAM>(&WordBreakProc));
527
528  // Get the metrics for the font.
529  base::win::ScopedGetDC screen_dc(NULL);
530  base::win::ScopedSelectObject font_in_dc(screen_dc, native_font);
531  TEXTMETRIC tm = {0};
532  GetTextMetrics(screen_dc, &tm);
533  int cap_height = font_.GetBaseline() - tm.tmInternalLeading;
534  // The ratio of a font's x-height to its cap height.  Sadly, Windows
535  // doesn't provide a true value for a font's x-height in its text
536  // metrics, so we approximate.
537  const float kXHeightRatio = 0.7f;
538  font_x_height_ = static_cast<int>(
539      (static_cast<float>(cap_height) * kXHeightRatio) + 0.5);
540
541  // Get the number of twips per pixel, which we need below to offset our text
542  // by the desired number of pixels.
543  const long kTwipsPerPixel =
544      kTwipsPerInch / GetDeviceCaps(screen_dc, LOGPIXELSY);
545
546  // Set the default character style -- adjust to our desired baseline.
547  CHARFORMAT cf = {0};
548  cf.dwMask = CFM_OFFSET;
549  cf.yOffset = -font_y_adjustment_ * kTwipsPerPixel;
550  SetDefaultCharFormat(cf);
551
552  SetBackgroundColor(background_color_);
553
554  if (!popup_window_mode_) {
555    // Non-read-only edit controls have a drop target.  Revoke it so that we can
556    // install our own.  Revoking automatically deletes the existing one.
557    HRESULT hr = RevokeDragDrop(m_hWnd);
558    DCHECK_EQ(S_OK, hr);
559
560    // Register our drop target.  The scoped_refptr here will delete the drop
561    // target if it fails to register itself correctly on |m_hWnd|.  Otherwise,
562    // the edit control will invoke RevokeDragDrop when it's being destroyed, so
563    // we don't have to do so.
564    scoped_refptr<EditDropTarget> drop_target(new EditDropTarget(this));
565  }
566}
567
568OmniboxViewWin::~OmniboxViewWin() {
569  // Explicitly release the text object model now that we're done with it, and
570  // before we free the library. If the library gets unloaded before this
571  // released, it becomes garbage. Note that since text_object_model_ is lazy
572  // initialized, it may still be null.
573  if (text_object_model_)
574    text_object_model_->Release();
575
576  // We balance our reference count and unpatch when the last instance has
577  // been destroyed.  This prevents us from relying on the AtExit or static
578  // destructor sequence to do our unpatching, which is generally fragile.
579  g_paint_patcher.Pointer()->DerefPatch();
580}
581
582views::View* OmniboxViewWin::parent_view() const {
583  return location_bar_;
584}
585
586void OmniboxViewWin::SaveStateToTab(WebContents* tab) {
587  DCHECK(tab);
588
589  const OmniboxEditModel::State model_state(model()->GetStateForTabSwitch());
590
591  CHARRANGE selection;
592  GetSelection(selection);
593  tab->SetUserData(
594      kAutocompleteEditStateKey,
595      new AutocompleteEditState(
596          model_state,
597          State(selection, saved_selection_for_focus_change_)));
598}
599
600void OmniboxViewWin::Update(const WebContents* tab_for_state_restoring) {
601  const bool visibly_changed_permanent_text =
602      model()->UpdatePermanentText(toolbar_model()->GetText(true));
603
604  const ToolbarModel::SecurityLevel security_level =
605      toolbar_model()->GetSecurityLevel();
606  const bool changed_security_level = (security_level != security_level_);
607
608  // Bail early when no visible state will actually change (prevents an
609  // unnecessary ScopedFreeze, and thus UpdateWindow()).
610  if (!changed_security_level && !visibly_changed_permanent_text &&
611      !tab_for_state_restoring)
612    return;
613
614  // Update our local state as desired.  We set security_level_ here so it will
615  // already be correct before we get to any RevertAll()s below and use it.
616  security_level_ = security_level;
617
618  // When we're switching to a new tab, restore its state, if any.
619  ScopedFreeze freeze(this, GetTextObjectModel());
620  if (tab_for_state_restoring) {
621    // Make sure we reset our own state first.  The new tab may not have any
622    // saved state, or it may not have had input in progress, in which case we
623    // won't overwrite all our local state.
624    RevertAll();
625
626    const AutocompleteEditState* state = static_cast<AutocompleteEditState*>(
627        tab_for_state_restoring->GetUserData(&kAutocompleteEditStateKey));
628    if (state) {
629      model()->RestoreState(state->model_state);
630
631      // Restore user's selection.  We do this after restoring the user_text
632      // above so we're selecting in the correct string.
633      SetSelectionRange(state->view_state.selection);
634      saved_selection_for_focus_change_ =
635          state->view_state.saved_selection_for_focus_change;
636    }
637  } else if (visibly_changed_permanent_text) {
638    // Not switching tabs, just updating the permanent text.  (In the case where
639    // we _were_ switching tabs, the RevertAll() above already drew the new
640    // permanent text.)
641
642    // Tweak: if the edit was previously nonempty and had all the text selected,
643    // select all the new text.  This makes one particular case better: the
644    // user clicks in the box to change it right before the permanent URL is
645    // changed.  Since the new URL is still fully selected, the user's typing
646    // will replace the edit contents as they'd intended.
647    //
648    // NOTE: The selection can be longer than the text length if the edit is in
649    // in rich text mode and the user has selected the "phantom newline" at the
650    // end, so use ">=" instead of "==" to see if all the text is selected.  In
651    // theory we prevent this case from ever occurring, but this is still safe.
652    CHARRANGE sel;
653    GetSelection(sel);
654    const bool was_reversed = (sel.cpMin > sel.cpMax);
655    const bool was_sel_all = (sel.cpMin != sel.cpMax) &&
656      IsSelectAllForRange(sel);
657
658    RevertAll();
659
660    if (was_sel_all)
661      SelectAll(was_reversed);
662  } else if (changed_security_level) {
663    // Only the security style changed, nothing else.  Redraw our text using it.
664    EmphasizeURLComponents();
665  }
666}
667
668void OmniboxViewWin::OpenMatch(const AutocompleteMatch& match,
669                               WindowOpenDisposition disposition,
670                               const GURL& alternate_nav_url,
671                               size_t selected_line) {
672  // When we navigate, we first revert to the unedited state, then if necessary
673  // synchronously change the permanent text to the new URL.  If we don't freeze
674  // here, the user could potentially see a flicker of the current URL before
675  // the new one reappears, which would look glitchy.
676  ScopedFreeze freeze(this, GetTextObjectModel());
677  OmniboxView::OpenMatch(match, disposition, alternate_nav_url, selected_line);
678}
679
680string16 OmniboxViewWin::GetText() const {
681  const int len = GetTextLength() + 1;
682  string16 str;
683  if (len > 1)
684    GetWindowText(WriteInto(&str, len), len);
685  return str;
686}
687
688void OmniboxViewWin::SetUserText(const string16& text,
689                                 const string16& display_text,
690                                 bool update_popup) {
691  ScopedFreeze freeze(this, GetTextObjectModel());
692  saved_selection_for_focus_change_.cpMin = -1;
693  OmniboxView::SetUserText(text, display_text, update_popup);
694}
695
696void OmniboxViewWin::SetWindowTextAndCaretPos(const string16& text,
697                                              size_t caret_pos,
698                                              bool update_popup,
699                                              bool notify_text_changed) {
700  SetWindowText(text.c_str());
701  PlaceCaretAt(caret_pos);
702
703  if (update_popup)
704    UpdatePopup();
705
706  if (notify_text_changed)
707    TextChanged();
708}
709
710void OmniboxViewWin::SetForcedQuery() {
711  const string16 current_text(GetText());
712  const size_t start = current_text.find_first_not_of(kWhitespaceWide);
713  if (start == string16::npos || (current_text[start] != '?'))
714    OmniboxView::SetUserText(L"?");
715  else
716    SetSelection(current_text.length(), start + 1);
717}
718
719bool OmniboxViewWin::IsSelectAll() const {
720  CHARRANGE selection;
721  GetSel(selection);
722  return IsSelectAllForRange(selection);
723}
724
725bool OmniboxViewWin::DeleteAtEndPressed() {
726  return delete_at_end_pressed_;
727}
728
729void OmniboxViewWin::GetSelectionBounds(string16::size_type* start,
730                                        string16::size_type* end) const {
731  CHARRANGE selection;
732  GetSel(selection);
733  *start = static_cast<size_t>(selection.cpMin);
734  *end = static_cast<size_t>(selection.cpMax);
735}
736
737void OmniboxViewWin::SelectAll(bool reversed) {
738  if (reversed)
739    SetSelection(GetTextLength(), 0);
740  else
741    SetSelection(0, GetTextLength());
742}
743
744void OmniboxViewWin::RevertAll() {
745  ScopedFreeze freeze(this, GetTextObjectModel());
746  saved_selection_for_focus_change_.cpMin = -1;
747  OmniboxView::RevertAll();
748}
749
750void OmniboxViewWin::UpdatePopup() {
751  ScopedFreeze freeze(this, GetTextObjectModel());
752  model()->SetInputInProgress(true);
753
754  // Don't allow the popup to open while the candidate window is open, so
755  // they don't overlap.
756  if (ime_candidate_window_open_)
757    return;
758
759  if (!model()->has_focus()) {
760    // When we're in the midst of losing focus, don't rerun autocomplete.  This
761    // can happen when losing focus causes the IME to cancel/finalize a
762    // composition.  We still want to note that user input is in progress, we
763    // just don't want to do anything else.
764    //
765    // Note that in this case the ScopedFreeze above was unnecessary; however,
766    // we're inside the callstack of OnKillFocus(), which has already frozen the
767    // edit, so this will never result in an unnecessary UpdateWindow() call.
768    return;
769  }
770
771  // Don't inline autocomplete when:
772  //   * The user is deleting text
773  //   * The caret/selection isn't at the end of the text
774  //   * The user has just pasted in something that replaced all the text
775  //   * The user is trying to compose something in an IME
776  CHARRANGE sel;
777  GetSel(sel);
778  model()->StartAutocomplete(sel.cpMax != sel.cpMin,
779                            (sel.cpMax < GetTextLength()) || IsImeComposing());
780}
781
782void OmniboxViewWin::SetFocus() {
783  ::SetFocus(m_hWnd);
784  // Restore caret visibility if focus is explicitly requested. This is
785  // necessary because if we already have invisible focus, the ::SetFocus()
786  // call above will short-circuit, preventing us from reaching
787  // OmniboxEditModel::OnSetFocus(), which handles restoring visibility when the
788  // omnibox regains focus after losing focus.
789  model()->SetCaretVisibility(true);
790}
791
792void OmniboxViewWin::ApplyCaretVisibility() {
793  // We hide the caret just before destroying it, since destroying a caret that
794  // is in the "solid" phase of its blinking will leave a solid vertical bar.
795  // We even hide and destroy the caret if we're going to create it again below.
796  // If the caret was already visible on entry to this function, the
797  // CreateCaret() call (which first destroys the old caret) might leave a solid
798  // vertical bar for the same reason as above.  Unconditionally hiding prevents
799  // this.  The caret could be visible on entry to this function if the
800  // underlying edit control had re-created it automatically (see comments in
801  // OnPaint()).
802  HideCaret();
803  // We use DestroyCaret()/CreateCaret() instead of simply HideCaret()/
804  // ShowCaret() because HideCaret() is not sticky across paint events, e.g. a
805  // window resize will effectively restore caret visibility, regardless of
806  // whether HideCaret() was called before. While we do catch and handle these
807  // paint events (see OnPaint()), it doesn't seem to be enough to simply call
808  // HideCaret() while handling them because of the unpredictability of this
809  // Windows API. According to the documentation, it should be a cumulative call
810  // e.g. 5 hide calls should be balanced by 5 show calls. We have not found
811  // this to be true, which may be explained by the fact that this API is called
812  // internally in Windows, as well.
813  ::DestroyCaret();
814  if (model()->is_caret_visible()) {
815    ::CreateCaret(m_hWnd, (HBITMAP) NULL, 1, font_.GetHeight());
816    // According to the Windows API documentation, a newly created caret needs
817    // ShowCaret to be visible.
818    ShowCaret();
819  }
820}
821
822void OmniboxViewWin::SetDropHighlightPosition(int position) {
823  if (drop_highlight_position_ != position) {
824    RepaintDropHighlight(drop_highlight_position_);
825    drop_highlight_position_ = position;
826    RepaintDropHighlight(drop_highlight_position_);
827  }
828}
829
830void OmniboxViewWin::MoveSelectedText(int new_position) {
831  const string16 selected_text(GetSelectedText());
832  CHARRANGE sel;
833  GetSel(sel);
834  DCHECK((sel.cpMax != sel.cpMin) && (new_position >= 0) &&
835         (new_position <= GetTextLength()));
836
837  ScopedFreeze freeze(this, GetTextObjectModel());
838  OnBeforePossibleChange();
839
840  // Nuke the selected text.
841  ReplaceSel(L"", TRUE);
842
843  // And insert it into the new location.
844  if (new_position >= sel.cpMin)
845    new_position -= (sel.cpMax - sel.cpMin);
846  PlaceCaretAt(new_position);
847  ReplaceSel(selected_text.c_str(), TRUE);
848
849  OnAfterPossibleChange();
850}
851
852void OmniboxViewWin::InsertText(int position, const string16& text) {
853  DCHECK((position >= 0) && (position <= GetTextLength()));
854  ScopedFreeze freeze(this, GetTextObjectModel());
855  OnBeforePossibleChange();
856  SetSelection(position, position);
857  ReplaceSel(text.c_str());
858  OnAfterPossibleChange();
859}
860
861void OmniboxViewWin::OnTemporaryTextMaybeChanged(const string16& display_text,
862                                                 bool save_original_selection,
863                                                 bool notify_text_changed) {
864  if (save_original_selection)
865    GetSelection(original_selection_);
866
867  // Set new text and cursor position.  Sometimes this does extra work (e.g.
868  // when the new text and the old text are identical), but it's only called
869  // when the user manually changes the selected line in the popup, so that's
870  // not really a problem.  Also, even when the text hasn't changed we'd want to
871  // update the caret, because if the user had the cursor in the middle of the
872  // text and then arrowed to another entry with the same text, we'd still want
873  // to move the caret.
874  ScopedFreeze freeze(this, GetTextObjectModel());
875  SetWindowTextAndCaretPos(display_text, display_text.length(), false,
876                           notify_text_changed);
877}
878
879bool OmniboxViewWin::OnInlineAutocompleteTextMaybeChanged(
880    const string16& display_text,
881    size_t user_text_length) {
882  // Update the text and selection.  Because this can be called repeatedly while
883  // typing, we've careful not to freeze the edit unless we really need to.
884  // Also, unlike in the temporary text case above, here we don't want to update
885  // the caret/selection unless we have to, since this might make the user's
886  // caret position change without warning during typing.
887  if (display_text == GetText())
888    return false;
889
890  ScopedFreeze freeze(this, GetTextObjectModel());
891  SetWindowText(display_text.c_str());
892  // Set a reversed selection to keep the caret in the same position, which
893  // avoids scrolling the user's text.
894  SetSelection(static_cast<LONG>(display_text.length()),
895               static_cast<LONG>(user_text_length));
896  TextChanged();
897  return true;
898}
899
900void OmniboxViewWin::OnRevertTemporaryText() {
901  SetSelectionRange(original_selection_);
902  // We got here because the user hit the Escape key. We explicitly don't call
903  // TextChanged(), since calling it breaks Instant-Extended, and isn't needed
904  // otherwise (in regular non-Instant or Instant-but-not-Extended modes).
905  //
906  // Why it breaks Instant-Extended: Instant handles the Escape key separately
907  // (cf: OmniboxEditModel::RevertTemporaryText). Calling TextChanged() makes
908  // the page think the user additionally typed some text, causing it to update
909  // its suggestions dropdown with new suggestions, which is wrong.
910  //
911  // Why it isn't needed: OmniboxPopupModel::ResetToDefaultMatch() has already
912  // been called by now; it would've called TextChanged() if it was warranted.
913}
914
915void OmniboxViewWin::OnBeforePossibleChange() {
916  // Record our state.
917  text_before_change_ = GetText();
918  GetSelection(sel_before_change_);
919}
920
921bool OmniboxViewWin::OnAfterPossibleChange() {
922  return OnAfterPossibleChangeInternal(false);
923}
924
925bool OmniboxViewWin::OnAfterPossibleChangeInternal(bool force_text_changed) {
926  // Prevent the user from selecting the "phantom newline" at the end of the
927  // edit.  If they try, we just silently move the end of the selection back to
928  // the end of the real text.
929  CHARRANGE new_sel;
930  GetSelection(new_sel);
931  const int length = GetTextLength();
932  if ((new_sel.cpMin > length) || (new_sel.cpMax > length)) {
933    if (new_sel.cpMin > length)
934      new_sel.cpMin = length;
935    if (new_sel.cpMax > length)
936      new_sel.cpMax = length;
937    SetSelectionRange(new_sel);
938  }
939  const bool selection_differs =
940      ((new_sel.cpMin != new_sel.cpMax) ||
941       (sel_before_change_.cpMin != sel_before_change_.cpMax)) &&
942      ((new_sel.cpMin != sel_before_change_.cpMin) ||
943       (new_sel.cpMax != sel_before_change_.cpMax));
944
945  // See if the text or selection have changed since OnBeforePossibleChange().
946  const string16 new_text(GetText());
947  const bool text_differs = (new_text != text_before_change_) ||
948      force_text_changed;
949
950  // When the user has deleted text, we don't allow inline autocomplete.  Make
951  // sure to not flag cases like selecting part of the text and then pasting
952  // (or typing) the prefix of that selection.  (We detect these by making
953  // sure the caret, which should be after any insertion, hasn't moved
954  // forward of the old selection start.)
955  const bool just_deleted_text =
956      (text_before_change_.length() > new_text.length()) &&
957      (new_sel.cpMin <= std::min(sel_before_change_.cpMin,
958                                 sel_before_change_.cpMax));
959
960  const bool something_changed = model()->OnAfterPossibleChange(
961      text_before_change_, new_text, new_sel.cpMin, new_sel.cpMax,
962      selection_differs, text_differs, just_deleted_text, !IsImeComposing());
963
964  if (selection_differs)
965    controller()->OnSelectionBoundsChanged();
966
967  if (something_changed && text_differs)
968    TextChanged();
969
970  if (text_differs) {
971    // Note that a TEXT_CHANGED event implies that the cursor/selection
972    // probably changed too, so we don't need to send both.
973    native_view_host_->NotifyAccessibilityEvent(
974        ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true);
975  } else if (selection_differs) {
976    // Notify assistive technology that the cursor or selection changed.
977    native_view_host_->NotifyAccessibilityEvent(
978        ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true);
979  } else if (delete_at_end_pressed_) {
980    model()->OnChanged();
981  }
982
983  return something_changed;
984}
985
986void OmniboxViewWin::OnCandidateWindowCountChanged(size_t window_count) {
987  ime_candidate_window_open_ = (window_count != 0);
988  if (ime_candidate_window_open_) {
989    CloseOmniboxPopup();
990  } else if (model()->user_input_in_progress()) {
991    // UpdatePopup assumes user input is in progress, so only call it if
992    // that's the case. Otherwise, autocomplete may run on an empty user
993    // text.
994    UpdatePopup();
995  }
996}
997
998void OmniboxViewWin::OnTextUpdated(const ui::Range& /*composition_range*/) {
999  if (ignore_ime_messages_)
1000    return;
1001  OnAfterPossibleChangeInternal(true);
1002  // Call OnBeforePossibleChange function here to get correct diff in next IME
1003  // update. The Text Services Framework does not provide any notification
1004  // before entering edit session, therefore we don't have good place to call
1005  // OnBeforePossibleChange.
1006  OnBeforePossibleChange();
1007}
1008
1009gfx::NativeView OmniboxViewWin::GetNativeView() const {
1010  return m_hWnd;
1011}
1012
1013// static
1014gfx::NativeView OmniboxViewWin::GetRelativeWindowForNativeView(
1015    gfx::NativeView edit_native_view) {
1016  // When an IME is attached to the rich-edit control, retrieve its window
1017  // handle, and the popup window of OmniboxPopupView will be shown under the
1018  // IME windows.
1019  // Otherwise, the popup window will be shown under top-most windows.
1020  // TODO(hbono): http://b/1111369 if we exclude this popup window from the
1021  // display area of IME windows, this workaround becomes unnecessary.
1022  HWND ime_window = ImmGetDefaultIMEWnd(edit_native_view);
1023  return ime_window ? ime_window : HWND_NOTOPMOST;
1024}
1025
1026gfx::NativeView OmniboxViewWin::GetRelativeWindowForPopup() const {
1027  return GetRelativeWindowForNativeView(GetNativeView());
1028}
1029
1030void OmniboxViewWin::SetInstantSuggestion(const string16& suggestion) {
1031  location_bar_->SetInstantSuggestion(suggestion);
1032}
1033
1034int OmniboxViewWin::TextWidth() const {
1035  return WidthNeededToDisplay(GetText());
1036}
1037
1038string16 OmniboxViewWin::GetInstantSuggestion() const {
1039  return location_bar_->GetInstantSuggestion();
1040}
1041
1042bool OmniboxViewWin::IsImeComposing() const {
1043  if (tsf_event_router_)
1044    return tsf_event_router_->IsImeComposing();
1045  bool ime_composing = false;
1046  HIMC context = ImmGetContext(m_hWnd);
1047  if (context) {
1048    ime_composing = !!ImmGetCompositionString(context, GCS_COMPSTR, NULL, 0);
1049    ImmReleaseContext(m_hWnd, context);
1050  }
1051  return ime_composing;
1052}
1053
1054int OmniboxViewWin::GetMaxEditWidth(int entry_width) const {
1055  RECT formatting_rect;
1056  GetRect(&formatting_rect);
1057  RECT edit_bounds;
1058  GetClientRect(&edit_bounds);
1059  return entry_width - formatting_rect.left -
1060      (edit_bounds.right - formatting_rect.right);
1061}
1062
1063views::View* OmniboxViewWin::AddToView(views::View* parent) {
1064  native_view_host_ = new OmniboxViewWrapper(this);
1065  parent->AddChildView(native_view_host_);
1066  native_view_host_->set_focus_view(parent);
1067  native_view_host_->Attach(GetNativeView());
1068  return native_view_host_;
1069}
1070
1071int OmniboxViewWin::OnPerformDrop(const ui::DropTargetEvent& event) {
1072  return OnPerformDropImpl(event, false);
1073}
1074
1075int OmniboxViewWin::OnPerformDropImpl(const ui::DropTargetEvent& event,
1076                                      bool in_drag) {
1077  const ui::OSExchangeData& data = event.data();
1078
1079  if (data.HasURL()) {
1080    GURL url;
1081    string16 title;
1082    if (data.GetURLAndTitle(&url, &title)) {
1083      string16 text(StripJavascriptSchemas(UTF8ToUTF16(url.spec())));
1084      OmniboxView::SetUserText(text);
1085      model()->AcceptInput(CURRENT_TAB, true);
1086      return CopyOrLinkDragOperation(event.source_operations());
1087    }
1088  } else if (data.HasString()) {
1089    int string_drop_position = drop_highlight_position();
1090    string16 text;
1091    if ((string_drop_position != -1 || !in_drag) && data.GetString(&text)) {
1092      DCHECK(string_drop_position == -1 ||
1093             ((string_drop_position >= 0) &&
1094              (string_drop_position <= GetTextLength())));
1095      if (in_drag) {
1096        if (event.source_operations()== ui::DragDropTypes::DRAG_MOVE)
1097          MoveSelectedText(string_drop_position);
1098        else
1099          InsertText(string_drop_position, text);
1100      } else {
1101        string16 collapsed_text(CollapseWhitespace(text, true));
1102        if (model()->CanPasteAndGo(collapsed_text))
1103          model()->PasteAndGo(collapsed_text);
1104      }
1105      return CopyOrLinkDragOperation(event.source_operations());
1106    }
1107  }
1108
1109  return ui::DragDropTypes::DRAG_NONE;
1110}
1111
1112void OmniboxViewWin::CopyURL() {
1113  DoCopyURL(toolbar_model()->GetURL(), toolbar_model()->GetText(false));
1114}
1115
1116bool OmniboxViewWin::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
1117  // Skip processing of [Alt]+<num-pad digit> Unicode alt key codes.
1118  if (event.IsUnicodeKeyCode())
1119    return true;
1120
1121  // Skip accelerators for key combinations omnibox wants to crack. This list
1122  // should be synced with OnKeyDownOnlyWritable() (but for tab which is dealt
1123  // with above in LocationBarView::SkipDefaultKeyEventProcessing).
1124  //
1125  // We cannot return true for all keys because we still need to handle some
1126  // accelerators (e.g., F5 for reload the page should work even when the
1127  // Omnibox gets focused).
1128  switch (event.key_code()) {
1129    case ui::VKEY_ESCAPE: {
1130      ScopedFreeze freeze(this, GetTextObjectModel());
1131      return model()->OnEscapeKeyPressed();
1132    }
1133
1134    case ui::VKEY_RETURN:
1135      return true;
1136
1137    case ui::VKEY_UP:
1138    case ui::VKEY_DOWN:
1139      return !event.IsAltDown();
1140
1141    case ui::VKEY_DELETE:
1142    case ui::VKEY_INSERT:
1143      return !event.IsAltDown() && event.IsShiftDown() &&
1144          !event.IsControlDown();
1145
1146    case ui::VKEY_X:
1147    case ui::VKEY_V:
1148      return !event.IsAltDown() && event.IsControlDown();
1149
1150    case ui::VKEY_BACK:
1151      return true;
1152
1153    default:
1154      return false;
1155  }
1156}
1157
1158void OmniboxViewWin::HandleExternalMsg(UINT msg,
1159                                       UINT flags,
1160                                       const CPoint& screen_point) {
1161  if (msg == WM_CAPTURECHANGED) {
1162    SendMessage(msg, 0, NULL);
1163    return;
1164  }
1165
1166  CPoint client_point(screen_point);
1167  ::MapWindowPoints(NULL, m_hWnd, &client_point, 1);
1168  SendMessage(msg, flags, MAKELPARAM(client_point.x, client_point.y));
1169}
1170
1171bool OmniboxViewWin::IsCommandIdChecked(int command_id) const {
1172  return false;
1173}
1174
1175bool OmniboxViewWin::IsCommandIdEnabled(int command_id) const {
1176  switch (command_id) {
1177    case IDS_UNDO:
1178      return !!CanUndo();
1179    case IDC_CUT:
1180      return !!CanCut();
1181    case IDC_COPY:
1182      return !!CanCopy();
1183    case IDC_COPY_URL:
1184      return !!CanCopy() &&
1185          !model()->user_input_in_progress() &&
1186          toolbar_model()->WouldReplaceSearchURLWithSearchTerms();
1187    case IDC_PASTE:
1188      return !!CanPaste();
1189    case IDS_PASTE_AND_GO:
1190      return model()->CanPasteAndGo(GetClipboardText());
1191    case IDS_SELECT_ALL:
1192      return !!CanSelectAll();
1193    case IDC_EDIT_SEARCH_ENGINES:
1194      return command_updater()->IsCommandEnabled(command_id);
1195    default:
1196      NOTREACHED();
1197      return false;
1198  }
1199}
1200
1201bool OmniboxViewWin::GetAcceleratorForCommandId(
1202    int command_id,
1203    ui::Accelerator* accelerator) {
1204  return location_bar_->GetWidget()->GetAccelerator(command_id, accelerator);
1205}
1206
1207bool OmniboxViewWin::IsItemForCommandIdDynamic(int command_id) const {
1208  // No need to change the default IDS_PASTE_AND_GO label unless this is a
1209  // search.
1210  return command_id == IDS_PASTE_AND_GO;
1211}
1212
1213string16 OmniboxViewWin::GetLabelForCommandId(int command_id) const {
1214  DCHECK_EQ(IDS_PASTE_AND_GO, command_id);
1215  return l10n_util::GetStringUTF16(
1216      model()->IsPasteAndSearch(GetClipboardText()) ?
1217      IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO);
1218}
1219
1220void OmniboxViewWin::ExecuteCommand(int command_id, int event_flags) {
1221  ScopedFreeze freeze(this, GetTextObjectModel());
1222  // These commands don't invoke the popup via OnBefore/AfterPossibleChange().
1223  if (command_id == IDS_PASTE_AND_GO) {
1224    model()->PasteAndGo(GetClipboardText());
1225    return;
1226  } else if (command_id == IDC_EDIT_SEARCH_ENGINES) {
1227    command_updater()->ExecuteCommand(command_id);
1228    return;
1229  } else if (command_id == IDC_COPY) {
1230    Copy();
1231    return;
1232  } else if (command_id == IDC_COPY_URL) {
1233    CopyURL();
1234    return;
1235  }
1236
1237  OnBeforePossibleChange();
1238  switch (command_id) {
1239    case IDS_UNDO:
1240      Undo();
1241      break;
1242
1243    case IDC_CUT:
1244      Cut();
1245      break;
1246
1247    case IDC_PASTE:
1248      Paste();
1249      break;
1250
1251    case IDS_SELECT_ALL:
1252      SelectAll(false);
1253      break;
1254
1255    default:
1256      NOTREACHED();
1257      break;
1258  }
1259  OnAfterPossibleChange();
1260}
1261
1262// static
1263int CALLBACK OmniboxViewWin::WordBreakProc(LPTSTR edit_text,
1264                                           int current_pos,
1265                                           int length,
1266                                           int action) {
1267  // TODO(pkasting): http://b/1111308 We should let other people, like ICU and
1268  // GURL, do the work for us here instead of writing all this ourselves.
1269
1270  // With no clear guidance from the MSDN docs on how to handle "not found" in
1271  // the "find the nearest xxx..." cases below, I cap the return values at
1272  // [0, length].  Since one of these (0) is also a valid position, the return
1273  // values are thus ambiguous :(
1274  switch (action) {
1275    // Find nearest character before current position that begins a word.
1276    case WB_LEFT:
1277    case WB_MOVEWORDLEFT: {
1278      if (current_pos < 2) {
1279        // Either current_pos == 0, so we have a "not found" case and return 0,
1280        // or current_pos == 1, and the only character before this position is
1281        // at 0.
1282        return 0;
1283      }
1284
1285      // Look for a delimiter before the previous character; the previous word
1286      // starts immediately after.  (If we looked for a delimiter before the
1287      // current character, we could stop on the immediate prior character,
1288      // which would mean we'd return current_pos -- which isn't "before the
1289      // current position".)
1290      const int prev_delim =
1291          WordBreakProc(edit_text, current_pos - 1, length, WB_LEFTBREAK);
1292
1293      if ((prev_delim == 0) &&
1294          !WordBreakProc(edit_text, 0, length, WB_ISDELIMITER)) {
1295        // Got back 0, but position 0 isn't a delimiter.  This was a "not
1296        // found" 0, so return one of our own.
1297        return 0;
1298      }
1299
1300      return prev_delim + 1;
1301    }
1302
1303    // Find nearest character after current position that begins a word.
1304    case WB_RIGHT:
1305    case WB_MOVEWORDRIGHT: {
1306      if (WordBreakProc(edit_text, current_pos, length, WB_ISDELIMITER)) {
1307        // The current character is a delimiter, so the next character starts
1308        // a new word.  Done.
1309        return current_pos + 1;
1310      }
1311
1312      // Look for a delimiter after the current character; the next word starts
1313      // immediately after.
1314      const int next_delim =
1315          WordBreakProc(edit_text, current_pos, length, WB_RIGHTBREAK);
1316      if (next_delim == length) {
1317        // Didn't find a delimiter.  Return length to signal "not found".
1318        return length;
1319      }
1320
1321      return next_delim + 1;
1322    }
1323
1324    // Determine if the current character delimits words.
1325    case WB_ISDELIMITER:
1326      return !!(WordBreakProc(edit_text, current_pos, length, WB_CLASSIFY) &
1327                WBF_BREAKLINE);
1328
1329    // Return the classification of the current character.
1330    case WB_CLASSIFY:
1331      if (IsWhitespace(edit_text[current_pos])) {
1332        // Whitespace normally breaks words, but the MSDN docs say that we must
1333        // not break on the CRs in a "CR, LF" or a "CR, CR, LF" sequence.  Just
1334        // check for an arbitrarily long sequence of CRs followed by LF and
1335        // report "not a delimiter" for the current CR in that case.
1336        while ((current_pos < (length - 1)) &&
1337               (edit_text[current_pos] == 0x13)) {
1338          if (edit_text[++current_pos] == 0x10)
1339            return WBF_ISWHITE;
1340        }
1341        return WBF_BREAKLINE | WBF_ISWHITE;
1342      }
1343
1344      // Punctuation normally breaks words, but the first two characters in
1345      // "://" (end of scheme) should not be breaks, so that "http://" will be
1346      // treated as one word.
1347      if (ispunct(edit_text[current_pos], std::locale()) &&
1348          !SchemeEnd(edit_text, current_pos, length) &&
1349          !SchemeEnd(edit_text, current_pos - 1, length))
1350        return WBF_BREAKLINE;
1351
1352      // Normal character, no flags.
1353      return 0;
1354
1355    // Finds nearest delimiter before current position.
1356    case WB_LEFTBREAK:
1357      for (int i = current_pos - 1; i >= 0; --i) {
1358        if (WordBreakProc(edit_text, i, length, WB_ISDELIMITER))
1359          return i;
1360      }
1361      return 0;
1362
1363    // Finds nearest delimiter after current position.
1364    case WB_RIGHTBREAK:
1365      for (int i = current_pos + 1; i < length; ++i) {
1366        if (WordBreakProc(edit_text, i, length, WB_ISDELIMITER))
1367          return i;
1368      }
1369      return length;
1370  }
1371
1372  NOTREACHED();
1373  return 0;
1374}
1375
1376// static
1377bool OmniboxViewWin::SchemeEnd(LPTSTR edit_text,
1378                               int current_pos,
1379                               int length) {
1380  return (current_pos >= 0) &&
1381         ((length - current_pos) > 2) &&
1382         (edit_text[current_pos] == ':') &&
1383         (edit_text[current_pos + 1] == '/') &&
1384         (edit_text[current_pos + 2] == '/');
1385}
1386
1387void OmniboxViewWin::OnChar(TCHAR ch, UINT repeat_count, UINT flags) {
1388  // Don't let alt-enter beep.  Not sure this is necessary, as the standard
1389  // alt-enter will hit DiscardWMSysChar() and get thrown away, and
1390  // ctrl-alt-enter doesn't seem to reach here for some reason?  At least not on
1391  // my system... still, this is harmless and maybe necessary in other locales.
1392  if (ch == VK_RETURN && (flags & KF_ALTDOWN))
1393    return;
1394
1395  // Escape is processed in OnKeyDown.  Don't let any WM_CHAR messages propagate
1396  // as we don't want the RichEdit to do anything funky.
1397  if (ch == VK_ESCAPE && !(flags & KF_ALTDOWN))
1398    return;
1399
1400  if (ch == VK_TAB) {
1401    // Don't add tabs to the input.
1402    return;
1403  }
1404
1405  HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags);
1406}
1407
1408void OmniboxViewWin::OnContextMenu(HWND window, const CPoint& point) {
1409  BuildContextMenu();
1410
1411  context_menu_runner_.reset(
1412      new views::MenuRunner(context_menu_contents_.get()));
1413
1414  gfx::Point location(point);
1415  if (point.x == -1 || point.y == -1) {
1416    POINT p;
1417    GetCaretPos(&p);
1418    MapWindowPoints(HWND_DESKTOP, &p, 1);
1419    location.SetPoint(p.x, p.y);
1420  }
1421
1422  ignore_result(context_menu_runner_->RunMenuAt(native_view_host_->GetWidget(),
1423      NULL, gfx::Rect(location, gfx::Size()), views::MenuItemView::TOPLEFT,
1424      ui::MENU_SOURCE_MOUSE, views::MenuRunner::HAS_MNEMONICS));
1425}
1426
1427void OmniboxViewWin::OnCopy() {
1428  string16 text(GetSelectedText());
1429  if (text.empty())
1430    return;
1431
1432  CHARRANGE sel;
1433  GURL url;
1434  bool write_url = false;
1435  GetSel(sel);
1436  // GetSel() doesn't preserve selection direction, so sel.cpMin will always be
1437  // the smaller value.
1438  model()->AdjustTextForCopy(sel.cpMin, IsSelectAll(), &text, &url, &write_url);
1439  if (write_url)
1440    DoCopyURL(url, text);
1441  else
1442    DoCopyText(text);
1443}
1444
1445LRESULT OmniboxViewWin::OnCreate(const CREATESTRUCTW* /*create_struct*/) {
1446  if (base::win::IsTSFAwareRequired()) {
1447    // Enable TSF support of RichEdit.
1448    SetEditStyle(SES_USECTF, SES_USECTF);
1449  }
1450  if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
1451    BOOL touch_mode = RegisterTouchWindow(m_hWnd, TWF_WANTPALM);
1452    DCHECK(touch_mode);
1453  }
1454  SetMsgHandled(FALSE);
1455
1456  // When TSF is enabled, OnTextUpdated() may be called without any previous
1457  // call that would have indicated the start of an editing session.  In order
1458  // to guarantee we've always called OnBeforePossibleChange() before
1459  // OnAfterPossibleChange(), we therefore call that here.  Note that multiple
1460  // (i.e. unmatched) calls to this function in a row are safe.
1461  if (base::win::IsTSFAwareRequired())
1462    OnBeforePossibleChange();
1463  return 0;
1464}
1465
1466void OmniboxViewWin::OnCut() {
1467  OnCopy();
1468
1469  // This replace selection will have no effect (even on the undo stack) if the
1470  // current selection is empty.
1471  ReplaceSel(L"", true);
1472}
1473
1474LRESULT OmniboxViewWin::OnGetObject(UINT message,
1475                                    WPARAM wparam,
1476                                    LPARAM lparam) {
1477  // This is a request for the native accessibility object.
1478  if (lparam == OBJID_CLIENT) {
1479    return LresultFromObject(IID_IAccessible, wparam,
1480                             native_view_host_->GetNativeViewAccessible());
1481  }
1482  return 0;
1483}
1484
1485LRESULT OmniboxViewWin::OnImeComposition(UINT message,
1486                                         WPARAM wparam,
1487                                         LPARAM lparam) {
1488  if (ignore_ime_messages_) {
1489    // This message was sent while we're in the middle of meddling with the
1490    // underlying edit control.  If we handle it below, OnAfterPossibleChange()
1491    // can get bogus text for the edit, and rerun autocomplete, destructively
1492    // modifying the result set that we're in the midst of using.  For example,
1493    // if SetWindowTextAndCaretPos() was called due to the user clicking an
1494    // entry in the popup, we're in the middle of executing SetSelectedLine(),
1495    // and changing the results can cause checkfailures.
1496    return DefWindowProc(message, wparam, lparam);
1497  }
1498
1499  ScopedFreeze freeze(this, GetTextObjectModel());
1500  OnBeforePossibleChange();
1501  LRESULT result = DefWindowProc(message, wparam, lparam);
1502  // Force an IME composition confirmation operation to trigger the text_changed
1503  // code in OnAfterPossibleChange(), even if identical contents are confirmed,
1504  // to make sure the model can update its internal states correctly.
1505  OnAfterPossibleChangeInternal((lparam & GCS_RESULTSTR) != 0);
1506  return result;
1507}
1508
1509LRESULT OmniboxViewWin::OnImeEndComposition(UINT message, WPARAM wparam,
1510                                            LPARAM lparam) {
1511  // The edit control auto-clears the selection on WM_IME_ENDCOMPOSITION, which
1512  // means any inline autocompletion we were showing will no longer be
1513  // selected, and therefore no longer replaced by further user typing.  To
1514  // avoid this we manually restore the original selection after the edit
1515  // handles the message.
1516  CHARRANGE range;
1517  GetSel(range);
1518  LRESULT result = DefWindowProc(message, wparam, lparam);
1519  SetSel(range);
1520  return result;
1521}
1522
1523LRESULT OmniboxViewWin::OnImeNotify(UINT message,
1524                                    WPARAM wparam,
1525                                    LPARAM lparam) {
1526  // Close the popup when the IME composition window is open, so they don't
1527  // overlap.
1528  switch (wparam) {
1529    case IMN_OPENCANDIDATE:
1530      ime_candidate_window_open_ = true;
1531      CloseOmniboxPopup();
1532      break;
1533    case IMN_CLOSECANDIDATE:
1534      ime_candidate_window_open_ = false;
1535
1536      // UpdatePopup assumes user input is in progress, so only call it if
1537      // that's the case. Otherwise, autocomplete may run on an empty user
1538      // text. For example, Baidu Japanese IME sends IMN_CLOSECANDIDATE when
1539      // composition mode is entered, but the user may not have input anything
1540      // yet.
1541      if (model()->user_input_in_progress())
1542        UpdatePopup();
1543
1544      break;
1545    default:
1546      break;
1547  }
1548  return DefWindowProc(message, wparam, lparam);
1549}
1550
1551LRESULT OmniboxViewWin::OnTouchEvent(UINT message,
1552                                     WPARAM wparam,
1553                                     LPARAM lparam) {
1554  // There is a bug in Windows 8 where in the generated mouse messages
1555  // after touch go to the window which previously had focus. This means that
1556  // if a user taps the omnibox to give it focus, we don't get the simulated
1557  // WM_LBUTTONDOWN, and thus don't properly select all the text. To ensure
1558  // that we get this message, we capture the mouse when the user is doing a
1559  // single-point tap on an unfocused model.
1560  if ((wparam == 1) && !model()->has_focus()) {
1561    TOUCHINPUT point = {0};
1562    if (ui::GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(lparam), 1,
1563                                     &point, sizeof(TOUCHINPUT))) {
1564      if (point.dwFlags & TOUCHEVENTF_DOWN)
1565        SetCapture();
1566      else if (point.dwFlags & TOUCHEVENTF_UP)
1567        ReleaseCapture();
1568    }
1569  }
1570  SetMsgHandled(false);
1571  return 0;
1572}
1573
1574void OmniboxViewWin::OnKeyDown(TCHAR key,
1575                               UINT repeat_count,
1576                               UINT flags) {
1577  delete_at_end_pressed_ = false;
1578
1579  if (OnKeyDownAllModes(key, repeat_count, flags))
1580    return;
1581
1582  // Make sure that we handle system key events like Alt-F4.
1583  if (popup_window_mode_) {
1584    DefWindowProc(GetCurrentMessage()->message, key, MAKELPARAM(repeat_count,
1585                                                                flags));
1586    return;
1587  }
1588
1589  if (OnKeyDownOnlyWritable(key, repeat_count, flags))
1590    return;
1591
1592  // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many
1593  // different keys (backspace, ctrl-v, ...), so we call this in both cases.
1594  HandleKeystroke(GetCurrentMessage()->message, key, repeat_count, flags);
1595}
1596
1597void OmniboxViewWin::OnKeyUp(TCHAR key,
1598                             UINT repeat_count,
1599                             UINT flags) {
1600  if (key == VK_CONTROL)
1601    model()->OnControlKeyChanged(false);
1602
1603  // On systems with RTL input languages, ctrl+shift toggles the reading order
1604  // (depending on which shift key is pressed). But by default the CRichEditCtrl
1605  // only changes the current reading order, and as soon as the user deletes all
1606  // the text, or we call SetWindowText(), it reverts to the "default" order.
1607  // To work around this, if the user hits ctrl+shift, we pass it to
1608  // DefWindowProc() while the edit is empty, which toggles the default reading
1609  // order; then we restore the user's input.
1610  if (!(flags & KF_ALTDOWN) &&
1611      (((key == VK_CONTROL) && (GetKeyState(VK_SHIFT) < 0)) ||
1612       ((key == VK_SHIFT) && (GetKeyState(VK_CONTROL) < 0)))) {
1613    ScopedFreeze freeze(this, GetTextObjectModel());
1614
1615    string16 saved_text(GetText());
1616    CHARRANGE saved_sel;
1617    GetSelection(saved_sel);
1618
1619    SetWindowText(L"");
1620
1621    DefWindowProc(WM_KEYUP, key, MAKELPARAM(repeat_count, flags));
1622
1623    SetWindowText(saved_text.c_str());
1624    SetSelectionRange(saved_sel);
1625    return;
1626  }
1627
1628  SetMsgHandled(false);
1629}
1630
1631void OmniboxViewWin::OnKillFocus(HWND focus_wnd) {
1632  if (m_hWnd == focus_wnd) {
1633    // Focus isn't actually leaving.
1634    SetMsgHandled(false);
1635    return;
1636  }
1637
1638  // This must be invoked before ClosePopup.
1639  model()->OnWillKillFocus(focus_wnd);
1640
1641  // Close the popup.
1642  CloseOmniboxPopup();
1643
1644  // Save the user's existing selection to restore it later.
1645  GetSelection(saved_selection_for_focus_change_);
1646
1647  // Tell the model to reset itself.
1648  model()->OnKillFocus();
1649
1650  // Let the CRichEditCtrl do its default handling.  This will complete any
1651  // in-progress IME composition.  We must do this after setting has_focus_ to
1652  // false so that UpdatePopup() will know not to rerun autocomplete.
1653  ScopedFreeze freeze(this, GetTextObjectModel());
1654  DefWindowProc(WM_KILLFOCUS, reinterpret_cast<WPARAM>(focus_wnd), 0);
1655
1656  // Cancel any user selection and scroll the text back to the beginning of the
1657  // URL.  We have to do this after calling DefWindowProc() because otherwise
1658  // an in-progress IME composition will be completed at the new caret position,
1659  // resulting in the string jumping unexpectedly to the front of the edit.
1660  //
1661  // Crazy hack: If we just do PlaceCaretAt(0), and the beginning of the text is
1662  // currently scrolled out of view, we can wind up with a blinking cursor in
1663  // the toolbar at the current X coordinate of the beginning of the text.  By
1664  // first doing a reverse-select-all to scroll the beginning of the text into
1665  // view, we work around this CRichEditCtrl bug.
1666  SelectAll(true);
1667  PlaceCaretAt(0);
1668
1669  if (tsf_event_router_)
1670    tsf_event_router_->SetManager(NULL);
1671}
1672
1673void OmniboxViewWin::OnLButtonDblClk(UINT keys, const CPoint& point) {
1674  // Save the double click info for later triple-click detection.
1675  tracking_double_click_ = true;
1676  double_click_point_ = point;
1677  double_click_time_ = GetCurrentMessage()->time;
1678  possible_drag_ = false;
1679
1680  // Modifying the selection counts as accepting any inline autocompletion, so
1681  // track "changes" made by clicking the mouse button.
1682  ScopedFreeze freeze(this, GetTextObjectModel());
1683  OnBeforePossibleChange();
1684
1685  DefWindowProc(WM_LBUTTONDBLCLK, keys,
1686                MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
1687
1688  // Rich Edit 4.1 doesn't select the last word when the user double clicks
1689  // past the text. Do it manually.
1690  CHARRANGE selection;
1691  GetSelection(selection);
1692  // The default window proc for Rich Edit 4.1 seems to select the CHARRANGE
1693  // {text_length, text_length + 1} after a double click past the text.
1694  int length = GetTextLength();
1695  if (selection.cpMin == length && selection.cpMax == length + 1) {
1696    string16 text = GetText();
1697    int word_break = WordBreakProc(&text[0], length, length, WB_LEFT);
1698    selection.cpMin = word_break;
1699    selection.cpMax = length;
1700    SetSelectionRange(selection);
1701  }
1702
1703  OnAfterPossibleChange();
1704
1705  gaining_focus_.reset();  // See NOTE in OnMouseActivate().
1706}
1707
1708void OmniboxViewWin::OnLButtonDown(UINT keys, const CPoint& point) {
1709  TrackMousePosition(kLeft, point);
1710  if (gaining_focus_.get()) {
1711    // When Chrome was already the activated app, we haven't reached
1712    // OnSetFocus() yet.  When we get there, don't restore the saved selection,
1713    // since it will just screw up the user's interaction with the edit.
1714    saved_selection_for_focus_change_.cpMin = -1;
1715
1716    // Crazy hack: In this particular case, the CRichEditCtrl seems to have an
1717    // internal flag that discards the next WM_LBUTTONDOWN without processing
1718    // it, so that clicks on the edit when its owning app is not activated are
1719    // eaten rather than processed (despite whatever the return value of
1720    // DefWindowProc(WM_MOUSEACTIVATE, ...) may say).  This behavior is
1721    // confusing and we want the click to be treated normally.  So, to reset the
1722    // CRichEditCtrl's internal flag, we pass it an extra WM_LBUTTONDOWN here
1723    // (as well as a matching WM_LBUTTONUP, just in case we'd be confusing some
1724    // kind of state tracking otherwise).
1725    DefWindowProc(WM_LBUTTONDOWN, keys, MAKELPARAM(point.x, point.y));
1726    DefWindowProc(WM_LBUTTONUP, keys, MAKELPARAM(point.x, point.y));
1727  }
1728
1729  // Check for triple click, then reset tracker.  Should be safe to subtract
1730  // double_click_time_ from the current message's time even if the timer has
1731  // wrapped in between.
1732  const bool is_triple_click = tracking_double_click_ &&
1733      views::NativeTextfieldWin::IsDoubleClick(double_click_point_, point,
1734          GetCurrentMessage()->time - double_click_time_);
1735  tracking_double_click_ = false;
1736
1737  if (!gaining_focus_.get() && !is_triple_click)
1738    OnPossibleDrag(point);
1739
1740  // Modifying the selection counts as accepting any inline autocompletion, so
1741  // track "changes" made by clicking the mouse button.
1742  ScopedFreeze freeze(this, GetTextObjectModel());
1743  OnBeforePossibleChange();
1744  DefWindowProc(WM_LBUTTONDOWN, keys,
1745                MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click),
1746                           point.y));
1747  OnAfterPossibleChange();
1748
1749  gaining_focus_.reset();
1750}
1751
1752void OmniboxViewWin::OnLButtonUp(UINT keys, const CPoint& point) {
1753  // default processing should happen first so we can see the result of the
1754  // selection
1755  ScopedFreeze freeze(this, GetTextObjectModel());
1756  DefWindowProc(WM_LBUTTONUP, keys,
1757                MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y));
1758
1759  SelectAllIfNecessary(kLeft, point);
1760
1761  tracking_click_[kLeft] = false;
1762
1763  possible_drag_ = false;
1764}
1765
1766void OmniboxViewWin::OnMButtonDblClk(UINT /*keys*/, const CPoint& /*point*/) {
1767  gaining_focus_.reset();  // See NOTE in OnMouseActivate().
1768
1769  // By default, the edit responds to middle-clicks by capturing the mouse and
1770  // ignoring all subsequent events until it receives another click (of any of
1771  // the left, middle, or right buttons).  This bizarre behavior is not only
1772  // useless but can cause the UI to appear unresponsive if a user accidentally
1773  // middle-clicks the edit (instead of a tab above it), so we purposefully eat
1774  // this message (instead of calling SetMsgHandled(false)) to avoid triggering
1775  // this.
1776}
1777
1778void OmniboxViewWin::OnMButtonDown(UINT /*keys*/, const CPoint& /*point*/) {
1779  tracking_double_click_ = false;
1780
1781  // See note in OnMButtonDblClk above.
1782}
1783
1784void OmniboxViewWin::OnMButtonUp(UINT /*keys*/, const CPoint& /*point*/) {
1785  possible_drag_ = false;
1786
1787  // See note in OnMButtonDblClk above.
1788}
1789
1790LRESULT OmniboxViewWin::OnMouseActivate(HWND window,
1791                                        UINT hit_test,
1792                                        UINT mouse_message) {
1793  // First, give other handlers a chance to handle the message to see if we are
1794  // actually going to activate and gain focus.
1795  LRESULT result = DefWindowProc(WM_MOUSEACTIVATE,
1796                                 reinterpret_cast<WPARAM>(window),
1797                                 MAKELPARAM(hit_test, mouse_message));
1798  // Check if we're getting focus from a click.  We have to do this here rather
1799  // than in OnXButtonDown() since in many scenarios OnSetFocus() will be
1800  // reached before OnXButtonDown(), preventing us from detecting this properly
1801  // there.  Also in those cases, we need to already know in OnSetFocus() that
1802  // we should not restore the saved selection.
1803  if ((!model()->has_focus() ||
1804       (model()->focus_state() == OMNIBOX_FOCUS_INVISIBLE)) &&
1805      ((mouse_message == WM_LBUTTONDOWN || mouse_message == WM_RBUTTONDOWN)) &&
1806      (result == MA_ACTIVATE)) {
1807    if (gaining_focus_) {
1808      // On Windows 8 in metro mode, we get two WM_MOUSEACTIVATE messages when
1809      // we click on the omnibox with the mouse.
1810      DCHECK(win8::IsSingleWindowMetroMode());
1811      return result;
1812    }
1813    gaining_focus_.reset(new ScopedFreeze(this, GetTextObjectModel()));
1814
1815    // Restore caret visibility whenever the user clicks in the omnibox in a
1816    // way that would give it focus.  We must handle this case separately here
1817    // because if the omnibox currently has invisible focus, the mouse event
1818    // won't trigger either SetFocus() or OmniboxEditModel::OnSetFocus().
1819    model()->SetCaretVisibility(true);
1820
1821    // NOTE: Despite |mouse_message| being WM_XBUTTONDOWN here, we're not
1822    // guaranteed to call OnXButtonDown() later!  Specifically, if this is the
1823    // second click of a double click, we'll reach here but later call
1824    // OnXButtonDblClk().  Make sure |gaining_focus_| gets reset both places,
1825    // or we'll have visual glitchiness and then DCHECK failures.
1826
1827    // Don't restore saved selection, it will just screw up our interaction
1828    // with this edit.
1829    saved_selection_for_focus_change_.cpMin = -1;
1830  }
1831  return result;
1832}
1833
1834void OmniboxViewWin::OnMouseMove(UINT keys, const CPoint& point) {
1835  if (possible_drag_) {
1836    StartDragIfNecessary(point);
1837    // Don't fall through to default mouse handling, otherwise a second
1838    // drag session may start.
1839    return;
1840  }
1841
1842  if (tracking_click_[kLeft] && !IsDrag(click_point_[kLeft], point))
1843    return;
1844
1845  tracking_click_[kLeft] = false;
1846
1847  // Return quickly if this can't change the selection/cursor, so we don't
1848  // create a ScopedFreeze (and thus do an UpdateWindow()) on every
1849  // WM_MOUSEMOVE.
1850  if (!(keys & MK_LBUTTON)) {
1851    DefWindowProc(WM_MOUSEMOVE, keys, MAKELPARAM(point.x, point.y));
1852    return;
1853  }
1854
1855  // Clamp the selection to the visible text so the user can't drag to select
1856  // the "phantom newline".  In theory we could achieve this by clipping the X
1857  // coordinate, but in practice the edit seems to behave nondeterministically
1858  // with similar sequences of clipped input coordinates fed to it.  Maybe it's
1859  // reading the mouse cursor position directly?
1860  //
1861  // This solution has a minor visual flaw, however: if there's a visible cursor
1862  // at the edge of the text (only true when there's no selection), dragging the
1863  // mouse around outside that edge repaints the cursor on every WM_MOUSEMOVE
1864  // instead of allowing it to blink normally.  To fix this, we special-case
1865  // this exact case and discard the WM_MOUSEMOVE messages instead of passing
1866  // them along.
1867  //
1868  // But even this solution has a flaw!  (Argh.)  In the case where the user has
1869  // a selection that starts at the edge of the edit, and proceeds to the middle
1870  // of the edit, and the user is dragging back past the start edge to remove
1871  // the selection, there's a redraw problem where the change between having the
1872  // last few bits of text still selected and having nothing selected can be
1873  // slow to repaint (which feels noticeably strange).  This occurs if you only
1874  // let the edit receive a single WM_MOUSEMOVE past the edge of the text.  I
1875  // think on each WM_MOUSEMOVE the edit is repainting its previous state, then
1876  // updating its internal variables to the new state but not repainting.  To
1877  // fix this, we allow one more WM_MOUSEMOVE through after the selection has
1878  // supposedly been shrunk to nothing; this makes the edit redraw the selection
1879  // quickly so it feels smooth.
1880  CHARRANGE selection;
1881  GetSel(selection);
1882  const bool possibly_can_discard_mousemove =
1883      (selection.cpMin == selection.cpMax) &&
1884      (((selection.cpMin == 0) &&
1885        (ClipXCoordToVisibleText(point.x, false) > point.x)) ||
1886       ((selection.cpMin == GetTextLength()) &&
1887        (ClipXCoordToVisibleText(point.x, false) < point.x)));
1888  if (!can_discard_mousemove_ || !possibly_can_discard_mousemove) {
1889    can_discard_mousemove_ = possibly_can_discard_mousemove;
1890    ScopedFreeze freeze(this, GetTextObjectModel());
1891    OnBeforePossibleChange();
1892    // Force the Y coordinate to the center of the clip rect.  The edit
1893    // behaves strangely when the cursor is dragged vertically: if the cursor
1894    // is in the middle of the text, drags inside the clip rect do nothing,
1895    // and drags outside the clip rect act as if the cursor jumped to the
1896    // left edge of the text.  When the cursor is at the right edge, drags of
1897    // just a few pixels vertically end up selecting the "phantom newline"...
1898    // sometimes.
1899    RECT r;
1900    GetRect(&r);
1901    DefWindowProc(WM_MOUSEMOVE, keys,
1902                  MAKELPARAM(point.x, (r.bottom - r.top) / 2));
1903    OnAfterPossibleChange();
1904  }
1905}
1906
1907void OmniboxViewWin::OnPaint(HDC bogus_hdc) {
1908  // We need to paint over the top of the edit.  If we simply let the edit do
1909  // its default painting, then do ours into the window DC, the screen is
1910  // updated in between and we can get flicker.  To avoid this, we force the
1911  // edit to paint into a memory DC, which we also paint onto, then blit the
1912  // whole thing to the screen.
1913
1914  // Don't paint if not necessary.
1915  CRect paint_clip_rect;
1916  if (!GetUpdateRect(&paint_clip_rect, true))
1917    return;
1918
1919  // Begin painting, and create a memory DC for the edit to paint into.
1920  CPaintDC paint_dc(m_hWnd);
1921  CDC memory_dc(CreateCompatibleDC(paint_dc));
1922  CRect rect;
1923  GetClientRect(&rect);
1924  // NOTE: This next call uses |paint_dc| instead of |memory_dc| because
1925  // |memory_dc| contains a 1x1 monochrome bitmap by default, which will cause
1926  // |memory_bitmap| to be monochrome, which isn't what we want.
1927  CBitmap memory_bitmap(CreateCompatibleBitmap(paint_dc, rect.Width(),
1928                                               rect.Height()));
1929  HBITMAP old_bitmap = memory_dc.SelectBitmap(memory_bitmap);
1930
1931  // Tell our intercept functions to supply our memory DC to the edit when it
1932  // tries to call BeginPaint().
1933  //
1934  // The sane way to do this would be to use WM_PRINTCLIENT to ask the edit to
1935  // paint into our desired DC.  Unfortunately, the Rich Edit 3.0 that ships
1936  // with Windows 2000/XP/Vista doesn't handle WM_PRINTCLIENT correctly; it
1937  // treats it just like WM_PAINT and calls BeginPaint(), ignoring our provided
1938  // DC.  The Rich Edit 6.0 that ships with Office 2007 handles this better, but
1939  // has other issues, and we can't redistribute that DLL anyway.  So instead,
1940  // we use this scary hack.
1941  //
1942  // NOTE: It's possible to get nested paint calls (!) (try setting the
1943  // permanent URL to something longer than the edit width, then selecting the
1944  // contents of the edit, typing a character, and hitting <esc>), so we can't
1945  // DCHECK(!edit_hwnd_) here.  Instead, just save off the old HWND, which most
1946  // of the time will be NULL.
1947  HWND old_edit_hwnd = edit_hwnd;
1948  edit_hwnd = m_hWnd;
1949  paint_struct = paint_dc.m_ps;
1950  paint_struct.hdc = memory_dc;
1951  DefWindowProc(WM_PAINT, reinterpret_cast<WPARAM>(bogus_hdc), 0);
1952
1953  // Make the selection look better.
1954  EraseTopOfSelection(&memory_dc, rect, paint_clip_rect);
1955
1956  // Draw a slash through the scheme if this is insecure.
1957  if (insecure_scheme_component_.is_nonempty())
1958    DrawSlashForInsecureScheme(memory_dc, rect, paint_clip_rect);
1959
1960  // Draw the drop highlight.
1961  if (drop_highlight_position_ != -1)
1962    DrawDropHighlight(memory_dc, rect, paint_clip_rect);
1963
1964  // Blit the memory DC to the actual paint DC and clean up.
1965  BitBlt(paint_dc, rect.left, rect.top, rect.Width(), rect.Height(), memory_dc,
1966         rect.left, rect.top, SRCCOPY);
1967  memory_dc.SelectBitmap(old_bitmap);
1968  edit_hwnd = old_edit_hwnd;
1969
1970  // If textfield has focus, reaffirm its caret visibility (without focus, a new
1971  // caret could be created and confuse the user as to where the focus is). This
1972  // needs to be called regardless of the current visibility of the caret. This
1973  // is because the underlying edit control will automatically re-create the
1974  // caret when it receives certain events that trigger repaints, e.g. window
1975  // resize events. This also checks for the existence of selected text, in
1976  // which case there shouldn't be a recreated caret since this would create
1977  // both a highlight and a blinking caret.
1978  if (model()->has_focus()) {
1979    CHARRANGE sel;
1980    GetSel(sel);
1981    if (sel.cpMin == sel.cpMax)
1982      ApplyCaretVisibility();
1983  }
1984}
1985
1986void OmniboxViewWin::OnPaste() {
1987  // Replace the selection if we have something to paste.
1988  const string16 text(GetClipboardText());
1989  if (!text.empty()) {
1990    // Record this paste, so we can do different behavior.
1991    model()->on_paste();
1992    // Force a Paste operation to trigger the text_changed code in
1993    // OnAfterPossibleChange(), even if identical contents are pasted into the
1994    // text box.
1995    text_before_change_.clear();
1996    ReplaceSel(text.c_str(), true);
1997  }
1998}
1999
2000void OmniboxViewWin::OnRButtonDblClk(UINT /*keys*/, const CPoint& /*point*/) {
2001  gaining_focus_.reset();  // See NOTE in OnMouseActivate().
2002  SetMsgHandled(false);
2003}
2004
2005void OmniboxViewWin::OnRButtonDown(UINT /*keys*/, const CPoint& point) {
2006  TrackMousePosition(kRight, point);
2007  tracking_double_click_ = false;
2008  possible_drag_ = false;
2009  gaining_focus_.reset();
2010  SetMsgHandled(false);
2011}
2012
2013void OmniboxViewWin::OnRButtonUp(UINT /*keys*/, const CPoint& point) {
2014  SelectAllIfNecessary(kRight, point);
2015  tracking_click_[kRight] = false;
2016  SetMsgHandled(false);
2017}
2018
2019void OmniboxViewWin::OnSetFocus(HWND focus_wnd) {
2020  views::FocusManager* focus_manager = location_bar_->GetFocusManager();
2021  if (focus_manager) {
2022    // Notify the FocusManager that the focused view is now the location bar
2023    // (our parent view).
2024    focus_manager->SetFocusedView(location_bar_);
2025  } else {
2026    NOTREACHED();
2027  }
2028
2029  model()->OnSetFocus(GetKeyState(VK_CONTROL) < 0);
2030
2031  // Restore saved selection if available.
2032  if (saved_selection_for_focus_change_.cpMin != -1) {
2033    SetSelectionRange(saved_selection_for_focus_change_);
2034    saved_selection_for_focus_change_.cpMin = -1;
2035  }
2036
2037  if (!tsf_event_router_) {
2038    SetMsgHandled(false);
2039  } else {
2040    DefWindowProc();
2041    // Document manager created by RichEdit can be obtained only after
2042    // WM_SETFOCUS event is handled.
2043    tsf_event_router_->SetManager(
2044        ui::TSFBridge::GetInstance()->GetThreadManager());
2045    SetMsgHandled(true);
2046  }
2047}
2048
2049LRESULT OmniboxViewWin::OnSetText(const wchar_t* text) {
2050  // Ignore all IME messages while we process this WM_SETTEXT message.
2051  // When SetWindowText() is called while an IME is composing text, the IME
2052  // calls SendMessage() to send a WM_IME_COMPOSITION message. When we receive
2053  // this WM_IME_COMPOSITION message, we update the omnibox and may call
2054  // SetWindowText() again. To stop this recursive message-handler call, we
2055  // stop updating the omnibox while we process a WM_SETTEXT message.
2056  // We wouldn't need to do this update anyway, because either we're in the
2057  // middle of updating the omnibox already or the caller of SetWindowText()
2058  // is going to update the omnibox next.
2059  base::AutoReset<bool> auto_reset_ignore_ime_messages(
2060      &ignore_ime_messages_, true);
2061  return DefWindowProc(WM_SETTEXT, 0, reinterpret_cast<LPARAM>(text));
2062}
2063
2064void OmniboxViewWin::OnSysChar(TCHAR ch,
2065                               UINT repeat_count,
2066                               UINT flags) {
2067  DCHECK(flags & KF_ALTDOWN);
2068  // Explicitly show the system menu at a good location on [Alt]+[Space].
2069  // Nearly all other [Alt]+<xxx> combos result in beeping rather than doing
2070  // something useful, so discard those. Note that [Ctrl]+[Alt]+<xxx> generates
2071  // WM_CHAR instead of WM_SYSCHAR, so it is not handled here.
2072  if (ch == VK_SPACE) {
2073    ui::ShowSystemMenu(
2074      native_view_host_->GetWidget()->GetTopLevelWidget()->GetNativeWindow());
2075  }
2076}
2077
2078void OmniboxViewWin::OnWindowPosChanging(WINDOWPOS* window_pos) {
2079  if (force_hidden_)
2080    window_pos->flags &= ~SWP_SHOWWINDOW;
2081  SetMsgHandled(true);
2082}
2083
2084BOOL OmniboxViewWin::OnMouseWheel(UINT flags, short delta, CPoint point) {
2085  // Forward the mouse-wheel message to the window under the mouse.
2086  if (!ui::RerouteMouseWheel(m_hWnd, MAKEWPARAM(flags, delta),
2087                             MAKELPARAM(point.x, point.y)))
2088    SetMsgHandled(false);
2089  return 0;
2090}
2091
2092void OmniboxViewWin::HandleKeystroke(UINT message,
2093                                     TCHAR key,
2094                                     UINT repeat_count,
2095                                     UINT flags) {
2096  ScopedFreeze freeze(this, GetTextObjectModel());
2097  OnBeforePossibleChange();
2098
2099  if (key == ui::VKEY_HOME || key == ui::VKEY_END) {
2100    // DefWindowProc() might reset the keyboard layout when it receives a
2101    // keydown event for VKEY_HOME or VKEY_END. When the window was created
2102    // with WS_EX_LAYOUTRTL and the current keyboard layout is not a RTL one,
2103    // if the input text is pure LTR text, the layout changes to the first RTL
2104    // keyboard layout in keyboard layout queue; if the input text is
2105    // bidirectional text, the layout changes to the keyboard layout of the
2106    // first RTL character in input text. When the window was created without
2107    // WS_EX_LAYOUTRTL and the current keyboard layout is not a LTR one, if the
2108    // input text is pure RTL text, the layout changes to English; if the input
2109    // text is bidirectional text, the layout changes to the keyboard layout of
2110    // the first LTR character in input text. Such keyboard layout change
2111    // behavior is surprising and inconsistent with keyboard behavior
2112    // elsewhere, so reset the layout in this case.
2113    HKL layout = GetKeyboardLayout(0);
2114    DefWindowProc(message, key, MAKELPARAM(repeat_count, flags));
2115    ActivateKeyboardLayout(layout, KLF_REORDER);
2116  } else {
2117    DefWindowProc(message, key, MAKELPARAM(repeat_count, flags));
2118  }
2119
2120  // CRichEditCtrl automatically turns on IMF_AUTOKEYBOARD when the user
2121  // inputs an RTL character, making it difficult for the user to control
2122  // what language is set as they type. Force this off to make the edit's
2123  // behavior more stable.
2124  const int lang_options = SendMessage(EM_GETLANGOPTIONS, 0, 0);
2125  if (lang_options & IMF_AUTOKEYBOARD)
2126    SendMessage(EM_SETLANGOPTIONS, 0, lang_options & ~IMF_AUTOKEYBOARD);
2127
2128  OnAfterPossibleChange();
2129}
2130
2131bool OmniboxViewWin::OnKeyDownOnlyWritable(TCHAR key,
2132                                           UINT repeat_count,
2133                                           UINT flags) {
2134  // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than
2135  // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places
2136  // in this function even with a WM_SYSKEYDOWN handler.
2137
2138  // If adding a new key that could possibly be an accelerator then you'll need
2139  // to update LocationBarView::SkipDefaultKeyEventProcessing() as well.
2140  int count = repeat_count;
2141  switch (key) {
2142    case VK_RIGHT:
2143      // TODO(sky): figure out RTL.
2144      if (base::i18n::IsRTL())
2145        return false;
2146      {
2147        CHARRANGE selection;
2148        GetSel(selection);
2149        return (selection.cpMin == selection.cpMax) &&
2150            (selection.cpMin == GetTextLength()) &&
2151            model()->CommitSuggestedText();
2152      }
2153
2154    case VK_RETURN:
2155      model()->AcceptInput((flags & KF_ALTDOWN) ?
2156          NEW_FOREGROUND_TAB : CURRENT_TAB, false);
2157      return true;
2158
2159    case VK_PRIOR:
2160    case VK_NEXT:
2161      count = model()->result().size();
2162      // FALL THROUGH
2163    case VK_UP:
2164    case VK_DOWN:
2165      // Ignore alt + numpad, but treat alt + (non-numpad) like (non-numpad).
2166      if ((flags & KF_ALTDOWN) && !(flags & KF_EXTENDED))
2167        return false;
2168
2169      model()->OnUpOrDownKeyPressed(((key == VK_PRIOR) || (key == VK_UP)) ?
2170          -count : count);
2171      return true;
2172
2173    // Hijacking Editing Commands
2174    //
2175    // We hijack the keyboard short-cuts for Cut, Copy, and Paste here so that
2176    // they go through our clipboard routines.  This allows us to be smarter
2177    // about how we interact with the clipboard and avoid bugs in the
2178    // CRichEditCtrl.  If we didn't hijack here, the edit control would handle
2179    // these internally with sending the WM_CUT, WM_COPY, or WM_PASTE messages.
2180    //
2181    // Cut:   Shift-Delete and Ctrl-x are treated as cut.  Ctrl-Shift-Delete and
2182    //        Ctrl-Shift-x are not treated as cut even though the underlying
2183    //        CRichTextEdit would treat them as such.  Also note that we bring
2184    //        up 'clear browsing data' on control-shift-delete.
2185    // Copy:  Ctrl-Insert and Ctrl-c is treated as copy.  Shift-Ctrl-c is not.
2186    //        (This is handled in OnKeyDownAllModes().)
2187    // Paste: Shift-Insert and Ctrl-v are treated as paste.  Ctrl-Shift-Insert
2188    //        and Ctrl-Shift-v are not.
2189    //
2190    // This behavior matches most, but not all Windows programs, and largely
2191    // conforms to what users expect.
2192
2193    case VK_DELETE:
2194      if (flags & KF_ALTDOWN)
2195        return false;
2196      if (GetKeyState(VK_SHIFT) >= 0) {
2197        if (GetKeyState(VK_CONTROL) >= 0) {
2198          CHARRANGE selection;
2199          GetSel(selection);
2200          delete_at_end_pressed_ = ((selection.cpMin == selection.cpMax) &&
2201                                    (selection.cpMin == GetTextLength()));
2202        }
2203        return false;
2204      }
2205      if (GetKeyState(VK_CONTROL) >= 0) {
2206        // Cut text if possible.
2207        CHARRANGE selection;
2208        GetSel(selection);
2209        if (selection.cpMin != selection.cpMax) {
2210          ScopedFreeze freeze(this, GetTextObjectModel());
2211          OnBeforePossibleChange();
2212          Cut();
2213          OnAfterPossibleChange();
2214        } else {
2215          if (model()->popup_model()->IsOpen()) {
2216            // This is a bit overloaded, but we hijack Shift-Delete in this
2217            // case to delete the current item from the pop-up.  We prefer
2218            // cutting to this when possible since that's the behavior more
2219            // people expect from Shift-Delete, and it's more commonly useful.
2220            model()->popup_model()->TryDeletingCurrentItem();
2221          }
2222        }
2223      }
2224      return true;
2225
2226    case 'X':
2227      if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
2228        return false;
2229      if (GetKeyState(VK_SHIFT) >= 0) {
2230        ScopedFreeze freeze(this, GetTextObjectModel());
2231        OnBeforePossibleChange();
2232        Cut();
2233        OnAfterPossibleChange();
2234      }
2235      return true;
2236
2237    case VK_INSERT:
2238      // Ignore insert by itself, so we don't turn overtype mode on/off.
2239      if (!(flags & KF_ALTDOWN) && (GetKeyState(VK_SHIFT) >= 0) &&
2240          (GetKeyState(VK_CONTROL) >= 0))
2241        return true;
2242      // FALL THROUGH
2243    case 'V':
2244      if ((flags & KF_ALTDOWN) ||
2245          (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0))
2246        return false;
2247      if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) {
2248        ScopedFreeze freeze(this, GetTextObjectModel());
2249        OnBeforePossibleChange();
2250        Paste();
2251        OnAfterPossibleChange();
2252      }
2253      return true;
2254
2255    case VK_BACK: {
2256      if ((flags & KF_ALTDOWN) || model()->is_keyword_hint() ||
2257          model()->keyword().empty())
2258        return false;
2259
2260      {
2261        CHARRANGE selection;
2262        GetSel(selection);
2263        if ((selection.cpMin != selection.cpMax) || (selection.cpMin != 0))
2264          return false;
2265      }
2266
2267      // We're showing a keyword and the user pressed backspace at the beginning
2268      // of the text. Delete the selected keyword.
2269      ScopedFreeze freeze(this, GetTextObjectModel());
2270      model()->ClearKeyword(GetText());
2271      return true;
2272    }
2273
2274    case VK_TAB: {
2275      const bool shift_pressed = GetKeyState(VK_SHIFT) < 0;
2276      if (model()->is_keyword_hint() && !shift_pressed) {
2277        // Accept the keyword.
2278        ScopedFreeze freeze(this, GetTextObjectModel());
2279        model()->AcceptKeyword(ENTERED_KEYWORD_MODE_VIA_TAB);
2280      } else if (shift_pressed &&
2281                 model()->popup_model()->selected_line_state() ==
2282                    OmniboxPopupModel::KEYWORD) {
2283        model()->ClearKeyword(GetText());
2284      } else {
2285        model()->OnUpOrDownKeyPressed(shift_pressed ? -count : count);
2286      }
2287      return true;
2288    }
2289
2290    case 0xbb:  // Ctrl-'='.  Triggers subscripting (even in plain text mode).
2291                // We don't use VK_OEM_PLUS in case the macro isn't defined.
2292                // (e.g., we don't have this symbol in embeded environment).
2293      return true;
2294
2295    default:
2296      return false;
2297  }
2298}
2299
2300bool OmniboxViewWin::OnKeyDownAllModes(TCHAR key,
2301                                       UINT repeat_count,
2302                                       UINT flags) {
2303  // See KF_ALTDOWN comment atop OnKeyDownOnlyWritable().
2304
2305  switch (key) {
2306    case VK_CONTROL:
2307      model()->OnControlKeyChanged(true);
2308      return false;
2309
2310    case VK_INSERT:
2311    case 'C':
2312      // See more detailed comments in OnKeyDownOnlyWritable().
2313      if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0))
2314        return false;
2315      if (GetKeyState(VK_SHIFT) >= 0)
2316        Copy();
2317      return true;
2318
2319    default:
2320      return false;
2321  }
2322}
2323
2324void OmniboxViewWin::GetSelection(CHARRANGE& sel) const {
2325  GetSel(sel);
2326
2327  // See if we need to reverse the direction of the selection.
2328  ITextDocument* const text_object_model = GetTextObjectModel();
2329  if (!text_object_model)
2330    return;
2331  base::win::ScopedComPtr<ITextSelection> selection;
2332  const HRESULT hr = text_object_model->GetSelection(selection.Receive());
2333  DCHECK_EQ(S_OK, hr);
2334  long flags;
2335  selection->GetFlags(&flags);
2336  if (flags & tomSelStartActive)
2337    std::swap(sel.cpMin, sel.cpMax);
2338}
2339
2340string16 OmniboxViewWin::GetSelectedText() const {
2341  CHARRANGE sel;
2342  GetSel(sel);
2343  string16 str;
2344  if (sel.cpMin != sel.cpMax)
2345    GetSelText(WriteInto(&str, sel.cpMax - sel.cpMin + 1));
2346  return str;
2347}
2348
2349void OmniboxViewWin::SetSelection(LONG start, LONG end) {
2350  SetSel(start, end);
2351
2352  if (start <= end)
2353    return;
2354
2355  // We need to reverse the direction of the selection.
2356  ITextDocument* const text_object_model = GetTextObjectModel();
2357  if (!text_object_model)
2358    return;
2359  base::win::ScopedComPtr<ITextSelection> selection;
2360  const HRESULT hr = text_object_model->GetSelection(selection.Receive());
2361  DCHECK_EQ(S_OK, hr);
2362  selection->SetFlags(tomSelStartActive);
2363}
2364
2365void OmniboxViewWin::PlaceCaretAt(string16::size_type pos) {
2366  SetSelection(static_cast<LONG>(pos), static_cast<LONG>(pos));
2367}
2368
2369bool OmniboxViewWin::IsSelectAllForRange(const CHARRANGE& sel) const {
2370  const int text_length = GetTextLength();
2371  return ((sel.cpMin == 0) && (sel.cpMax >= text_length)) ||
2372      ((sel.cpMax == 0) && (sel.cpMin >= text_length));
2373}
2374
2375LONG OmniboxViewWin::ClipXCoordToVisibleText(LONG x,
2376                                             bool is_triple_click) const {
2377  // Clip the X coordinate to the left edge of the text. Careful:
2378  // PosFromChar(0) may return a negative X coordinate if the beginning of the
2379  // text has scrolled off the edit, so don't go past the clip rect's edge.
2380  PARAFORMAT2 pf2;
2381  GetParaFormat(pf2);
2382  // Calculation of the clipped coordinate is more complicated if the paragraph
2383  // layout is RTL layout, or if there is RTL characters inside the LTR layout
2384  // paragraph.
2385  const bool ltr_text_in_ltr_layout = !(pf2.wEffects & PFE_RTLPARA) &&
2386      !base::i18n::StringContainsStrongRTLChars(GetText());
2387  const int length = GetTextLength();
2388  RECT r;
2389  GetRect(&r);
2390  // The values returned by PosFromChar() seem to refer always
2391  // to the left edge of the character's bounding box.
2392  const LONG first_position_x = PosFromChar(0).x;
2393  LONG min_x = first_position_x;
2394  if (!ltr_text_in_ltr_layout) {
2395    for (int i = 1; i < length; ++i)
2396      min_x = std::min(min_x, PosFromChar(i).x);
2397  }
2398  const LONG left_bound = std::max(r.left, min_x);
2399  // PosFromChar(length) is a phantom character past the end of the text. It is
2400  // not necessarily a right bound; in RTL controls it may be a left bound. So
2401  // treat it as a right bound only if it is to the right of the first
2402  // character.
2403  LONG right_bound = r.right;
2404  LONG end_position_x = PosFromChar(length).x;
2405  if (end_position_x >= first_position_x) {
2406    right_bound = std::min(right_bound, end_position_x);  // LTR case.
2407  }
2408  // For trailing characters that are 2 pixels wide or less (like "l" in some
2409  // fonts), we have a problem:
2410  //   * Clicks on any pixel within the character will place the cursor before
2411  //     the character.
2412  //   * Clicks on the pixel just after the character will not allow triple-
2413  //     click to work properly (true for any last character width).
2414  // So, we move to the last pixel of the character when this is a
2415  // triple-click, and moving to one past the last pixel in all other
2416  // scenarios.  This way, all clicks that can move the cursor will place it at
2417  // the end of the text, but triple-click will still work.
2418  if (x < left_bound) {
2419    return (is_triple_click && ltr_text_in_ltr_layout) ? left_bound - 1 :
2420                                                         left_bound;
2421  }
2422  if ((length == 0) || (x < right_bound))
2423    return x;
2424  return is_triple_click ? (right_bound - 1) : right_bound;
2425}
2426
2427int OmniboxViewWin::GetOmniboxTextLength() const {
2428  return static_cast<int>(GetTextLength());
2429}
2430
2431void OmniboxViewWin::EmphasizeURLComponents() {
2432  ITextDocument* const text_object_model = GetTextObjectModel();
2433  ScopedFreeze freeze(this, text_object_model);
2434  ScopedSuspendUndo suspend_undo(text_object_model);
2435
2436  // Save the selection.
2437  CHARRANGE saved_sel;
2438  GetSelection(saved_sel);
2439
2440  // See whether the contents are a URL with a non-empty host portion, which we
2441  // should emphasize.  To check for a URL, rather than using the type returned
2442  // by Parse(), ask the model, which will check the desired page transition for
2443  // this input.  This can tell us whether an UNKNOWN input string is going to
2444  // be treated as a search or a navigation, and is the same method the Paste
2445  // And Go system uses.
2446  url_parse::Component scheme, host;
2447  string16 text(GetText());
2448  AutocompleteInput::ParseForEmphasizeComponents(text, &scheme, &host);
2449
2450  // Set the baseline emphasis.
2451  CHARFORMAT cf = {0};
2452  cf.dwMask = CFM_COLOR;
2453  // If we're going to emphasize parts of the text, then the baseline state
2454  // should be "de-emphasized".  If not, then everything should be rendered in
2455  // the standard text color unless we should grey out the entire URL.
2456  bool grey_out_url = text.substr(scheme.begin, scheme.len) ==
2457      UTF8ToUTF16(extensions::kExtensionScheme);
2458  bool grey_base = model()->CurrentTextIsURL() &&
2459      (host.is_nonempty() || grey_out_url);
2460  cf.crTextColor = skia::SkColorToCOLORREF(location_bar_->GetColor(
2461      security_level_,
2462      grey_base ? LocationBarView::DEEMPHASIZED_TEXT : LocationBarView::TEXT));
2463  // NOTE: Don't use SetDefaultCharFormat() instead of the below; that sets
2464  // the format that will get applied to text added in the future, not to text
2465  // already in the edit.
2466  SelectAll(false);
2467  SetSelectionCharFormat(cf);
2468  if (host.is_nonempty() && !grey_out_url) {
2469    // We've found a host name and we should provide emphasis to host names,
2470    // so emphasize it.
2471    cf.crTextColor = skia::SkColorToCOLORREF(location_bar_->GetColor(
2472        security_level_, LocationBarView::TEXT));
2473    SetSelection(host.begin, host.end());
2474    SetSelectionCharFormat(cf);
2475  }
2476
2477  // Emphasize the scheme for security UI display purposes (if necessary).
2478  insecure_scheme_component_.reset();
2479  if (!model()->user_input_in_progress() && model()->CurrentTextIsURL() &&
2480      scheme.is_nonempty() && (security_level_ != ToolbarModel::NONE)) {
2481    if (security_level_ == ToolbarModel::SECURITY_ERROR) {
2482      insecure_scheme_component_.begin = scheme.begin;
2483      insecure_scheme_component_.len = scheme.len;
2484    }
2485    cf.crTextColor = skia::SkColorToCOLORREF(location_bar_->GetColor(
2486        security_level_, LocationBarView::SECURITY_TEXT));
2487    SetSelection(scheme.begin, scheme.end());
2488    SetSelectionCharFormat(cf);
2489  }
2490
2491  // Restore the selection.
2492  SetSelectionRange(saved_sel);
2493}
2494
2495void OmniboxViewWin::EraseTopOfSelection(CDC* dc,
2496                                         const CRect& client_rect,
2497                                         const CRect& paint_clip_rect) {
2498  // Find the area we care about painting.   We could calculate the rect
2499  // containing just the selected portion, but there's no harm in simply erasing
2500  // the whole top of the client area, and at least once I saw us manage to
2501  // select the "phantom newline" briefly, which looks very weird if not clipped
2502  // off at the same height.
2503  CRect erase_rect(client_rect.left, client_rect.top, client_rect.right,
2504                   client_rect.top + font_y_adjustment_);
2505  erase_rect.IntersectRect(erase_rect, paint_clip_rect);
2506
2507  // Erase to the background color.
2508  if (!erase_rect.IsRectNull())
2509    dc->FillSolidRect(&erase_rect, background_color_);
2510}
2511
2512void OmniboxViewWin::DrawSlashForInsecureScheme(HDC hdc,
2513                                                const CRect& client_rect,
2514                                                const CRect& paint_clip_rect) {
2515  DCHECK(insecure_scheme_component_.is_nonempty());
2516
2517  // Calculate the rect, in window coordinates, containing the portion of the
2518  // scheme where we'll be drawing the slash.  Vertically, we draw across one
2519  // x-height of text, plus an additional 3 stroke diameters (the stroke width
2520  // plus a half-stroke width of space between the stroke and the text, both
2521  // above and below the text).
2522  const int font_top = client_rect.top + font_y_adjustment_;
2523  const SkScalar kStrokeWidthPixels = SkIntToScalar(2);
2524  const int kAdditionalSpaceOutsideFont =
2525      static_cast<int>(ceil(kStrokeWidthPixels * 1.5f));
2526  const int font_ascent = font_.GetBaseline();
2527  const CRect scheme_rect(
2528      PosFromChar(insecure_scheme_component_.begin).x,
2529      font_top + font_ascent - font_x_height_ - kAdditionalSpaceOutsideFont,
2530      PosFromChar(insecure_scheme_component_.end()).x,
2531      font_top + font_ascent + kAdditionalSpaceOutsideFont);
2532
2533  // Clip to the portion we care about and translate to canvas coordinates
2534  // (see the canvas creation below) for use later.
2535  CRect canvas_clip_rect, canvas_paint_clip_rect;
2536  canvas_clip_rect.IntersectRect(scheme_rect, client_rect);
2537  canvas_paint_clip_rect.IntersectRect(canvas_clip_rect, paint_clip_rect);
2538  if (canvas_paint_clip_rect.IsRectNull())
2539    return;  // We don't need to paint any of this region, so just bail early.
2540  canvas_clip_rect.OffsetRect(-scheme_rect.left, -scheme_rect.top);
2541  canvas_paint_clip_rect.OffsetRect(-scheme_rect.left, -scheme_rect.top);
2542
2543  // Create a paint context for drawing the antialiased stroke.
2544  SkPaint paint;
2545  paint.setAntiAlias(true);
2546  paint.setStrokeWidth(kStrokeWidthPixels);
2547  paint.setStrokeCap(SkPaint::kRound_Cap);
2548
2549  // Create a canvas as large as |scheme_rect| to do our drawing, and initialize
2550  // it to fully transparent so any antialiasing will look nice when painted
2551  // atop the edit.
2552  gfx::Canvas canvas(gfx::Size(scheme_rect.Width(), scheme_rect.Height()),
2553                     ui::SCALE_FACTOR_100P, false);
2554  SkCanvas* sk_canvas = canvas.sk_canvas();
2555  sk_canvas->getDevice()->accessBitmap(true).eraseARGB(0, 0, 0, 0);
2556
2557  // Calculate the start and end of the stroke, which are just the lower left
2558  // and upper right corners of the canvas, inset by the radius of the endcap
2559  // so we don't clip the endcap off.
2560  const SkScalar kEndCapRadiusPixels = kStrokeWidthPixels / SkIntToScalar(2);
2561  const SkPoint start_point = {
2562      kEndCapRadiusPixels,
2563      SkIntToScalar(scheme_rect.Height()) - kEndCapRadiusPixels };
2564  const SkPoint end_point = {
2565      SkIntToScalar(scheme_rect.Width()) - kEndCapRadiusPixels,
2566      kEndCapRadiusPixels };
2567
2568  // Calculate the selection rectangle in canvas coordinates, which we'll use
2569  // to clip the stroke so we can draw the unselected and selected portions.
2570  CHARRANGE sel;
2571  GetSel(sel);
2572  const SkRect selection_rect = {
2573      SkIntToScalar(PosFromChar(sel.cpMin).x - scheme_rect.left),
2574      SkIntToScalar(0),
2575      SkIntToScalar(PosFromChar(sel.cpMax).x - scheme_rect.left),
2576      SkIntToScalar(scheme_rect.Height()) };
2577
2578  // Draw the unselected portion of the stroke.
2579  sk_canvas->save();
2580  if (selection_rect.isEmpty() ||
2581      sk_canvas->clipRect(selection_rect, SkRegion::kDifference_Op)) {
2582    paint.setColor(location_bar_->GetColor(security_level_,
2583                                           LocationBarView::SECURITY_TEXT));
2584    sk_canvas->drawLine(start_point.fX, start_point.fY,
2585                        end_point.fX, end_point.fY, paint);
2586  }
2587  sk_canvas->restore();
2588
2589  // Draw the selected portion of the stroke.
2590  if (!selection_rect.isEmpty() && sk_canvas->clipRect(selection_rect)) {
2591    paint.setColor(location_bar_->GetColor(security_level_,
2592                                           LocationBarView::SELECTED_TEXT));
2593    sk_canvas->drawLine(start_point.fX, start_point.fY,
2594                        end_point.fX, end_point.fY, paint);
2595  }
2596
2597  // Now copy what we drew to the target HDC.
2598  skia::DrawToNativeContext(sk_canvas, hdc,
2599      scheme_rect.left + canvas_paint_clip_rect.left - canvas_clip_rect.left,
2600      std::max(scheme_rect.top, client_rect.top) + canvas_paint_clip_rect.top -
2601          canvas_clip_rect.top, &canvas_paint_clip_rect);
2602}
2603
2604void OmniboxViewWin::DrawDropHighlight(HDC hdc,
2605                                       const CRect& client_rect,
2606                                       const CRect& paint_clip_rect) {
2607  DCHECK_NE(-1, drop_highlight_position_);
2608
2609  const int highlight_y = client_rect.top + font_y_adjustment_;
2610  const int highlight_x = PosFromChar(drop_highlight_position_).x - 1;
2611  const CRect highlight_rect(highlight_x,
2612                             highlight_y,
2613                             highlight_x + 1,
2614                             highlight_y + font_.GetHeight());
2615
2616  // Clip the highlight to the region being painted.
2617  CRect clip_rect;
2618  clip_rect.IntersectRect(highlight_rect, paint_clip_rect);
2619  if (clip_rect.IsRectNull())
2620    return;
2621
2622  HGDIOBJ last_pen = SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(0, 0, 0)));
2623  MoveToEx(hdc, clip_rect.left, clip_rect.top, NULL);
2624  LineTo(hdc, clip_rect.left, clip_rect.bottom);
2625  DeleteObject(SelectObject(hdc, last_pen));
2626}
2627
2628void OmniboxViewWin::TextChanged() {
2629  ScopedFreeze freeze(this, GetTextObjectModel());
2630  OmniboxView::TextChanged();
2631}
2632
2633ITextDocument* OmniboxViewWin::GetTextObjectModel() const {
2634  if (!text_object_model_) {
2635    // This is lazily initialized, instead of being initialized in the
2636    // constructor, in order to avoid hurting startup performance.
2637    base::win::ScopedComPtr<IRichEditOle, NULL> ole_interface;
2638    ole_interface.Attach(GetOleInterface());
2639    if (ole_interface) {
2640      ole_interface.QueryInterface(
2641          __uuidof(ITextDocument),
2642          reinterpret_cast<void**>(&text_object_model_));
2643    }
2644  }
2645  return text_object_model_;
2646}
2647
2648void OmniboxViewWin::StartDragIfNecessary(const CPoint& point) {
2649  if (initiated_drag_ || !IsDrag(click_point_[kLeft], point))
2650    return;
2651
2652  ui::OSExchangeData data;
2653
2654  DWORD supported_modes = DROPEFFECT_COPY;
2655
2656  CHARRANGE sel;
2657  GetSelection(sel);
2658
2659  // We're about to start a drag session, but the edit is expecting a mouse up
2660  // that it uses to reset internal state.  If we don't send a mouse up now,
2661  // when the mouse moves back into the edit the edit will reset the selection.
2662  // So, we send the event now which resets the selection.  We then restore the
2663  // selection and start the drag.  We always send lbuttonup as otherwise we
2664  // might trigger a context menu (right up).  This seems scary, but doesn't
2665  // seem to cause problems.
2666  {
2667    ScopedFreeze freeze(this, GetTextObjectModel());
2668    DefWindowProc(WM_LBUTTONUP, 0,
2669                  MAKELPARAM(click_point_[kLeft].x, click_point_[kLeft].y));
2670    SetSelectionRange(sel);
2671  }
2672
2673  const string16 start_text(GetText());
2674  string16 text_to_write(GetSelectedText());
2675  GURL url;
2676  bool write_url;
2677  const bool is_all_selected = IsSelectAllForRange(sel);
2678
2679  // |sel| was set by GetSelection(), which preserves selection direction, so
2680  // sel.cpMin may not be the smaller value.
2681  model()->AdjustTextForCopy(std::min(sel.cpMin, sel.cpMax), is_all_selected,
2682                             &text_to_write, &url, &write_url);
2683
2684  if (write_url) {
2685    string16 title;
2686    gfx::Image favicon;
2687    if (is_all_selected)
2688      model()->GetDataForURLExport(&url, &title, &favicon);
2689    button_drag_utils::SetURLAndDragImage(url, title, favicon.AsImageSkia(),
2690        &data, native_view_host_->GetWidget());
2691    supported_modes |= DROPEFFECT_LINK;
2692    content::RecordAction(UserMetricsAction("Omnibox_DragURL"));
2693  } else {
2694    supported_modes |= DROPEFFECT_MOVE;
2695    content::RecordAction(UserMetricsAction("Omnibox_DragString"));
2696  }
2697
2698  data.SetString(text_to_write);
2699
2700  scoped_refptr<ui::DragSourceWin> drag_source(new ui::DragSourceWin);
2701  DWORD dropped_mode;
2702  base::AutoReset<bool> auto_reset_in_drag(&in_drag_, true);
2703  if (DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data),
2704                 drag_source, supported_modes, &dropped_mode) ==
2705          DRAGDROP_S_DROP) {
2706    if ((dropped_mode == DROPEFFECT_MOVE) && (start_text == GetText())) {
2707      ScopedFreeze freeze(this, GetTextObjectModel());
2708      OnBeforePossibleChange();
2709      SetSelectionRange(sel);
2710      ReplaceSel(L"", true);
2711      OnAfterPossibleChange();
2712    }
2713    // else case, not a move or it was a move and the drop was on us.
2714    // If the drop was on us, EditDropTarget took care of the move so that
2715    // we don't have to delete the text.
2716    possible_drag_ = false;
2717  } else {
2718    // Drag was canceled or failed. The mouse may still be down and
2719    // over us, in which case we need possible_drag_ to remain true so
2720    // that we don't forward mouse move events to the edit which will
2721    // start another drag.
2722    //
2723    // NOTE: we didn't use mouse capture during the mouse down as DoDragDrop
2724    // does its own capture.
2725    CPoint cursor_location;
2726    GetCursorPos(&cursor_location);
2727
2728    CRect client_rect;
2729    GetClientRect(&client_rect);
2730
2731    CPoint client_origin_on_screen(client_rect.left, client_rect.top);
2732    ClientToScreen(&client_origin_on_screen);
2733    client_rect.MoveToXY(client_origin_on_screen.x,
2734                         client_origin_on_screen.y);
2735    possible_drag_ = (client_rect.PtInRect(cursor_location) &&
2736                      ((GetKeyState(VK_LBUTTON) != 0) ||
2737                       (GetKeyState(VK_MBUTTON) != 0) ||
2738                       (GetKeyState(VK_RBUTTON) != 0)));
2739  }
2740
2741  initiated_drag_ = true;
2742  tracking_click_[kLeft] = false;
2743}
2744
2745void OmniboxViewWin::OnPossibleDrag(const CPoint& point) {
2746  if (possible_drag_)
2747    return;
2748
2749  click_point_[kLeft] = point;
2750  initiated_drag_ = false;
2751
2752  CHARRANGE selection;
2753  GetSel(selection);
2754  if (selection.cpMin != selection.cpMax) {
2755    const POINT min_sel_location(PosFromChar(selection.cpMin));
2756    const POINT max_sel_location(PosFromChar(selection.cpMax));
2757    // NOTE: we don't consider the y location here as we always pass a
2758    // y-coordinate in the middle to the default handler which always triggers
2759    // a drag regardless of the y-coordinate.
2760    possible_drag_ = (point.x >= min_sel_location.x) &&
2761                     (point.x < max_sel_location.x);
2762  }
2763}
2764
2765void OmniboxViewWin::RepaintDropHighlight(int position) {
2766  if ((position != -1) && (position <= GetTextLength())) {
2767    const POINT min_loc(PosFromChar(position));
2768    const RECT highlight_bounds = {min_loc.x - 1, font_y_adjustment_,
2769        min_loc.x + 2, font_.GetHeight() + font_y_adjustment_};
2770    InvalidateRect(&highlight_bounds, false);
2771  }
2772}
2773
2774void OmniboxViewWin::BuildContextMenu() {
2775  if (context_menu_contents_.get())
2776    return;
2777
2778  context_menu_contents_.reset(new ui::SimpleMenuModel(this));
2779  // Set up context menu.
2780  if (popup_window_mode_) {
2781    context_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY);
2782  } else {
2783    context_menu_contents_->AddItemWithStringId(IDS_UNDO, IDS_UNDO);
2784    context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
2785    context_menu_contents_->AddItemWithStringId(IDC_CUT, IDS_CUT);
2786    context_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY);
2787    if (chrome::IsQueryExtractionEnabled(model()->profile()))
2788      context_menu_contents_->AddItemWithStringId(IDC_COPY_URL, IDS_COPY_URL);
2789    context_menu_contents_->AddItemWithStringId(IDC_PASTE, IDS_PASTE);
2790    // GetContextualLabel() will override this next label with the
2791    // IDS_PASTE_AND_SEARCH label as needed.
2792    context_menu_contents_->AddItemWithStringId(IDS_PASTE_AND_GO,
2793                                                IDS_PASTE_AND_GO);
2794    context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
2795    context_menu_contents_->AddItemWithStringId(IDS_SELECT_ALL, IDS_SELECT_ALL);
2796    context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
2797    context_menu_contents_->AddItemWithStringId(IDC_EDIT_SEARCH_ENGINES,
2798                                                IDS_EDIT_SEARCH_ENGINES);
2799  }
2800}
2801
2802void OmniboxViewWin::SelectAllIfNecessary(MouseButton button,
2803                                          const CPoint& point) {
2804  // When the user has clicked and released to give us focus, select all.
2805  if (tracking_click_[button] &&
2806      !IsDrag(click_point_[button], point)) {
2807    // Select all in the reverse direction so as not to scroll the caret
2808    // into view and shift the contents jarringly.
2809    SelectAll(true);
2810    possible_drag_ = false;
2811  }
2812}
2813
2814void OmniboxViewWin::TrackMousePosition(MouseButton button,
2815                                        const CPoint& point) {
2816  if (gaining_focus_.get()) {
2817    // This click is giving us focus, so we need to track how much the mouse
2818    // moves to see if it's a drag or just a click. Clicks should select all
2819    // the text.
2820    tracking_click_[button] = true;
2821    click_point_[button] = point;
2822  }
2823}
2824
2825int OmniboxViewWin::GetHorizontalMargin() const {
2826  RECT rect;
2827  GetRect(&rect);
2828  RECT client_rect;
2829  GetClientRect(&client_rect);
2830  return (rect.left - client_rect.left) + (client_rect.right - rect.right);
2831}
2832
2833int OmniboxViewWin::WidthNeededToDisplay(const string16& text) const {
2834  // Use font_.GetStringWidth() instead of PosFromChar(GetTextLength()) because
2835  // PosFromChar() is apparently buggy. In both LTR UI and RTL UI with
2836  // left-to-right layout, PosFromChar(i) might return 0 when i is greater than
2837  // 1.
2838  return font_.GetStringWidth(text) + GetHorizontalMargin();
2839}
2840