OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" | 5 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/auto_reset.h" | 10 #include "base/auto_reset.h" |
(...skipping 445 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
456 base::string16* text, | 456 base::string16* text, |
457 GURL* url, | 457 GURL* url, |
458 bool* write_url) { | 458 bool* write_url) { |
459 *write_url = false; | 459 *write_url = false; |
460 | 460 |
461 // Do not adjust if selection did not start at the beginning of the field, or | 461 // Do not adjust if selection did not start at the beginning of the field, or |
462 // if the URL was omitted. | 462 // if the URL was omitted. |
463 if ((sel_min != 0) || controller_->GetToolbarModel()->WouldReplaceURL()) | 463 if ((sel_min != 0) || controller_->GetToolbarModel()->WouldReplaceURL()) |
464 return; | 464 return; |
465 | 465 |
466 if (!user_input_in_progress_ && is_all_selected) { | 466 // Check whether the user is trying to copy the current page's URL by |
467 // The user selected all the text and has not edited it. Use the url as the | 467 // selecting the whole thing without editing it. |
468 // text so that if the scheme was stripped it's added back, and the url | 468 // |
469 // is unescaped (we escape parts of the url for display). | 469 // This is complicated by ZeroSuggest. When ZeroSuggest is active, the user |
| 470 // may be selecting different items and thus changing the address bar text, |
| 471 // even though !user_input_in_progress_; and the permanent URL may change |
| 472 // without updating the visible text, just like when user input is in |
| 473 // progress. In these cases, we don't want to copy the underlying URL, we |
| 474 // want to copy what the user actually sees. However, if we simply never do |
| 475 // this block when !popup_model()->IsOpen(), then just clicking into the |
| 476 // address bar and trying to copy will always bypass this block on pages that |
| 477 // trigger ZeroSuggest, which is too conservative. Instead, in the |
| 478 // ZeroSuggest case, we check that (a) the user hasn't selected one of the |
| 479 // other suggestions, and (b) the selected text is still the same as the |
| 480 // permanent text. ((b) probably implies (a), but it doesn't hurt to be |
| 481 // sure.) If these hold, then it's safe to copy the underlying URL. |
| 482 if (!user_input_in_progress_ && is_all_selected && |
| 483 (!popup_model() || !popup_model()->IsOpen() || |
| 484 ((popup_model()->selected_line() == 0) && (*text == permanent_text_)))) { |
| 485 // It's safe to copy the underlying URL. These lines ensure that if the |
| 486 // scheme was stripped it's added back, and the URL is unescaped (we escape |
| 487 // parts of it for display). |
470 *url = PermanentURL(); | 488 *url = PermanentURL(); |
471 *text = base::UTF8ToUTF16(url->spec()); | 489 *text = base::UTF8ToUTF16(url->spec()); |
472 *write_url = true; | 490 *write_url = true; |
473 return; | 491 return; |
474 } | 492 } |
475 | 493 |
476 // We can't use CurrentTextIsURL() or GetDataForURLExport() because right now | 494 // We can't use CurrentTextIsURL() or GetDataForURLExport() because right now |
477 // the user is probably holding down control to cause the copy, which will | 495 // the user is probably holding down control to cause the copy, which will |
478 // screw up our calculation of the desired_tld. | 496 // screw up our calculation of the desired_tld. |
479 AutocompleteMatch match; | 497 AutocompleteMatch match; |
(...skipping 498 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
978 delegate_->OnInputStateChanged(); | 996 delegate_->OnInputStateChanged(); |
979 } | 997 } |
980 | 998 |
981 void OmniboxEditModel::OnKillFocus() { | 999 void OmniboxEditModel::OnKillFocus() { |
982 SetFocusState(OMNIBOX_FOCUS_NONE, OMNIBOX_FOCUS_CHANGE_EXPLICIT); | 1000 SetFocusState(OMNIBOX_FOCUS_NONE, OMNIBOX_FOCUS_CHANGE_EXPLICIT); |
983 focus_source_ = INVALID; | 1001 focus_source_ = INVALID; |
984 control_key_state_ = UP; | 1002 control_key_state_ = UP; |
985 paste_state_ = NONE; | 1003 paste_state_ = NONE; |
986 } | 1004 } |
987 | 1005 |
| 1006 bool OmniboxEditModel::WillHandleEscapeKey() const { |
| 1007 return user_input_in_progress_ || |
| 1008 (has_temporary_text_ && |
| 1009 (CurrentMatch(NULL).destination_url != original_url_)); |
| 1010 } |
| 1011 |
988 bool OmniboxEditModel::OnEscapeKeyPressed() { | 1012 bool OmniboxEditModel::OnEscapeKeyPressed() { |
989 const AutocompleteMatch& match = CurrentMatch(NULL); | 1013 if (has_temporary_text_ && |
990 if (has_temporary_text_) { | 1014 (CurrentMatch(NULL).destination_url != original_url_)) { |
991 if (match.destination_url != original_url_) { | 1015 RevertTemporaryText(true); |
992 RevertTemporaryText(true); | 1016 return true; |
993 return true; | |
994 } | |
995 } | 1017 } |
996 | 1018 |
997 // We do not clear the pending entry from the omnibox when a load is first | 1019 // We do not clear the pending entry from the omnibox when a load is first |
998 // stopped. If the user presses Escape while stopped, we clear it. | 1020 // stopped. If the user presses Escape while stopped, whether editing or not, |
| 1021 // we clear it. |
999 if (delegate_->CurrentPageExists() && !delegate_->IsLoading()) { | 1022 if (delegate_->CurrentPageExists() && !delegate_->IsLoading()) { |
1000 delegate_->GetNavigationController().DiscardNonCommittedEntries(); | 1023 delegate_->GetNavigationController().DiscardNonCommittedEntries(); |
1001 view_->Update(); | 1024 view_->Update(); |
1002 } | 1025 } |
1003 | 1026 |
1004 // If the user wasn't editing, but merely had focus in the edit, allow <esc> | |
1005 // to be processed as an accelerator, so it can still be used to stop a load. | |
1006 // When the permanent text isn't all selected we still fall through to the | |
1007 // SelectAll() call below so users can arrow around in the text and then hit | |
1008 // <esc> to quickly replace all the text; this matches IE. | |
1009 const bool has_zero_suggest_match = match.provider && | |
1010 (match.provider->type() == AutocompleteProvider::TYPE_ZERO_SUGGEST); | |
1011 if (!has_zero_suggest_match && !user_input_in_progress_ && | |
1012 view_->IsSelectAll()) | |
1013 return false; | |
1014 | |
1015 if (!user_text_.empty()) { | 1027 if (!user_text_.empty()) { |
1016 UMA_HISTOGRAM_ENUMERATION(kOmniboxUserTextClearedHistogram, | 1028 UMA_HISTOGRAM_ENUMERATION(kOmniboxUserTextClearedHistogram, |
1017 OMNIBOX_USER_TEXT_CLEARED_WITH_ESCAPE, | 1029 OMNIBOX_USER_TEXT_CLEARED_WITH_ESCAPE, |
1018 OMNIBOX_USER_TEXT_CLEARED_NUM_OF_ITEMS); | 1030 OMNIBOX_USER_TEXT_CLEARED_NUM_OF_ITEMS); |
1019 } | 1031 } |
1020 | 1032 |
| 1033 // Unconditionally revert/select all. This ensures any popup, whether due to |
| 1034 // normal editing or ZeroSuggest, is closed, and the full text is selected. |
| 1035 // This in turn allows the user to use escape to quickly select all the text |
| 1036 // for ease of replacement, and matches other browsers. |
| 1037 bool user_input_was_in_progress = user_input_in_progress_; |
1021 view_->RevertAll(); | 1038 view_->RevertAll(); |
1022 view_->SelectAll(true); | 1039 view_->SelectAll(true); |
1023 return true; | 1040 |
| 1041 // If the user was in the midst of editing, don't cancel any underlying page |
| 1042 // load. This doesn't match IE or Firefox, but seems more correct. Note that |
| 1043 // we do allow the page load to be stopped in the case where ZeroSuggest was |
| 1044 // visible; this is so that it's still possible to focus the address bar and |
| 1045 // hit escape once to stop a load even if the address being loaded triggers |
| 1046 // the ZeroSuggest popup. |
| 1047 return user_input_was_in_progress; |
1024 } | 1048 } |
1025 | 1049 |
1026 void OmniboxEditModel::OnControlKeyChanged(bool pressed) { | 1050 void OmniboxEditModel::OnControlKeyChanged(bool pressed) { |
1027 if (pressed == (control_key_state_ == UP)) | 1051 if (pressed == (control_key_state_ == UP)) |
1028 control_key_state_ = pressed ? DOWN_WITHOUT_CHANGE : UP; | 1052 control_key_state_ = pressed ? DOWN_WITHOUT_CHANGE : UP; |
1029 } | 1053 } |
1030 | 1054 |
1031 void OmniboxEditModel::OnPaste() { | 1055 void OmniboxEditModel::OnPaste() { |
1032 UMA_HISTOGRAM_COUNTS("Omnibox.Paste", 1); | 1056 UMA_HISTOGRAM_COUNTS("Omnibox.Paste", 1); |
1033 paste_state_ = PASTING; | 1057 paste_state_ = PASTING; |
(...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1469 // Update state and notify view if the omnibox has focus and the caret | 1493 // Update state and notify view if the omnibox has focus and the caret |
1470 // visibility changed. | 1494 // visibility changed. |
1471 const bool was_caret_visible = is_caret_visible(); | 1495 const bool was_caret_visible = is_caret_visible(); |
1472 focus_state_ = state; | 1496 focus_state_ = state; |
1473 if (focus_state_ != OMNIBOX_FOCUS_NONE && | 1497 if (focus_state_ != OMNIBOX_FOCUS_NONE && |
1474 is_caret_visible() != was_caret_visible) | 1498 is_caret_visible() != was_caret_visible) |
1475 view_->ApplyCaretVisibility(); | 1499 view_->ApplyCaretVisibility(); |
1476 | 1500 |
1477 delegate_->OnFocusChanged(focus_state_, reason); | 1501 delegate_->OnFocusChanged(focus_state_, reason); |
1478 } | 1502 } |
OLD | NEW |