[OmniboxAPI] Introducing the omnibox unscoped extensions provider
This CL introduces the skeleton for the provider that will be getting suggestions from an extension in unscoped mode. These results will later be added to an attributed section. The CL handles sending `onInputStarted` and `onInputChanged` to extensions in unscoped mode. To make this CL easier for review: - the suggestions and their events are not handled in this CL, and will be handled separately. - there is no synchronization between the keyword provider and the new provider yet. This is safe for now as the provider does not run unless the experiment and permission are enabled. Note: more tests will be added in crrev.com/c/6109532 when suggestions are available. Bug: 381279308,381280752,381280574 Change-Id: I7002f44c089c7d7583748bef9f1c163f8acc8ecd Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6064059 Commit-Queue: Salma Elmahallawy <esalma@google.com> Reviewed-by: Fabio Tirelo <ftirelo@chromium.org> Cr-Commit-Position: refs/heads/main@{#1398674}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b44605b483
commit
f217bab63a
chrome/browser
BUILD.gn
autocomplete
components/omnibox/browser
BUILD.gnDEPSautocomplete_classifier.ccautocomplete_controller.ccautocomplete_controller.hautocomplete_provider.ccautocomplete_provider.hautocomplete_provider_client.hmock_autocomplete_provider_client.hunscoped_extension_provider.ccunscoped_extension_provider.hunscoped_extension_provider_delegate.ccunscoped_extension_provider_delegate.hunscoped_extension_provider_unittest.cc
extensions
ios/chrome/browser/autocomplete/model
@ -7531,6 +7531,8 @@ static_library("browser") {
|
||||
"autocomplete/keyword_extensions_delegate_impl.h",
|
||||
"autocomplete/shortcuts_extensions_manager.cc",
|
||||
"autocomplete/shortcuts_extensions_manager.h",
|
||||
"autocomplete/unscoped_extension_provider_delegate_impl.cc",
|
||||
"autocomplete/unscoped_extension_provider_delegate_impl.h",
|
||||
"browsing_data/counters/hosted_apps_counter.cc",
|
||||
"browsing_data/counters/hosted_apps_counter.h",
|
||||
"controlled_frame/controlled_frame_media_access_handler.cc",
|
||||
|
@ -85,6 +85,8 @@
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#include "chrome/browser/autocomplete/keyword_extensions_delegate_impl.h"
|
||||
#include "chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h"
|
||||
#include "extensions/common/extension_features.h"
|
||||
#endif
|
||||
|
||||
#if !BUILDFLAG(IS_ANDROID)
|
||||
@ -273,6 +275,19 @@ ChromeAutocompleteProviderClient::GetKeywordExtensionsDelegate(
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<UnscopedExtensionProviderDelegate>
|
||||
ChromeAutocompleteProviderClient::GetUnscopedExtensionProviderDelegate(
|
||||
UnscopedExtensionProvider* provider) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
CHECK(base::FeatureList::IsEnabled(
|
||||
extensions_features::kExperimentalOmniboxLabs));
|
||||
return std::make_unique<UnscopedExtensionProviderDelegateImpl>(profile_,
|
||||
provider);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string ChromeAutocompleteProviderClient::GetAcceptLanguages() const {
|
||||
return profile_->GetPrefs()->GetString(language::prefs::kAcceptLanguages);
|
||||
}
|
||||
|
@ -72,6 +72,9 @@ class ChromeAutocompleteProviderClient : public AutocompleteProviderClient {
|
||||
scoped_refptr<ShortcutsBackend> GetShortcutsBackendIfExists() override;
|
||||
std::unique_ptr<KeywordExtensionsDelegate> GetKeywordExtensionsDelegate(
|
||||
KeywordProvider* keyword_provider) override;
|
||||
std::unique_ptr<UnscopedExtensionProviderDelegate>
|
||||
GetUnscopedExtensionProviderDelegate(
|
||||
UnscopedExtensionProvider* provider) override;
|
||||
std::string GetAcceptLanguages() const override;
|
||||
std::string GetEmbedderRepresentationOfAboutScheme() const override;
|
||||
std::vector<std::u16string> GetBuiltinURLs() override;
|
||||
|
@ -0,0 +1,61 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
|
||||
#include "chrome/browser/omnibox/omnibox_input_watcher_factory.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "components/omnibox/browser/autocomplete_input.h"
|
||||
#include "components/omnibox/browser/unscoped_extension_provider.h"
|
||||
|
||||
UnscopedExtensionProviderDelegateImpl::UnscopedExtensionProviderDelegateImpl(
|
||||
Profile* profile,
|
||||
UnscopedExtensionProvider* provider)
|
||||
: profile_(profile), provider_(provider) {
|
||||
CHECK(provider_);
|
||||
omnibox_input_observation_.Observe(
|
||||
OmniboxInputWatcherFactory::GetForBrowserContext(profile_));
|
||||
}
|
||||
|
||||
UnscopedExtensionProviderDelegateImpl::
|
||||
~UnscopedExtensionProviderDelegateImpl() = default;
|
||||
|
||||
bool UnscopedExtensionProviderDelegateImpl::Start(
|
||||
const AutocompleteInput& input,
|
||||
bool minimal_changes,
|
||||
std::set<std::string> unscoped_mode_extension_ids) {
|
||||
bool want_asynchronous_matches = !input.omit_asynchronous_matches();
|
||||
|
||||
if (minimal_changes) {
|
||||
// TODO(378538411): return previous cached matches if there are only minimal
|
||||
// changes.
|
||||
} else {
|
||||
// TODO(378538411): reset last input and suggest matches.
|
||||
for (const std::string& extension_id : unscoped_mode_extension_ids) {
|
||||
extensions::ExtensionOmniboxEventRouter::OnInputChanged(
|
||||
profile_, extension_id, base::UTF16ToUTF8(input.text()),
|
||||
current_request_id_);
|
||||
}
|
||||
set_done(false);
|
||||
}
|
||||
return want_asynchronous_matches;
|
||||
}
|
||||
|
||||
void UnscopedExtensionProviderDelegateImpl::IncrementRequestId() {
|
||||
current_request_id_++;
|
||||
}
|
||||
|
||||
// Input has been accepted, so end this input session. Ensure
|
||||
// the `OnInputCancelled()` event is not sent, and no more stray
|
||||
// suggestions_ready events are handled.
|
||||
void UnscopedExtensionProviderDelegateImpl::OnOmniboxInputEntered() {
|
||||
// TODO(378538411): make sure this called when a match created by this class
|
||||
// is selected.
|
||||
IncrementRequestId();
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_AUTOCOMPLETE_UNSCOPED_EXTENSION_PROVIDER_DELEGATE_IMPL_H_
|
||||
#define CHROME_BROWSER_AUTOCOMPLETE_UNSCOPED_EXTENSION_PROVIDER_DELEGATE_IMPL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "components/omnibox/browser/autocomplete_match.h"
|
||||
#include "components/omnibox/browser/omnibox_input_watcher.h"
|
||||
#include "components/omnibox/browser/unscoped_extension_provider.h"
|
||||
#include "components/omnibox/browser/unscoped_extension_provider_delegate.h"
|
||||
#include "extensions/buildflags/buildflags.h"
|
||||
|
||||
#if !BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#error "Should not be included when extensions are disabled"
|
||||
#endif
|
||||
|
||||
class UnscopedExtensionProvider;
|
||||
class Profile;
|
||||
|
||||
// Delegate observes extension events as well as input changes in the omnibox.
|
||||
// This class is considered the intermediary between the provider (in
|
||||
// components/) and the Omnibox API (in chrome/browser).
|
||||
class UnscopedExtensionProviderDelegateImpl
|
||||
: public UnscopedExtensionProviderDelegate,
|
||||
public OmniboxInputWatcher::Observer,
|
||||
public OmniboxSuggestionsWatcher::Observer {
|
||||
public:
|
||||
UnscopedExtensionProviderDelegateImpl(Profile* profile,
|
||||
UnscopedExtensionProvider* provider);
|
||||
UnscopedExtensionProviderDelegateImpl(
|
||||
const UnscopedExtensionProviderDelegateImpl&) = delete;
|
||||
UnscopedExtensionProviderDelegateImpl& operator=(
|
||||
const UnscopedExtensionProviderDelegateImpl&) = delete;
|
||||
~UnscopedExtensionProviderDelegateImpl() override;
|
||||
|
||||
// UnscopedExtensionProviderDelegate:
|
||||
bool Start(const AutocompleteInput& input,
|
||||
bool minimal_changes,
|
||||
std::set<std::string> unscoped_mode_extension_ids) override;
|
||||
void IncrementRequestId() override;
|
||||
|
||||
// OmniboxInputWatcher::Observer:
|
||||
void OnOmniboxInputEntered() override;
|
||||
|
||||
private:
|
||||
void set_done(bool done) { provider_->set_done(done); }
|
||||
bool done() const { return provider_->done(); }
|
||||
|
||||
// Identifies the current input state. This is incremented each time the
|
||||
// autocomplete edit's input changes in any way. It is used to tell whether
|
||||
// suggest results from the extension are current.
|
||||
int current_request_id_ = 0;
|
||||
|
||||
// The input from the last request to the extension.
|
||||
AutocompleteInput extension_suggest_last_input_;
|
||||
|
||||
// TODO(378538411): populate this once the suggestions logic is implemented.
|
||||
// Saved suggestions that were received from the extension used
|
||||
// for resetting matches without asking the extension again.
|
||||
std::vector<AutocompleteMatch> extension_suggest_matches_;
|
||||
|
||||
raw_ptr<Profile> profile_;
|
||||
|
||||
// The owner of this class.
|
||||
raw_ptr<UnscopedExtensionProvider> provider_;
|
||||
|
||||
base::ScopedObservation<OmniboxInputWatcher, OmniboxInputWatcher::Observer>
|
||||
omnibox_input_observation_{this};
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_AUTOCOMPLETE_UNSCOPED_EXTENSION_PROVIDER_DELEGATE_IMPL_H_
|
@ -297,6 +297,10 @@ static_library("browser") {
|
||||
"tailored_word_break_iterator.h",
|
||||
"titled_url_match_utils.cc",
|
||||
"titled_url_match_utils.h",
|
||||
"unscoped_extension_provider.cc",
|
||||
"unscoped_extension_provider.h",
|
||||
"unscoped_extension_provider_delegate.cc",
|
||||
"unscoped_extension_provider_delegate.h",
|
||||
"url_index_private_data.cc",
|
||||
"url_index_private_data.h",
|
||||
"url_prefix.cc",
|
||||
@ -415,7 +419,10 @@ static_library("browser") {
|
||||
}
|
||||
|
||||
if (enable_extensions) {
|
||||
deps += [ "//extensions/common:common_constants" ]
|
||||
deps += [
|
||||
"//extensions/common:common_constants",
|
||||
"//extensions/common:extension_features",
|
||||
]
|
||||
sources += [
|
||||
"omnibox_input_watcher.cc",
|
||||
"omnibox_input_watcher.h",
|
||||
@ -835,6 +842,7 @@ source_set("unit_tests") {
|
||||
"suggestion_group_unittest.cc",
|
||||
"tailored_word_break_iterator_unittest.cc",
|
||||
"titled_url_match_utils_unittest.cc",
|
||||
"unscoped_extension_provider_unittest.cc",
|
||||
"url_prefix_unittest.cc",
|
||||
"url_scoring_signals_annotator_unittest.cc",
|
||||
"voice_suggest_provider_unittest.cc",
|
||||
|
@ -47,6 +47,7 @@ include_rules = [
|
||||
"+content/public/common/url_constants.h",
|
||||
"+extensions/buildflags/buildflags.h",
|
||||
"+extensions/common/constants.h",
|
||||
"+extensions/common/extension_features.h",
|
||||
"+google_apis/gaia/gaia_constants.h",
|
||||
"+net",
|
||||
"+services/metrics/public/cpp",
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "components/omnibox/browser/autocomplete_provider.h"
|
||||
#include "components/omnibox/browser/omnibox_field_trial.h"
|
||||
#include "components/omnibox/common/omnibox_features.h"
|
||||
#include "extensions/buildflags/buildflags.h"
|
||||
#include "third_party/metrics_proto/omnibox_event.pb.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
@ -25,6 +26,10 @@
|
||||
#include "components/history_clusters/core/config.h" // nogncheck
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#include "extensions/common/extension_features.h" // nogncheck
|
||||
#endif
|
||||
|
||||
AutocompleteClassifier::AutocompleteClassifier(
|
||||
std::unique_ptr<AutocompleteController> controller,
|
||||
std::unique_ptr<AutocompleteSchemeClassifier> scheme_classifier)
|
||||
@ -82,6 +87,14 @@ int AutocompleteClassifier::DefaultOmniboxProviders(bool is_low_memory_device) {
|
||||
(history_embeddings::GetFeatureParameters().omnibox_scoped ||
|
||||
history_embeddings::GetFeatureParameters().omnibox_unscoped
|
||||
? AutocompleteProvider::TYPE_HISTORY_EMBEDDINGS
|
||||
: 0) |
|
||||
#endif
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
// `UnscopedExtensionProvider` should only be included when extensions are
|
||||
// enabled and the `ExperimentalOmniboxLabs` feature is enabled.
|
||||
(base::FeatureList::IsEnabled(
|
||||
extensions_features::kExperimentalOmniboxLabs)
|
||||
? AutocompleteProvider::TYPE_UNSCOPED_EXTENSION
|
||||
: 0)
|
||||
#else
|
||||
0
|
||||
|
@ -72,6 +72,7 @@
|
||||
#include "components/omnibox/browser/search_provider.h"
|
||||
#include "components/omnibox/browser/search_scoring_signals_annotator.h"
|
||||
#include "components/omnibox/browser/shortcuts_provider.h"
|
||||
#include "components/omnibox/browser/unscoped_extension_provider.h"
|
||||
#include "components/omnibox/browser/url_scoring_signals_annotator.h"
|
||||
#include "components/omnibox/browser/voice_suggest_provider.h"
|
||||
#include "components/omnibox/browser/zero_suggest_provider.h"
|
||||
@ -1200,6 +1201,11 @@ void AutocompleteController::InitializeAsyncProviders(int provider_types) {
|
||||
new HistoryEmbeddingsProvider(provider_client_.get(), this));
|
||||
}
|
||||
#endif
|
||||
if (provider_types & AutocompleteProvider::TYPE_UNSCOPED_EXTENSION) {
|
||||
unscoped_extension_provider_ =
|
||||
new UnscopedExtensionProvider(provider_client_.get(), this);
|
||||
providers_.push_back(unscoped_extension_provider_.get());
|
||||
}
|
||||
}
|
||||
|
||||
void AutocompleteController::InitializeSyncProviders(int provider_types) {
|
||||
|
@ -265,6 +265,9 @@ class AutocompleteController : public AutocompleteProviderListener,
|
||||
return history_url_provider_;
|
||||
}
|
||||
KeywordProvider* keyword_provider() const { return keyword_provider_; }
|
||||
UnscopedExtensionProvider* unscoped_extension_provider() const {
|
||||
return unscoped_extension_provider_;
|
||||
}
|
||||
SearchProvider* search_provider() const { return search_provider_; }
|
||||
ClipboardProvider* clipboard_provider() const { return clipboard_provider_; }
|
||||
VoiceSuggestProvider* voice_suggest_provider() const {
|
||||
@ -517,6 +520,8 @@ class AutocompleteController : public AutocompleteProviderListener,
|
||||
|
||||
raw_ptr<KeywordProvider> keyword_provider_;
|
||||
|
||||
raw_ptr<UnscopedExtensionProvider> unscoped_extension_provider_;
|
||||
|
||||
raw_ptr<SearchProvider> search_provider_;
|
||||
|
||||
raw_ptr<ZeroSuggestProvider> zero_suggest_provider_;
|
||||
|
@ -80,6 +80,8 @@ const char* AutocompleteProvider::TypeToString(Type type) {
|
||||
return "HistoryEmbeddings";
|
||||
case TYPE_ENTERPRISE_SEARCH_AGGREGATOR:
|
||||
return "EnterpriseSearchAggregator";
|
||||
case TYPE_UNSCOPED_EXTENSION:
|
||||
return "UnscopedExtension";
|
||||
default:
|
||||
DUMP_WILL_BE_NOTREACHED()
|
||||
<< "Unhandled AutocompleteProvider::Type " << type;
|
||||
@ -163,6 +165,8 @@ AutocompleteProvider::AsOmniboxEventProviderType() const {
|
||||
return metrics::OmniboxEventProto::HISTORY_EMBEDDINGS;
|
||||
case TYPE_ENTERPRISE_SEARCH_AGGREGATOR:
|
||||
return metrics::OmniboxEventProto::ENTERPRISE_SEARCH_AGGREGATOR;
|
||||
case TYPE_UNSCOPED_EXTENSION:
|
||||
return metrics::OmniboxEventProto::UNSCOPED_EXTENSION;
|
||||
default:
|
||||
// TODO(crbug.com/40940012) This was a NOTREACHED that we converted to
|
||||
// help debug crbug.com/1499235 since NOTREACHED's don't log their
|
||||
|
@ -178,6 +178,7 @@ class AutocompleteProvider
|
||||
TYPE_FEATURED_SEARCH = 1 << 20,
|
||||
TYPE_HISTORY_EMBEDDINGS = 1 << 21,
|
||||
TYPE_ENTERPRISE_SEARCH_AGGREGATOR = 1 << 22,
|
||||
TYPE_UNSCOPED_EXTENSION = 1 << 23,
|
||||
// When adding a value here, also update:
|
||||
// - omnibox_event.proto
|
||||
// - `AutocompleteProvider::AsOmniboxEventProviderType`
|
||||
|
@ -19,6 +19,8 @@ class AutocompleteClassifier;
|
||||
class AutocompleteSchemeClassifier;
|
||||
class AutocompleteScoringModelService;
|
||||
class DocumentSuggestionsService;
|
||||
class UnscopedExtensionProvider;
|
||||
class UnscopedExtensionProviderDelegate;
|
||||
class GURL;
|
||||
class InMemoryURLIndex;
|
||||
class KeywordExtensionsDelegate;
|
||||
@ -97,6 +99,8 @@ class AutocompleteProviderClient : public OmniboxAction::Client {
|
||||
virtual scoped_refptr<ShortcutsBackend> GetShortcutsBackendIfExists() = 0;
|
||||
virtual std::unique_ptr<KeywordExtensionsDelegate>
|
||||
GetKeywordExtensionsDelegate(KeywordProvider* keyword_provider) = 0;
|
||||
virtual std::unique_ptr<UnscopedExtensionProviderDelegate>
|
||||
GetUnscopedExtensionProviderDelegate(UnscopedExtensionProvider* provider) = 0;
|
||||
virtual OmniboxTriggeredFeatureService* GetOmniboxTriggeredFeatureService()
|
||||
const = 0;
|
||||
virtual AutocompleteScoringModelService* GetAutocompleteScoringModelService()
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "components/omnibox/browser/mock_tab_matcher.h"
|
||||
#include "components/omnibox/browser/remote_suggestions_service.h"
|
||||
#include "components/omnibox/browser/shortcuts_backend.h"
|
||||
#include "components/omnibox/browser/unscoped_extension_provider_delegate.h"
|
||||
#include "components/omnibox/browser/zero_suggest_cache_service.h"
|
||||
#include "services/network/public/cpp/shared_url_loader_factory.h"
|
||||
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
|
||||
@ -93,6 +94,11 @@ class MockAutocompleteProviderClient
|
||||
KeywordProvider* keyword_provider) override {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<UnscopedExtensionProviderDelegate>
|
||||
GetUnscopedExtensionProviderDelegate(
|
||||
UnscopedExtensionProvider* unscoped_extension_provider) override {
|
||||
return std::move(unscoped_extension_provider_delegate_);
|
||||
}
|
||||
OmniboxTriggeredFeatureService* GetOmniboxTriggeredFeatureService()
|
||||
const override {
|
||||
return omnibox_triggered_feature_service_.get();
|
||||
@ -157,6 +163,11 @@ class MockAutocompleteProviderClient
|
||||
pedal_provider_ = std::move(pedal_provider);
|
||||
}
|
||||
|
||||
void set_unscoped_extension_provider_delegate(
|
||||
std::unique_ptr<UnscopedExtensionProviderDelegate> delegate) {
|
||||
unscoped_extension_provider_delegate_ = std::move(delegate);
|
||||
}
|
||||
|
||||
void set_template_url_service(TemplateURLService* template_url_service) {
|
||||
template_url_service_ = template_url_service;
|
||||
}
|
||||
@ -189,6 +200,8 @@ class MockAutocompleteProviderClient
|
||||
std::unique_ptr<OmniboxTriggeredFeatureService>
|
||||
omnibox_triggered_feature_service_;
|
||||
std::unique_ptr<ProviderStateService> provider_state_service_;
|
||||
std::unique_ptr<UnscopedExtensionProviderDelegate>
|
||||
unscoped_extension_provider_delegate_;
|
||||
MockTabMatcher tab_matcher_;
|
||||
raw_ptr<signin::IdentityManager> identity_manager_ = nullptr; // Not owned.
|
||||
};
|
||||
|
80
components/omnibox/browser/unscoped_extension_provider.cc
Normal file
80
components/omnibox/browser/unscoped_extension_provider.cc
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "components/omnibox/browser/unscoped_extension_provider.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/check_is_test.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/omnibox/browser/autocomplete_provider.h"
|
||||
#include "components/omnibox/browser/autocomplete_provider_client.h"
|
||||
#include "components/omnibox/browser/autocomplete_provider_listener.h"
|
||||
#include "components/omnibox/browser/unscoped_extension_provider_delegate.h"
|
||||
#include "components/search_engines/template_url_service.h"
|
||||
#include "components/strings/grit/components_strings.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/page_transition_types.h"
|
||||
|
||||
UnscopedExtensionProvider::UnscopedExtensionProvider(
|
||||
AutocompleteProviderClient* client,
|
||||
AutocompleteProviderListener* listener)
|
||||
: AutocompleteProvider(AutocompleteProvider::TYPE_UNSCOPED_EXTENSION),
|
||||
client_(client),
|
||||
template_url_service_(client->GetTemplateURLService()),
|
||||
delegate_(client->GetUnscopedExtensionProviderDelegate(this)) {
|
||||
AddListener(listener);
|
||||
}
|
||||
|
||||
UnscopedExtensionProvider::~UnscopedExtensionProvider() = default;
|
||||
|
||||
void UnscopedExtensionProvider::Start(const AutocompleteInput& input,
|
||||
bool minimal_changes) {
|
||||
// No need to do other checks if there are no unscoped extensions.
|
||||
std::set<std::string> unscoped_extensions = GetUnscopedModeExtensionIds();
|
||||
if (unscoped_extensions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(378538411): investigate enabling zero-suggest to the extensions.
|
||||
if (input.IsZeroSuggest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return early if only synchronous matches are needed and the changes are not
|
||||
// minimal. Minimal changes will not need an async call.
|
||||
if (input.omit_asynchronous_matches() && !minimal_changes) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not send anything in keyword mode.
|
||||
// TODO(378538411): Figure out if `done_` needs to be reset.
|
||||
if (input.InKeywordMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!minimal_changes) {
|
||||
// Reset done and increment the input ID to discard any stale extension
|
||||
// suggestions that may be incoming later if the current request id and
|
||||
// incoming request ids do not match.
|
||||
done_ = true;
|
||||
delegate_->IncrementRequestId();
|
||||
}
|
||||
delegate_->Start(input, minimal_changes, unscoped_extensions);
|
||||
}
|
||||
|
||||
void UnscopedExtensionProvider::Stop(bool clear_cached_results,
|
||||
bool due_to_user_inactivity) {
|
||||
AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
|
||||
delegate_->IncrementRequestId();
|
||||
}
|
||||
|
||||
std::set<std::string> UnscopedExtensionProvider::GetUnscopedModeExtensionIds()
|
||||
const {
|
||||
// Make sure the model is loaded. This is cheap and quickly bails out if
|
||||
// the model is already loaded.
|
||||
template_url_service_->Load();
|
||||
return template_url_service_->GetUnscopedModeExtensionIds();
|
||||
}
|
49
components/omnibox/browser/unscoped_extension_provider.h
Normal file
49
components/omnibox/browser/unscoped_extension_provider.h
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef COMPONENTS_OMNIBOX_BROWSER_UNSCOPED_EXTENSION_PROVIDER_H_
|
||||
#define COMPONENTS_OMNIBOX_BROWSER_UNSCOPED_EXTENSION_PROVIDER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "components/omnibox/browser/autocomplete_match.h"
|
||||
#include "components/omnibox/browser/autocomplete_provider.h"
|
||||
#include "components/omnibox/browser/omnibox_suggestions_watcher.h"
|
||||
#include "components/omnibox/browser/unscoped_extension_provider_delegate.h"
|
||||
|
||||
class AutocompleteInput;
|
||||
class AutocompleteProviderClient;
|
||||
class AutocompleteProviderListener;
|
||||
class TemplateURLService;
|
||||
|
||||
// Provides suggestions from extension that are allowed to run in unscoped mode
|
||||
// (i.e. without requiring keyword mode).
|
||||
class UnscopedExtensionProvider : public AutocompleteProvider {
|
||||
public:
|
||||
UnscopedExtensionProvider(AutocompleteProviderClient* client,
|
||||
AutocompleteProviderListener* listener);
|
||||
UnscopedExtensionProvider(const UnscopedExtensionProvider&) = delete;
|
||||
UnscopedExtensionProvider& operator=(const UnscopedExtensionProvider&) =
|
||||
delete;
|
||||
|
||||
// AutocompleteProvider:
|
||||
void Start(const AutocompleteInput& input, bool minimal_changes) override;
|
||||
void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
|
||||
|
||||
void set_done(bool done) { done_ = done; }
|
||||
bool done() const { return done_; }
|
||||
|
||||
private:
|
||||
~UnscopedExtensionProvider() override;
|
||||
|
||||
std::set<std::string> GetUnscopedModeExtensionIds() const;
|
||||
|
||||
raw_ptr<AutocompleteProviderClient> client_;
|
||||
raw_ptr<TemplateURLService> template_url_service_;
|
||||
std::unique_ptr<UnscopedExtensionProviderDelegate> delegate_;
|
||||
};
|
||||
|
||||
#endif // COMPONENTS_OMNIBOX_BROWSER_UNSCOPED_EXTENSION_PROVIDER_H_
|
@ -0,0 +1,10 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "components/omnibox/browser/unscoped_extension_provider_delegate.h"
|
||||
|
||||
UnscopedExtensionProviderDelegate::UnscopedExtensionProviderDelegate() =
|
||||
default;
|
||||
UnscopedExtensionProviderDelegate::~UnscopedExtensionProviderDelegate() =
|
||||
default;
|
@ -0,0 +1,32 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef COMPONENTS_OMNIBOX_BROWSER_UNSCOPED_EXTENSION_PROVIDER_DELEGATE_H_
|
||||
#define COMPONENTS_OMNIBOX_BROWSER_UNSCOPED_EXTENSION_PROVIDER_DELEGATE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "components/omnibox/browser/autocomplete_input.h"
|
||||
|
||||
class AutocompleteInput;
|
||||
|
||||
class UnscopedExtensionProviderDelegate {
|
||||
public:
|
||||
UnscopedExtensionProviderDelegate();
|
||||
UnscopedExtensionProviderDelegate(const UnscopedExtensionProviderDelegate&) =
|
||||
delete;
|
||||
UnscopedExtensionProviderDelegate& operator=(
|
||||
const UnscopedExtensionProviderDelegate&) = delete;
|
||||
virtual ~UnscopedExtensionProviderDelegate();
|
||||
|
||||
// Starts a new request to the extension.
|
||||
virtual bool Start(const AutocompleteInput& input,
|
||||
bool minimal_changes,
|
||||
std::set<std::string> unscoped_mode_extension_ids) = 0;
|
||||
|
||||
// Increments the id of the request sent to the extension.
|
||||
virtual void IncrementRequestId() = 0;
|
||||
};
|
||||
|
||||
#endif // COMPONENTS_OMNIBOX_BROWSER_UNSCOPED_EXTENSION_PROVIDER_DELEGATE_H_
|
@ -0,0 +1,160 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "components/omnibox/browser/unscoped_extension_provider.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "components/omnibox/browser/autocomplete_scheme_classifier.h"
|
||||
#include "components/omnibox/browser/mock_autocomplete_provider_client.h"
|
||||
#include "components/omnibox/browser/test_scheme_classifier.h"
|
||||
#include "components/search_engines/search_engines_switches.h"
|
||||
#include "components/search_engines/search_engines_test_environment.h"
|
||||
#include "components/search_engines/template_url.h"
|
||||
#include "components/search_engines/template_url_service.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/metrics_proto/omnibox_event.pb.h"
|
||||
#include "third_party/metrics_proto/omnibox_focus_type.pb.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/url_constants.h"
|
||||
|
||||
using base::ASCIIToUTF16;
|
||||
using ::testing::_;
|
||||
|
||||
class UnscopedExtensionProviderTest : public testing::Test {
|
||||
public:
|
||||
class MockUnscopedExtensionProviderDelegate
|
||||
: public UnscopedExtensionProviderDelegate {
|
||||
public:
|
||||
MockUnscopedExtensionProviderDelegate() = default;
|
||||
MockUnscopedExtensionProviderDelegate(
|
||||
const MockUnscopedExtensionProviderDelegate&) = delete;
|
||||
MockUnscopedExtensionProviderDelegate& operator=(
|
||||
const MockUnscopedExtensionProviderDelegate&) = delete;
|
||||
|
||||
~MockUnscopedExtensionProviderDelegate() override = default;
|
||||
|
||||
MOCK_METHOD(bool,
|
||||
Start,
|
||||
(const AutocompleteInput&, bool, std::set<std::string>),
|
||||
(override));
|
||||
MOCK_METHOD(void, IncrementRequestId, (), (override));
|
||||
};
|
||||
|
||||
protected:
|
||||
UnscopedExtensionProviderTest() = default;
|
||||
void SetUp() override;
|
||||
void InitProvider(
|
||||
std::unique_ptr<MockUnscopedExtensionProviderDelegate> mock_delegate);
|
||||
|
||||
base::test::TaskEnvironment task_environment_;
|
||||
search_engines::SearchEnginesTestEnvironment search_engines_test_environment_;
|
||||
std::unique_ptr<MockAutocompleteProviderClient> client_;
|
||||
// The provider is initialized by calling `InitProvider()` in
|
||||
// the tests after the expectations have been set on the delegate.
|
||||
scoped_refptr<UnscopedExtensionProvider> extension_provider_;
|
||||
};
|
||||
|
||||
void UnscopedExtensionProviderTest::SetUp() {
|
||||
client_ = std::make_unique<MockAutocompleteProviderClient>();
|
||||
client_->set_template_url_service(
|
||||
search_engines_test_environment_.template_url_service());
|
||||
}
|
||||
|
||||
void UnscopedExtensionProviderTest::InitProvider(
|
||||
std::unique_ptr<MockUnscopedExtensionProviderDelegate> mock_delegate) {
|
||||
client_->set_unscoped_extension_provider_delegate(std::move(mock_delegate));
|
||||
extension_provider_ = new UnscopedExtensionProvider(client_.get(), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(UnscopedExtensionProviderTest, RunsAndIncrementsRequestIdWithChanges) {
|
||||
std::unique_ptr<MockUnscopedExtensionProviderDelegate> mock_delegate =
|
||||
std::make_unique<MockUnscopedExtensionProviderDelegate>();
|
||||
client_->GetTemplateURLService()->AddToUnscopedModeExtensionIds("id");
|
||||
|
||||
AutocompleteInput input(u"input", metrics::OmniboxEventProto::OTHER,
|
||||
TestSchemeClassifier());
|
||||
input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_DEFAULT);
|
||||
input.set_omit_asynchronous_matches(false);
|
||||
|
||||
EXPECT_CALL(*mock_delegate, IncrementRequestId);
|
||||
EXPECT_CALL(*mock_delegate, Start);
|
||||
|
||||
InitProvider(std::move(mock_delegate));
|
||||
extension_provider_->Start(input, /*minimal_changes=*/false);
|
||||
}
|
||||
|
||||
TEST_F(UnscopedExtensionProviderTest,
|
||||
RunsAndMaintainsRequestIdWithMinimalChanges) {
|
||||
std::unique_ptr<MockUnscopedExtensionProviderDelegate> mock_delegate =
|
||||
std::make_unique<MockUnscopedExtensionProviderDelegate>();
|
||||
client_->GetTemplateURLService()->AddToUnscopedModeExtensionIds("id");
|
||||
|
||||
AutocompleteInput input(u"input", metrics::OmniboxEventProto::OTHER,
|
||||
TestSchemeClassifier());
|
||||
input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_DEFAULT);
|
||||
|
||||
EXPECT_CALL(*mock_delegate, IncrementRequestId).Times(0);
|
||||
EXPECT_CALL(*mock_delegate, Start);
|
||||
|
||||
InitProvider(std::move(mock_delegate));
|
||||
extension_provider_->Start(input, /*minimal_changes=*/true);
|
||||
}
|
||||
|
||||
TEST_F(UnscopedExtensionProviderTest,
|
||||
DoesNotRunWithChangesAndOmitAsyncMatches) {
|
||||
std::unique_ptr<MockUnscopedExtensionProviderDelegate> mock_delegate =
|
||||
std::make_unique<MockUnscopedExtensionProviderDelegate>();
|
||||
client_->GetTemplateURLService()->AddToUnscopedModeExtensionIds("id");
|
||||
|
||||
AutocompleteInput input(u"input", metrics::OmniboxEventProto::OTHER,
|
||||
TestSchemeClassifier());
|
||||
input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_DEFAULT);
|
||||
input.set_omit_asynchronous_matches(true);
|
||||
|
||||
EXPECT_CALL(*mock_delegate, IncrementRequestId).Times(0);
|
||||
EXPECT_CALL(*mock_delegate, Start).Times(0);
|
||||
|
||||
InitProvider(std::move(mock_delegate));
|
||||
extension_provider_->Start(input, /*minimal_changes=*/false);
|
||||
}
|
||||
|
||||
TEST_F(UnscopedExtensionProviderTest, DoesNotRunOnFocus) {
|
||||
std::unique_ptr<MockUnscopedExtensionProviderDelegate> mock_delegate =
|
||||
std::make_unique<MockUnscopedExtensionProviderDelegate>();
|
||||
client_->GetTemplateURLService()->AddToUnscopedModeExtensionIds("id");
|
||||
|
||||
AutocompleteInput input(u"input", metrics::OmniboxEventProto::OTHER,
|
||||
TestSchemeClassifier());
|
||||
input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_FOCUS);
|
||||
|
||||
EXPECT_CALL(*mock_delegate, IncrementRequestId).Times(0);
|
||||
EXPECT_CALL(*mock_delegate, Start).Times(0);
|
||||
|
||||
InitProvider(std::move(mock_delegate));
|
||||
extension_provider_->Start(input, /*minimal_changes=*/false);
|
||||
}
|
||||
|
||||
TEST_F(UnscopedExtensionProviderTest, DoesNotRunWithNoUnscopedExtensions) {
|
||||
std::unique_ptr<MockUnscopedExtensionProviderDelegate> mock_delegate =
|
||||
std::make_unique<MockUnscopedExtensionProviderDelegate>();
|
||||
AutocompleteInput input(u"input", metrics::OmniboxEventProto::OTHER,
|
||||
TestSchemeClassifier());
|
||||
input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_DEFAULT);
|
||||
|
||||
EXPECT_CALL(*mock_delegate, IncrementRequestId).Times(0);
|
||||
EXPECT_CALL(*mock_delegate, Start).Times(0);
|
||||
|
||||
InitProvider(std::move(mock_delegate));
|
||||
extension_provider_->Start(input, /*minimal_changes=*/true);
|
||||
}
|
@ -19,6 +19,7 @@ if (is_component_build) {
|
||||
visibility = [
|
||||
"//extensions/common:common_constants",
|
||||
"//extensions/common:export",
|
||||
"//extensions/common:extension_features",
|
||||
]
|
||||
public_deps = [
|
||||
"//extensions/common:constants_impl",
|
||||
|
@ -67,6 +67,14 @@ source_set("constants_impl") {
|
||||
configs += [ "//build/config/compiler:wexit_time_destructors" ]
|
||||
}
|
||||
|
||||
source_set("extension_features") {
|
||||
sources = [
|
||||
"extension_features.cc",
|
||||
"extension_features.h",
|
||||
]
|
||||
public_deps = [ "//base" ]
|
||||
}
|
||||
|
||||
mojom("mojom") {
|
||||
# We don't want Blink variants of bindings to be generated.
|
||||
disable_variants = true
|
||||
@ -292,8 +300,6 @@ static_library("common") {
|
||||
"extension.h",
|
||||
"extension_api.cc",
|
||||
"extension_api.h",
|
||||
"extension_features.cc",
|
||||
"extension_features.h",
|
||||
"extension_id.h",
|
||||
"extension_l10n_util.cc",
|
||||
"extension_l10n_util.h",
|
||||
@ -497,6 +503,7 @@ static_library("common") {
|
||||
|
||||
public_deps = [
|
||||
":common_constants",
|
||||
":extension_features",
|
||||
":mojom",
|
||||
"//components/services/app_service",
|
||||
"//content/public/common",
|
||||
|
@ -60,6 +60,9 @@ class AutocompleteProviderClientImpl : public AutocompleteProviderClient {
|
||||
scoped_refptr<ShortcutsBackend> GetShortcutsBackendIfExists() override;
|
||||
std::unique_ptr<KeywordExtensionsDelegate> GetKeywordExtensionsDelegate(
|
||||
KeywordProvider* keyword_provider) override;
|
||||
std::unique_ptr<UnscopedExtensionProviderDelegate>
|
||||
GetUnscopedExtensionProviderDelegate(
|
||||
UnscopedExtensionProvider* unscoped_extension_provider) override;
|
||||
OmniboxTriggeredFeatureService* GetOmniboxTriggeredFeatureService()
|
||||
const override;
|
||||
AutocompleteScoringModelService* GetAutocompleteScoringModelService()
|
||||
|
@ -171,6 +171,12 @@ AutocompleteProviderClientImpl::GetKeywordExtensionsDelegate(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<UnscopedExtensionProviderDelegate>
|
||||
AutocompleteProviderClientImpl::GetUnscopedExtensionProviderDelegate(
|
||||
UnscopedExtensionProvider* unscoped_extension_provider) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OmniboxTriggeredFeatureService*
|
||||
AutocompleteProviderClientImpl::GetOmniboxTriggeredFeatureService() const {
|
||||
return omnibox_triggered_feature_service_.get();
|
||||
|
Reference in New Issue
Block a user