Skip to content

Commit

Permalink
[Omnibox] Classify document provider results.
Browse files Browse the repository at this point in the history
This CL styles the parts of document results’ descriptions which match the
user input. E.g., given the user input 'rain if you dare', and a document
titled 'how to tell if your kitten is a rainbow', the result should display
"if", "you" in "your", and "rain" in "rainbow" as bolded:

how to tell if your kitten is a rainbow
            ^^ ^^^              ^^^^

Bug: 925483
Change-Id: I4c0f255ae5bbefa57e9980ade626efac69a76a18
Reviewed-on: https://chromium-review.googlesource.com/c/1435983
Commit-Queue: manuk hovanesian <manukh@chromium.org>
Reviewed-by: Justin Donnelly <jdonnelly@chromium.org>
Cr-Commit-Position: refs/heads/master@{#626773}
  • Loading branch information
manuk authored and Commit Bot committed Jan 28, 2019
1 parent 1e25e4f commit 7c3b973
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 40 deletions.
39 changes: 37 additions & 2 deletions components/omnibox/browser/document_provider.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/feature_list.h"
#include "base/i18n/case_conversion.h"
#include "base/i18n/time_formatting.h"
#include "base/json/json_reader.h"
#include "base/metrics/field_trial_params.h"
Expand All @@ -24,14 +25,18 @@
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_provider_client.h"
#include "components/omnibox/browser/autocomplete_provider_listener.h"
#include "components/omnibox/browser/document_suggestions_service.h"
#include "components/omnibox/browser/history_provider.h"
#include "components/omnibox/browser/in_memory_url_index_types.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/omnibox/browser/omnibox_pref_names.h"
#include "components/omnibox/browser/scored_history_match.h"
#include "components/omnibox/browser/search_provider.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
Expand Down Expand Up @@ -208,6 +213,8 @@ void DocumentProvider::Start(const AutocompleteInput& input,

Stop(true, false);

input_ = input;

// Create a request for suggestions, routing completion to
base::BindOnce(&DocumentProvider::OnDocumentSuggestionsLoaderAvailable,
weak_ptr_factory_.GetWeakPtr()),
Expand Down Expand Up @@ -458,8 +465,7 @@ bool DocumentProvider::ParseDocumentSearchResults(const base::Value& root_val,
match.stripped_destination_url = GURL(original_url);
}
match.contents = AutocompleteMatch::SanitizeString(title);
AutocompleteMatch::AddLastClassificationIfNecessary(
&match.contents_class, 0, ACMatchClassification::NONE);
match.contents_class = Classify(match.contents, input_.text());
const base::DictionaryValue* metadata = nullptr;
if (result->GetDictionary("metadata", &metadata)) {
if (metadata->GetString("mimeType", &mimetype)) {
Expand Down Expand Up @@ -497,3 +503,32 @@ bool DocumentProvider::ParseDocumentSearchResults(const base::Value& root_val,
}
return true;
}

// static
ACMatchClassifications DocumentProvider::Classify(
const base::string16& text,
const base::string16& input_text) {
base::string16 clean_text = bookmarks::CleanUpTitleForMatching(text);
base::string16 lower_input_text(base::i18n::ToLower(input_text));
String16Vector input_terms =
base::SplitString(lower_input_text, base::kWhitespaceUTF16,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);

TermMatches matches;
for (size_t i = 0; i < input_terms.size(); ++i) {
TermMatches term_matches = MatchTermInString(input_terms[i], clean_text, i);
matches.insert(matches.end(), term_matches.begin(), term_matches.end());
}
matches = SortMatches(matches);
matches = DeoverlapMatches(matches);

WordStarts word_starts;
String16VectorFromString16(clean_text, false, &word_starts);

WordStarts terms_to_word_starts_offsets(input_terms.size(), 0);
matches = ScoredHistoryMatch::FilterTermMatchesByWordStarts(
matches, terms_to_word_starts_offsets, word_starts, 0, std::string::npos);

return HistoryProvider::SpansFromTermMatch(matches, clean_text.length(),
false);
}
16 changes: 16 additions & 0 deletions components/omnibox/browser/document_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ class DocumentProvider : public AutocompleteProvider {
const std::string& modified_timestamp_string,
base::Time now);

// Returns a set of classifications that highlight all the occurrences of
// |input_text| at word breaks in |text|. E.g., given |input_text|
// "rain if you dare" and |text| "how to tell if your kitten is a rainbow",
// will return the classifications:
// __ ___ ____
// how to tell if your kitten is a rainbow
// ^ ^ ^^ ^ ^ ^
// NONE M |M | | NONE
// NONE NONE MATCH
static ACMatchClassifications Classify(const base::string16& input_text,
const base::string16& text);

// Whether a field trial has triggered for this query and this session,
// respectively. Works similarly to BaseSearchProvider, though this class does
// not inherit from it.
Expand All @@ -135,6 +147,10 @@ class DocumentProvider : public AutocompleteProvider {
// Listener to notify when results are available.
AutocompleteProviderListener* listener_;

// Saved when starting a new autocomplete request so that it can be retrieved
// when responses return asynchronously.
AutocompleteInput input_;

// Loader used to retrieve results.
std::unique_ptr<network::SimpleURLLoader> loader_;

Expand Down
63 changes: 31 additions & 32 deletions components/omnibox/browser/history_provider.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,36 +39,6 @@ bool HistoryProvider::PreventInlineAutocomplete(
(!input.text().empty() && base::IsUnicodeWhitespace(input.text().back()));
}

HistoryProvider::HistoryProvider(AutocompleteProvider::Type type,
AutocompleteProviderClient* client)
: AutocompleteProvider(type), client_(client) {
}

HistoryProvider::~HistoryProvider() {}

void HistoryProvider::DeleteMatchFromMatches(const AutocompleteMatch& match) {
bool found = false;
BookmarkModel* bookmark_model = client_->GetBookmarkModel();
for (auto i(matches_.begin()); i != matches_.end(); ++i) {
if (i->destination_url == match.destination_url && i->type == match.type) {
found = true;
if ((i->type == AutocompleteMatchType::URL_WHAT_YOU_TYPED) ||
(bookmark_model &&
bookmark_model->IsBookmarked(i->destination_url))) {
// We can't get rid of What-You-Typed or Bookmarked matches,
// but we can make them look like they have no backing data.
i->deletable = false;
i->description.clear();
i->description_class.clear();
} else {
matches_.erase(i);
}
break;
}
}
DCHECK(found) << "Asked to delete a URL that isn't in our set of matches";
}

// static
ACMatchClassifications HistoryProvider::SpansFromTermMatch(
const TermMatches& matches,
Expand All @@ -87,8 +57,8 @@ ACMatchClassifications HistoryProvider::SpansFromTermMatch(
size_t match_count = matches.size();
for (size_t i = 0; i < match_count;) {
size_t offset = matches[i].offset;
spans.push_back(ACMatchClassification(offset,
ACMatchClassification::MATCH | url_style));
spans.push_back(ACMatchClassification(
offset, ACMatchClassification::MATCH | url_style));
// Skip all adjacent matches.
do {
offset += matches[i].length;
Expand All @@ -100,3 +70,32 @@ ACMatchClassifications HistoryProvider::SpansFromTermMatch(

return spans;
}

HistoryProvider::HistoryProvider(AutocompleteProvider::Type type,
AutocompleteProviderClient* client)
: AutocompleteProvider(type), client_(client) {}

HistoryProvider::~HistoryProvider() {}

void HistoryProvider::DeleteMatchFromMatches(const AutocompleteMatch& match) {
bool found = false;
BookmarkModel* bookmark_model = client_->GetBookmarkModel();
for (auto i(matches_.begin()); i != matches_.end(); ++i) {
if (i->destination_url == match.destination_url && i->type == match.type) {
found = true;
if ((i->type == AutocompleteMatchType::URL_WHAT_YOU_TYPED) ||
(bookmark_model &&
bookmark_model->IsBookmarked(i->destination_url))) {
// We can't get rid of What-You-Typed or Bookmarked matches,
// but we can make them look like they have no backing data.
i->deletable = false;
i->description.clear();
i->description_class.clear();
} else {
matches_.erase(i);
}
break;
}
}
DCHECK(found) << "Asked to delete a URL that isn't in our set of matches";
}
12 changes: 6 additions & 6 deletions components/omnibox/browser/history_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class HistoryProvider : public AutocompleteProvider {
// is true or the input text contains trailing whitespace.
static bool PreventInlineAutocomplete(const AutocompleteInput& input);

// Fill and return an ACMatchClassifications structure given the |matches|
// to highlight.
static ACMatchClassifications SpansFromTermMatch(const TermMatches& matches,
size_t text_length,
bool is_url);

protected:
HistoryProvider(AutocompleteProvider::Type type,
AutocompleteProviderClient* client);
Expand All @@ -37,12 +43,6 @@ class HistoryProvider : public AutocompleteProvider {
// backing data.
void DeleteMatchFromMatches(const AutocompleteMatch& match);

// Fill and return an ACMatchClassifications structure given the |matches|
// to highlight.
static ACMatchClassifications SpansFromTermMatch(const TermMatches& matches,
size_t text_length,
bool is_url);

AutocompleteProviderClient* client() { return client_; }

private:
Expand Down

0 comments on commit 7c3b973

Please sign in to comment.