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