blob: e348cc4d379b93dc031bdaf4770c4fdbb91cc472 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/app_list/search/omnibox_result.h"
#include <stddef.h>
#include "ash/public/cpp/app_list/app_list_config.h"
#include "ash/public/cpp/app_list/app_list_features.h"
#include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
#include "chrome/browser/ui/app_list/search/search_util.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/omnibox/browser/autocomplete_controller.h"
#include "components/omnibox/browser/autocomplete_match_type.h"
#include "components/omnibox/browser/vector_icons.h"
#include "ui/gfx/paint_vector_icon.h"
#include "url/gurl.h"
#include "url/url_canon.h"
using bookmarks::BookmarkModel;
namespace app_list {
namespace {
// #000 at 87% opacity.
constexpr SkColor kListIconColor = SkColorSetARGB(0xDE, 0x00, 0x00, 0x00);
int ACMatchStyleToTagStyle(int styles) {
int tag_styles = 0;
if (styles & ACMatchClassification::URL)
tag_styles |= ash::SearchResultTag::URL;
if (styles & ACMatchClassification::MATCH)
tag_styles |= ash::SearchResultTag::MATCH;
if (styles & ACMatchClassification::DIM)
tag_styles |= ash::SearchResultTag::DIM;
return tag_styles;
}
// Translates ACMatchClassifications into ChromeSearchResult tags.
void ACMatchClassificationsToTags(const base::string16& text,
const ACMatchClassifications& text_classes,
ChromeSearchResult::Tags* tags) {
int tag_styles = ash::SearchResultTag::NONE;
size_t tag_start = 0;
for (size_t i = 0; i < text_classes.size(); ++i) {
const ACMatchClassification& text_class = text_classes[i];
// Closes current tag.
if (tag_styles != ash::SearchResultTag::NONE) {
tags->push_back(
ash::SearchResultTag(tag_styles, tag_start, text_class.offset));
tag_styles = ash::SearchResultTag::NONE;
}
if (text_class.style == ACMatchClassification::NONE)
continue;
tag_start = text_class.offset;
tag_styles = ACMatchStyleToTagStyle(text_class.style);
}
if (tag_styles != ash::SearchResultTag::NONE) {
tags->push_back(ash::SearchResultTag(tag_styles, tag_start, text.length()));
}
}
// AutocompleteMatchType::Type to vector icon, used for app list.
const gfx::VectorIcon& TypeToVectorIcon(AutocompleteMatchType::Type type) {
switch (type) {
case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
case AutocompleteMatchType::HISTORY_URL:
case AutocompleteMatchType::HISTORY_TITLE:
case AutocompleteMatchType::HISTORY_BODY:
case AutocompleteMatchType::HISTORY_KEYWORD:
case AutocompleteMatchType::NAVSUGGEST:
case AutocompleteMatchType::BOOKMARK_TITLE:
case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED:
case AutocompleteMatchType::CLIPBOARD:
case AutocompleteMatchType::PHYSICAL_WEB_DEPRECATED:
case AutocompleteMatchType::PHYSICAL_WEB_OVERFLOW_DEPRECATED:
case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
case AutocompleteMatchType::DOCUMENT_SUGGESTION:
case AutocompleteMatchType::PEDAL:
return kIcDomainIcon;
case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
case AutocompleteMatchType::SEARCH_SUGGEST:
case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
case AutocompleteMatchType::SEARCH_SUGGEST_TAIL:
case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE:
case AutocompleteMatchType::SEARCH_OTHER_ENGINE:
case AutocompleteMatchType::CONTACT_DEPRECATED:
case AutocompleteMatchType::VOICE_SUGGEST:
return kIcSearchIcon;
case AutocompleteMatchType::SEARCH_HISTORY:
case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
return kIcHistoryIcon;
case AutocompleteMatchType::CALCULATOR:
return kIcEqualIcon;
case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
case AutocompleteMatchType::NUM_TYPES:
NOTREACHED();
break;
}
NOTREACHED();
return kIcDomainIcon;
}
} // namespace
OmniboxResult::OmniboxResult(Profile* profile,
AppListControllerDelegate* list_controller,
AutocompleteController* autocomplete_controller,
const AutocompleteMatch& match)
: profile_(profile),
list_controller_(list_controller),
autocomplete_controller_(autocomplete_controller),
match_(match) {
if (match_.search_terms_args && autocomplete_controller_) {
match_.search_terms_args->from_app_list = true;
autocomplete_controller_->UpdateMatchDestinationURL(
*match_.search_terms_args, &match_);
}
set_id(match_.destination_url.spec());
set_comparable_id(match_.stripped_destination_url.spec());
SetResultType(ash::SearchResultType::kOmnibox);
// Derive relevance from omnibox relevance and normalize it to [0, 1].
// The magic number 1500 is the highest score of an omnibox result.
// See comments in autocomplete_provider.h.
set_relevance(match_.relevance / 1500.0);
if (AutocompleteMatch::IsSearchType(match_.type))
SetIsOmniboxSearch(true);
UpdateIcon();
UpdateTitleAndDetails();
}
OmniboxResult::~OmniboxResult() = default;
void OmniboxResult::Open(int event_flags) {
RecordHistogram(OMNIBOX_SEARCH_RESULT);
list_controller_->OpenURL(profile_, match_.destination_url, match_.transition,
ui::DispositionFromEventFlags(event_flags));
}
void OmniboxResult::UpdateIcon() {
BookmarkModel* bookmark_model =
BookmarkModelFactory::GetForBrowserContext(profile_);
bool is_bookmarked =
bookmark_model && bookmark_model->IsBookmarked(match_.destination_url);
const gfx::VectorIcon& icon =
is_bookmarked ? kIcBookmarkIcon : TypeToVectorIcon(match_.type);
SetIcon(gfx::CreateVectorIcon(
icon, AppListConfig::instance().search_list_icon_dimension(),
kListIconColor));
}
void OmniboxResult::UpdateTitleAndDetails() {
// For url result with non-empty description, swap title and details. Thus,
// the url description is presented as title, and url itself is presented as
// details.
const bool use_directly = !IsUrlResultWithDescription();
ChromeSearchResult::Tags title_tags;
if (use_directly) {
SetTitle(match_.contents);
ACMatchClassificationsToTags(match_.contents, match_.contents_class,
&title_tags);
} else {
SetTitle(match_.description);
ACMatchClassificationsToTags(match_.description, match_.description_class,
&title_tags);
}
SetTitleTags(title_tags);
ChromeSearchResult::Tags details_tags;
if (use_directly) {
SetDetails(match_.description);
ACMatchClassificationsToTags(match_.description, match_.description_class,
&details_tags);
} else {
SetDetails(match_.contents);
ACMatchClassificationsToTags(match_.contents, match_.contents_class,
&details_tags);
}
SetDetailsTags(details_tags);
}
bool OmniboxResult::IsUrlResultWithDescription() const {
return !AutocompleteMatch::IsSearchType(match_.type) &&
!match_.description.empty();
}
} // namespace app_list