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