1 // Copyright (c) 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 // This file defines helper functions shared by the various implementations 6 // of OmniboxView. 7 8 #include "chrome/browser/ui/omnibox/omnibox_view.h" 9 10 #include "base/strings/string16.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/autocomplete/autocomplete_match.h" 14 #include "chrome/browser/search/search.h" 15 #include "chrome/browser/search_engines/template_url.h" 16 #include "chrome/browser/search_engines/template_url_service.h" 17 #include "chrome/browser/search_engines/template_url_service_factory.h" 18 #include "chrome/browser/ui/omnibox/omnibox_edit_controller.h" 19 #include "chrome/browser/ui/toolbar/toolbar_model.h" 20 #include "grit/generated_resources.h" 21 #include "ui/base/clipboard/clipboard.h" 22 #include "ui/base/l10n/l10n_util.h" 23 24 // static 25 base::string16 OmniboxView::StripJavascriptSchemas(const base::string16& text) { 26 const base::string16 kJsPrefix( 27 base::ASCIIToUTF16(url::kJavaScriptScheme) + base::ASCIIToUTF16(":")); 28 base::string16 out(text); 29 while (StartsWith(out, kJsPrefix, false)) { 30 base::TrimWhitespace(out.substr(kJsPrefix.length()), base::TRIM_LEADING, 31 &out); 32 } 33 return out; 34 } 35 36 // static 37 base::string16 OmniboxView::SanitizeTextForPaste(const base::string16& text) { 38 // Check for non-newline whitespace; if found, collapse whitespace runs down 39 // to single spaces. 40 // TODO(shess): It may also make sense to ignore leading or 41 // trailing whitespace when making this determination. 42 for (size_t i = 0; i < text.size(); ++i) { 43 if (IsWhitespace(text[i]) && text[i] != '\n' && text[i] != '\r') { 44 const base::string16 collapsed = base::CollapseWhitespace(text, false); 45 // If the user is pasting all-whitespace, paste a single space 46 // rather than nothing, since pasting nothing feels broken. 47 return collapsed.empty() ? 48 base::ASCIIToUTF16(" ") : StripJavascriptSchemas(collapsed); 49 } 50 } 51 52 // Otherwise, all whitespace is newlines; remove it entirely. 53 return StripJavascriptSchemas(base::CollapseWhitespace(text, true)); 54 } 55 56 // static 57 base::string16 OmniboxView::GetClipboardText() { 58 // Try text format. 59 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 60 if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(), 61 ui::CLIPBOARD_TYPE_COPY_PASTE)) { 62 base::string16 text; 63 clipboard->ReadText(ui::CLIPBOARD_TYPE_COPY_PASTE, &text); 64 return SanitizeTextForPaste(text); 65 } 66 67 // Try bookmark format. 68 // 69 // It is tempting to try bookmark format first, but the URL we get out of a 70 // bookmark has been cannonicalized via GURL. This means if a user copies 71 // and pastes from the URL bar to itself, the text will get fixed up and 72 // cannonicalized, which is not what the user expects. By pasting in this 73 // order, we are sure to paste what the user copied. 74 if (clipboard->IsFormatAvailable(ui::Clipboard::GetUrlWFormatType(), 75 ui::CLIPBOARD_TYPE_COPY_PASTE)) { 76 std::string url_str; 77 clipboard->ReadBookmark(NULL, &url_str); 78 // pass resulting url string through GURL to normalize 79 GURL url(url_str); 80 if (url.is_valid()) 81 return StripJavascriptSchemas(base::UTF8ToUTF16(url.spec())); 82 } 83 84 return base::string16(); 85 } 86 87 OmniboxView::~OmniboxView() { 88 } 89 90 void OmniboxView::HandleOriginChipMouseRelease() { 91 // Only hide if there isn't any current text in the Omnibox (e.g. search 92 // terms). 93 if (controller()->GetToolbarModel()->GetText().empty()) 94 controller()->HideOriginChip(); 95 } 96 97 void OmniboxView::OnDidKillFocus() { 98 if (chrome::ShouldDisplayOriginChip() && !model()->user_input_in_progress()) 99 controller()->ShowOriginChip(); 100 } 101 102 void OmniboxView::OpenMatch(const AutocompleteMatch& match, 103 WindowOpenDisposition disposition, 104 const GURL& alternate_nav_url, 105 const base::string16& pasted_text, 106 size_t selected_line) { 107 // Invalid URLs such as chrome://history can end up here. 108 if (!match.destination_url.is_valid() || !model_) 109 return; 110 model_->OpenMatch( 111 match, disposition, alternate_nav_url, pasted_text, selected_line); 112 OnMatchOpened(match, model_->profile(), controller_->GetWebContents()); 113 } 114 115 bool OmniboxView::IsEditingOrEmpty() const { 116 return (model_.get() && model_->user_input_in_progress()) || 117 (GetOmniboxTextLength() == 0); 118 } 119 120 int OmniboxView::GetIcon() const { 121 if (!IsEditingOrEmpty()) 122 return controller_->GetToolbarModel()->GetIcon(); 123 return AutocompleteMatch::TypeToLocationBarIcon(model_.get() ? 124 model_->CurrentTextType() : AutocompleteMatchType::URL_WHAT_YOU_TYPED); 125 } 126 127 base::string16 OmniboxView::GetHintText() const { 128 // Attempt to determine the default search provider and use that in the hint 129 // text. 130 TemplateURLService* template_url_service = 131 TemplateURLServiceFactory::GetForProfile(model_->profile()); 132 if (template_url_service) { 133 TemplateURL* template_url = 134 template_url_service->GetDefaultSearchProvider(); 135 if (template_url) 136 return l10n_util::GetStringFUTF16( 137 IDS_OMNIBOX_EMPTY_HINT_WITH_DEFAULT_SEARCH_PROVIDER, 138 template_url->AdjustedShortNameForLocaleDirection()); 139 } 140 141 // Otherwise return a hint based on there being no default search provider. 142 return l10n_util::GetStringUTF16( 143 IDS_OMNIBOX_EMPTY_HINT_NO_DEFAULT_SEARCH_PROVIDER); 144 } 145 146 void OmniboxView::SetUserText(const base::string16& text) { 147 SetUserText(text, text, true); 148 } 149 150 void OmniboxView::SetUserText(const base::string16& text, 151 const base::string16& display_text, 152 bool update_popup) { 153 if (model_.get()) 154 model_->SetUserText(text); 155 SetWindowTextAndCaretPos(display_text, display_text.length(), update_popup, 156 true); 157 } 158 159 void OmniboxView::ShowURL() { 160 SetFocus(); 161 controller_->GetToolbarModel()->set_origin_chip_enabled(false); 162 controller_->GetToolbarModel()->set_url_replacement_enabled(false); 163 model_->UpdatePermanentText(); 164 RevertWithoutResettingSearchTermReplacement(); 165 SelectAll(true); 166 } 167 168 void OmniboxView::HideURL() { 169 controller_->GetToolbarModel()->set_origin_chip_enabled(true); 170 controller_->GetToolbarModel()->set_url_replacement_enabled(true); 171 model_->UpdatePermanentText(); 172 RevertWithoutResettingSearchTermReplacement(); 173 } 174 175 void OmniboxView::RevertAll() { 176 controller_->GetToolbarModel()->set_origin_chip_enabled(true); 177 controller_->GetToolbarModel()->set_url_replacement_enabled(true); 178 RevertWithoutResettingSearchTermReplacement(); 179 } 180 181 void OmniboxView::RevertWithoutResettingSearchTermReplacement() { 182 CloseOmniboxPopup(); 183 if (model_.get()) 184 model_->Revert(); 185 TextChanged(); 186 } 187 188 void OmniboxView::CloseOmniboxPopup() { 189 if (model_.get()) 190 model_->StopAutocomplete(); 191 } 192 193 bool OmniboxView::IsImeShowingPopup() const { 194 // Default to claiming that the IME is not showing a popup, since hiding the 195 // omnibox dropdown is a bad user experience when we don't know for sure that 196 // we have to. 197 return false; 198 } 199 200 void OmniboxView::ShowImeIfNeeded() { 201 } 202 203 bool OmniboxView::IsIndicatingQueryRefinement() const { 204 // The default implementation always returns false. Mobile ports can override 205 // this method and implement as needed. 206 return false; 207 } 208 209 void OmniboxView::OnMatchOpened(const AutocompleteMatch& match, 210 Profile* profile, 211 content::WebContents* web_contents) const {} 212 213 OmniboxView::OmniboxView(Profile* profile, 214 OmniboxEditController* controller, 215 CommandUpdater* command_updater) 216 : controller_(controller), 217 command_updater_(command_updater) { 218 // |profile| can be NULL in tests. 219 if (profile) 220 model_.reset(new OmniboxEditModel(this, controller, profile)); 221 } 222 223 void OmniboxView::TextChanged() { 224 EmphasizeURLComponents(); 225 if (model_.get()) 226 model_->OnChanged(); 227 } 228