[email protected] | b1ce3069 | 2012-05-15 18:26:35 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 067095a | 2011-05-24 23:43:44 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // This file defines helper functions shared by the various implementations |
| 6 | // of OmniboxView. |
| 7 | |
blundell | 7dbd379 | 2015-08-05 15:14:19 | [diff] [blame] | 8 | #include "components/omnibox/browser/omnibox_view.h" |
[email protected] | 067095a | 2011-05-24 23:43:44 | [diff] [blame] | 9 | |
dcheng | 51ace48a | 2015-12-26 22:45:17 | [diff] [blame] | 10 | #include <utility> |
| 11 | |
[email protected] | 1152118 | 2013-06-11 04:06:36 | [diff] [blame] | 12 | #include "base/strings/string16.h" |
| 13 | #include "base/strings/string_util.h" |
[email protected] | 5846d58 | 2013-06-08 16:02:12 | [diff] [blame] | 14 | #include "base/strings/utf_string_conversions.h" |
avi | f57136c1 | 2015-12-25 23:27:45 | [diff] [blame] | 15 | #include "build/build_config.h" |
blundell | 2102f7c | 2015-07-09 10:00:53 | [diff] [blame] | 16 | #include "components/omnibox/browser/autocomplete_match.h" |
blundell | 26a618e | 2015-07-31 11:16:45 | [diff] [blame] | 17 | #include "components/omnibox/browser/omnibox_client.h" |
blundell | 3f0c8bb4 | 2015-08-03 18:55:05 | [diff] [blame] | 18 | #include "components/omnibox/browser/omnibox_edit_controller.h" |
thomasanderson | 00687d0 | 2016-06-08 16:06:34 | [diff] [blame] | 19 | #include "components/omnibox/browser/omnibox_edit_model.h" |
blundell | 11d93bc | 2015-07-31 12:33:12 | [diff] [blame] | 20 | #include "components/toolbar/toolbar_model.h" |
hashimoto | cfb460f | 2014-09-25 06:59:00 | [diff] [blame] | 21 | #include "grit/components_scaled_resources.h" |
[email protected] | cb39d67 | 2014-02-06 02:22:11 | [diff] [blame] | 22 | #include "ui/base/l10n/l10n_util.h" |
estade | 3c7c6b1 | 2015-11-10 01:25:37 | [diff] [blame] | 23 | #include "ui/gfx/vector_icons_public.h" |
[email protected] | 067095a | 2011-05-24 23:43:44 | [diff] [blame] | 24 | |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 25 | // static |
[email protected] | 6a72a63 | 2013-12-12 22:22:00 | [diff] [blame] | 26 | base::string16 OmniboxView::StripJavascriptSchemas(const base::string16& text) { |
[email protected] | d9dfe40 | 2013-12-24 23:30:21 | [diff] [blame] | 27 | const base::string16 kJsPrefix( |
[email protected] | cca6f39 | 2014-05-28 21:32:26 | [diff] [blame] | 28 | base::ASCIIToUTF16(url::kJavaScriptScheme) + base::ASCIIToUTF16(":")); |
[email protected] | e9273e19 | 2013-12-11 17:51:49 | [diff] [blame] | 29 | base::string16 out(text); |
brettw | 66d1b81b | 2015-07-06 19:29:40 | [diff] [blame] | 30 | while (base::StartsWith(out, kJsPrefix, |
| 31 | base::CompareCase::INSENSITIVE_ASCII)) { |
[email protected] | 8af69c6c | 2014-03-03 19:05:31 | [diff] [blame] | 32 | base::TrimWhitespace(out.substr(kJsPrefix.length()), base::TRIM_LEADING, |
| 33 | &out); |
| 34 | } |
[email protected] | 067095a | 2011-05-24 23:43:44 | [diff] [blame] | 35 | return out; |
| 36 | } |
| 37 | |
[email protected] | b1ce3069 | 2012-05-15 18:26:35 | [diff] [blame] | 38 | // static |
[email protected] | 6a72a63 | 2013-12-12 22:22:00 | [diff] [blame] | 39 | base::string16 OmniboxView::SanitizeTextForPaste(const base::string16& text) { |
[email protected] | 6cf51b6 | 2013-08-10 13:49:22 | [diff] [blame] | 40 | // Check for non-newline whitespace; if found, collapse whitespace runs down |
| 41 | // to single spaces. |
| 42 | // TODO(shess): It may also make sense to ignore leading or |
| 43 | // trailing whitespace when making this determination. |
| 44 | for (size_t i = 0; i < text.size(); ++i) { |
brettw | b341306 | 2015-06-24 00:39:02 | [diff] [blame] | 45 | if (base::IsUnicodeWhitespace(text[i]) && |
| 46 | text[i] != '\n' && text[i] != '\r') { |
[email protected] | 1e1229a1 | 2014-03-11 23:16:24 | [diff] [blame] | 47 | const base::string16 collapsed = base::CollapseWhitespace(text, false); |
[email protected] | 6cf51b6 | 2013-08-10 13:49:22 | [diff] [blame] | 48 | // If the user is pasting all-whitespace, paste a single space |
| 49 | // rather than nothing, since pasting nothing feels broken. |
| 50 | return collapsed.empty() ? |
[email protected] | d9dfe40 | 2013-12-24 23:30:21 | [diff] [blame] | 51 | base::ASCIIToUTF16(" ") : StripJavascriptSchemas(collapsed); |
[email protected] | 6cf51b6 | 2013-08-10 13:49:22 | [diff] [blame] | 52 | } |
| 53 | } |
| 54 | |
| 55 | // Otherwise, all whitespace is newlines; remove it entirely. |
[email protected] | 1e1229a1 | 2014-03-11 23:16:24 | [diff] [blame] | 56 | return StripJavascriptSchemas(base::CollapseWhitespace(text, true)); |
[email protected] | 6cf51b6 | 2013-08-10 13:49:22 | [diff] [blame] | 57 | } |
| 58 | |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 59 | OmniboxView::~OmniboxView() { |
| 60 | } |
| 61 | |
| 62 | void OmniboxView::OpenMatch(const AutocompleteMatch& match, |
| 63 | WindowOpenDisposition disposition, |
| 64 | const GURL& alternate_nav_url, |
[email protected] | 60b55e9 | 2014-02-12 03:14:18 | [diff] [blame] | 65 | const base::string16& pasted_text, |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 66 | size_t selected_line) { |
| 67 | // Invalid URLs such as chrome://history can end up here. |
[email protected] | 94b8a51a | 2014-03-26 20:57:55 | [diff] [blame] | 68 | if (!match.destination_url.is_valid() || !model_) |
| 69 | return; |
pkasting | da6dd84 | 2016-02-24 15:26:59 | [diff] [blame] | 70 | const AutocompleteMatch::Type match_type = match.type; |
[email protected] | 94b8a51a | 2014-03-26 20:57:55 | [diff] [blame] | 71 | model_->OpenMatch( |
| 72 | match, disposition, alternate_nav_url, pasted_text, selected_line); |
pkasting | da6dd84 | 2016-02-24 15:26:59 | [diff] [blame] | 73 | // WARNING: |match| may refer to a deleted object at this point! |
| 74 | OnMatchOpened(match_type); |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 75 | } |
| 76 | |
| 77 | bool OmniboxView::IsEditingOrEmpty() const { |
| 78 | return (model_.get() && model_->user_input_in_progress()) || |
| 79 | (GetOmniboxTextLength() == 0); |
| 80 | } |
| 81 | |
palmer | c354e28 | 2016-05-24 00:55:57 | [diff] [blame] | 82 | gfx::VectorIconId OmniboxView::GetVectorIcon() const { |
palmer | c354e28 | 2016-05-24 00:55:57 | [diff] [blame] | 83 | if (!IsEditingOrEmpty()) |
| 84 | return controller_->GetToolbarModel()->GetVectorIcon(); |
| 85 | |
palmer | 79f10e4 | 2016-07-22 03:45:57 | [diff] [blame] | 86 | return AutocompleteMatch::TypeToVectorIcon( |
estade | 3c7c6b1 | 2015-11-10 01:25:37 | [diff] [blame] | 87 | model_ ? model_->CurrentTextType() |
| 88 | : AutocompleteMatchType::URL_WHAT_YOU_TYPED); |
estade | 3c7c6b1 | 2015-11-10 01:25:37 | [diff] [blame] | 89 | } |
| 90 | |
[email protected] | e9273e19 | 2013-12-11 17:51:49 | [diff] [blame] | 91 | void OmniboxView::SetUserText(const base::string16& text) { |
thomasanderson | 2f005e5 | 2016-05-18 21:28:28 | [diff] [blame] | 92 | SetUserText(text, true); |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 93 | } |
| 94 | |
[email protected] | e9273e19 | 2013-12-11 17:51:49 | [diff] [blame] | 95 | void OmniboxView::SetUserText(const base::string16& text, |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 96 | bool update_popup) { |
| 97 | if (model_.get()) |
| 98 | model_->SetUserText(text); |
thomasanderson | 2f005e5 | 2016-05-18 21:28:28 | [diff] [blame] | 99 | SetWindowTextAndCaretPos(text, text.length(), update_popup, true); |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 100 | } |
| 101 | |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 102 | void OmniboxView::RevertAll() { |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 103 | CloseOmniboxPopup(); |
| 104 | if (model_.get()) |
| 105 | model_->Revert(); |
| 106 | TextChanged(); |
| 107 | } |
| 108 | |
| 109 | void OmniboxView::CloseOmniboxPopup() { |
| 110 | if (model_.get()) |
| 111 | model_->StopAutocomplete(); |
| 112 | } |
| 113 | |
[email protected] | 1317783 | 2013-05-31 07:46:05 | [diff] [blame] | 114 | bool OmniboxView::IsImeShowingPopup() const { |
[email protected] | d4606b5d | 2013-06-20 22:38:59 | [diff] [blame] | 115 | // Default to claiming that the IME is not showing a popup, since hiding the |
| 116 | // omnibox dropdown is a bad user experience when we don't know for sure that |
| 117 | // we have to. |
| 118 | return false; |
[email protected] | 1317783 | 2013-05-31 07:46:05 | [diff] [blame] | 119 | } |
| 120 | |
[email protected] | 183e28d | 2014-01-20 18:18:02 | [diff] [blame] | 121 | void OmniboxView::ShowImeIfNeeded() { |
| 122 | } |
| 123 | |
[email protected] | 40a0115 | 2013-06-20 03:52:00 | [diff] [blame] | 124 | bool OmniboxView::IsIndicatingQueryRefinement() const { |
| 125 | // The default implementation always returns false. Mobile ports can override |
| 126 | // this method and implement as needed. |
| 127 | return false; |
| 128 | } |
| 129 | |
pkasting | da6dd84 | 2016-02-24 15:26:59 | [diff] [blame] | 130 | void OmniboxView::OnMatchOpened(AutocompleteMatch::Type match_type) { |
[email protected] | 5655ea3 | 2014-06-21 05:28:08 | [diff] [blame] | 131 | } |
[email protected] | 94b8a51a | 2014-03-26 20:57:55 | [diff] [blame] | 132 | |
thomasanderson | 00687d0 | 2016-06-08 16:06:34 | [diff] [blame] | 133 | void OmniboxView::GetState(State* state) { |
| 134 | state->text = GetText(); |
| 135 | state->keyword = model()->keyword(); |
| 136 | state->is_keyword_selected = model()->is_keyword_selected(); |
| 137 | GetSelectionBounds(&state->sel_start, &state->sel_end); |
| 138 | } |
| 139 | |
| 140 | OmniboxView::StateChanges OmniboxView::GetStateChanges(const State& before, |
| 141 | const State& after) { |
| 142 | OmniboxView::StateChanges state_changes; |
| 143 | state_changes.old_text = &before.text; |
| 144 | state_changes.new_text = &after.text; |
| 145 | state_changes.new_sel_start = after.sel_start; |
| 146 | state_changes.new_sel_end = after.sel_end; |
| 147 | const bool old_sel_empty = before.sel_start == before.sel_end; |
| 148 | const bool new_sel_empty = after.sel_start == after.sel_end; |
| 149 | const bool sel_same_ignoring_direction = |
| 150 | std::min(before.sel_start, before.sel_end) == |
| 151 | std::min(after.sel_start, after.sel_end) && |
| 152 | std::max(before.sel_start, before.sel_end) == |
| 153 | std::max(after.sel_start, after.sel_end); |
| 154 | state_changes.selection_differs = |
| 155 | (!old_sel_empty || !new_sel_empty) && !sel_same_ignoring_direction; |
| 156 | state_changes.text_differs = before.text != after.text; |
| 157 | state_changes.keyword_differs = |
| 158 | (after.is_keyword_selected != before.is_keyword_selected) || |
| 159 | (after.is_keyword_selected && before.is_keyword_selected && |
| 160 | after.keyword != before.keyword); |
| 161 | |
| 162 | // When the user has deleted text, we don't allow inline autocomplete. Make |
| 163 | // sure to not flag cases like selecting part of the text and then pasting |
| 164 | // (or typing) the prefix of that selection. (We detect these by making |
| 165 | // sure the caret, which should be after any insertion, hasn't moved |
| 166 | // forward of the old selection start.) |
| 167 | state_changes.just_deleted_text = |
| 168 | (before.text.length() > after.text.length()) && |
| 169 | (after.sel_start <= std::min(before.sel_start, before.sel_end)); |
| 170 | |
| 171 | return state_changes; |
| 172 | } |
| 173 | |
blundell | d28fa85 | 2015-08-04 16:19:54 | [diff] [blame] | 174 | OmniboxView::OmniboxView(OmniboxEditController* controller, |
dcheng | 259570c | 2016-04-22 00:45:57 | [diff] [blame] | 175 | std::unique_ptr<OmniboxClient> client) |
blundell | 18b35fd | 2015-07-28 13:16:06 | [diff] [blame] | 176 | : controller_(controller) { |
blundell | d28fa85 | 2015-08-04 16:19:54 | [diff] [blame] | 177 | // |client| can be null in tests. |
| 178 | if (client) { |
dcheng | 51ace48a | 2015-12-26 22:45:17 | [diff] [blame] | 179 | model_.reset(new OmniboxEditModel(this, controller, std::move(client))); |
blundell | d28fa85 | 2015-08-04 16:19:54 | [diff] [blame] | 180 | } |
[email protected] | c51eed7 | 2012-08-07 22:01:55 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | void OmniboxView::TextChanged() { |
| 184 | EmphasizeURLComponents(); |
| 185 | if (model_.get()) |
| 186 | model_->OnChanged(); |
| 187 | } |