diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2023-10-27 17:02:53 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2023-10-27 17:04:08 +0200 |
commit | 3dce9b5818576f04ce21cec4b3686eda012e5b65 (patch) | |
tree | fe3d59c6da3e62c74563710ba63996585293c743 /chromium/chrome/browser/extensions | |
parent | 5a424f4a7b188b75da63eb697f63558af0b17f6f (diff) |
BASELINE: Update Chromium to 118.0.5993.24
Change-Id: I8373334b8ea8225ab4d934dc676aabc6a85a7efa
Diffstat (limited to 'chromium/chrome/browser/extensions')
210 files changed, 5445 insertions, 3959 deletions
diff --git a/chromium/chrome/browser/extensions/BUILD.gn b/chromium/chrome/browser/extensions/BUILD.gn index 51ffd40dbf0..690a5ddf57d 100644 --- a/chromium/chrome/browser/extensions/BUILD.gn +++ b/chromium/chrome/browser/extensions/BUILD.gn @@ -185,8 +185,6 @@ static_library("extensions") { "api/identity/identity_launch_web_auth_flow_function.h", "api/identity/identity_mint_queue.cc", "api/identity/identity_mint_queue.h", - "api/identity/identity_private_api.cc", - "api/identity/identity_private_api.h", "api/identity/identity_remove_cached_auth_token_function.cc", "api/identity/identity_remove_cached_auth_token_function.h", "api/identity/identity_token_cache.cc", @@ -290,6 +288,14 @@ static_library("extensions") { "api/proxy/proxy_api_constants.h", "api/proxy/proxy_api_helpers.cc", "api/proxy/proxy_api_helpers.h", + "api/reading_list/reading_list_api.cc", + "api/reading_list/reading_list_api.h", + "api/reading_list/reading_list_api_constants.cc", + "api/reading_list/reading_list_api_constants.h", + "api/reading_list/reading_list_event_router.cc", + "api/reading_list/reading_list_event_router.h", + "api/reading_list/reading_list_util.cc", + "api/reading_list/reading_list_util.h", "api/resources_private/resources_private_api.cc", "api/resources_private/resources_private_api.h", "api/runtime/chrome_runtime_api_delegate.cc", @@ -566,6 +572,8 @@ static_library("extensions") { "extension_system_impl.h", "extension_tab_util.cc", "extension_tab_util.h", + "extension_telemetry_service_verdict_handler.cc", + "extension_telemetry_service_verdict_handler.h", "extension_ui_util.cc", "extension_ui_util.h", "extension_uninstall_dialog.cc", @@ -735,6 +743,13 @@ static_library("extensions") { ] } + if (is_chromeos_ash) { + sources += [ + "file_handlers/web_file_handlers_permission_handler.cc", + "file_handlers/web_file_handlers_permission_handler.h", + ] + } + configs += [ "//build/config:precompiled_headers", "//build/config/compiler:wexit_time_destructors", @@ -811,7 +826,6 @@ static_library("extensions") { "//chrome/browser/safe_browsing:metrics_collector", "//chrome/browser/ui/tabs:tab_enums", "//chrome/browser/web_applications", - "//chromeos/components/kiosk", "//components/cbor:cbor", "//components/commerce/core:pref_names", "//components/device_reauth", @@ -865,6 +879,7 @@ static_library("extensions") { "//components/language/core/common", "//components/language/core/language_model", "//components/live_caption:constants", + "//components/media_device_salt", "//components/nacl/common:buildflags", "//components/navigation_interception", "//components/net_log", @@ -875,6 +890,8 @@ static_library("extensions") { "//components/password_manager/core/browser", "//components/password_manager/core/browser:affiliation", "//components/password_manager/core/browser:import_results", + "//components/password_manager/core/browser/features:password_features", + "//components/password_manager/core/browser/import:importer", "//components/password_manager/core/browser/leak_detection", "//components/payments/core", "//components/performance_manager", @@ -883,7 +900,9 @@ static_library("extensions") { "//components/policy/core/browser", "//components/pref_registry", "//components/privacy_sandbox:privacy_sandbox_prefs", + "//components/privacy_sandbox:tracking_protection_prefs", "//components/proxy_config", + "//components/reading_list/core", "//components/resources", "//components/safe_browsing:buildflags", "//components/safe_browsing/content/browser/web_ui:web_ui", @@ -892,7 +911,7 @@ static_library("extensions") { "//components/safe_browsing/core/common:safe_browsing_prefs", "//components/safe_browsing/core/common/proto:csd_proto", "//components/search_engines", - "//components/services/app_service/public/cpp:app_types", + "//components/services/app_service", "//components/services/patch/content", "//components/services/unzip/content", "//components/services/unzip/public/cpp", @@ -1170,11 +1189,11 @@ static_library("extensions") { "//ash/webui/camera_app_ui:mojo_bindings", "//ash/webui/file_manager/untrusted_resources:file_manager_untrusted_resources", "//ash/webui/resources:media_app_bundle_resources_grit", + "//ash/webui/settings/public/constants:mojom", "//chrome/browser/ash/crosapi", "//chrome/browser/ash/crostini:crostini_installer_types_mojom", "//chrome/browser/devtools", "//chrome/browser/nearby_sharing/common", - "//chrome/browser/ui/webui/settings/chromeos/constants:mojom", "//chromeos/ash/components/attestation", "//chromeos/ash/components/cryptohome", "//chromeos/ash/components/dbus", @@ -1193,6 +1212,7 @@ static_library("extensions") { "//chromeos/ash/components/login/auth", "//chromeos/ash/components/login/login_state", "//chromeos/ash/components/network", + "//chromeos/ash/components/osauth/public", "//chromeos/ash/components/proximity_auth", "//chromeos/ash/components/settings", "//chromeos/ash/components/system", @@ -1286,7 +1306,6 @@ static_library("extensions") { "system_display/display_info_provider_mac.cc", "system_display/display_info_provider_mac.h", ] - configs += [ "//build/config/compiler:enable_arc" ] } if (is_linux || is_chromeos_lacros) { @@ -1407,7 +1426,6 @@ static_library("test_support") { testonly = true sources = [ - "api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h", "api/passwords_private/test_passwords_private_delegate.cc", "api/passwords_private/test_passwords_private_delegate.h", "chrome_extension_test_notification_observer.cc", diff --git a/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc b/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc index 6fb95362c99..807a958ebbb 100644 --- a/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc +++ b/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc @@ -112,25 +112,8 @@ IN_PROC_BROWSER_TEST_P(AlarmsApiTest, IncognitoSpanning) { EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } -// Test that the histogram for counting the number of alarms for an extension -// is working properly. The PRE step installs the alarms and we'll check the -// histogram in the main part of the test. -IN_PROC_BROWSER_TEST_P(AlarmsApiTest, PRE_Count) { +IN_PROC_BROWSER_TEST_P(AlarmsApiTest, Count) { EXPECT_TRUE(RunExtensionTest("alarms/count")) << message_; } -// TODO(crbug.com/1405713): Fix failing test on Mac builders. -#if BUILDFLAG(IS_MAC) -#define MAYBE_Count DISABLED_Count -#else -#define MAYBE_Count Count -#endif -IN_PROC_BROWSER_TEST_P(AlarmsApiTest, MAYBE_Count) { - // The histogram will be updated when the extension is loaded during - // startup. This will happen before we enter the test, so just check - // that the update is present. - histogram_tester_->ExpectUniqueSample( - "Extensions.AlarmManager.AlarmsLoadedCount", 100, 1); -} - } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc b/chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc index c10ee262684..6aec2a92208 100644 --- a/chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc +++ b/chromium/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc @@ -25,6 +25,7 @@ #include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h" #include "chrome/browser/extensions/api/preference/preference_api.h" #include "chrome/browser/extensions/api/processes/processes_api.h" +#include "chrome/browser/extensions/api/reading_list/reading_list_event_router.h" #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h" #include "chrome/browser/extensions/api/sessions/sessions_api.h" #include "chrome/browser/extensions/api/settings_overrides/settings_overrides_api.h" @@ -101,6 +102,7 @@ void EnsureApiBrowserContextKeyedServiceFactoriesBuilt() { #endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) extensions::PreferenceAPI::GetFactoryInstance(); extensions::ProcessesAPI::GetFactoryInstance(); + extensions::ReadingListEventRouter::GetFactoryInstance(); extensions::SafeBrowsingPrivateEventRouterFactory::GetInstance(); extensions::SessionsAPI::GetFactoryInstance(); extensions::SettingsPrivateEventRouterFactory::GetInstance(); diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc index a956d2e7f5d..b619e9a0bde 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc @@ -6,6 +6,7 @@ #include <stddef.h> +#include <algorithm> #include <utility> #include "base/functional/bind.h" @@ -22,12 +23,15 @@ #include "components/autofill/content/browser/content_autofill_driver.h" #include "components/autofill/content/browser/content_autofill_driver_factory.h" #include "components/autofill/core/browser/autofill_address_util.h" +#include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/browser_autofill_manager.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/data_model/iban.h" #include "components/autofill/core/browser/form_data_importer.h" +#include "components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h" #include "components/autofill/core/browser/payments/local_card_migration_manager.h" +#include "components/autofill/core/browser/payments/mandatory_reauth_manager.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h" #include "components/autofill/core/browser/personal_data_manager.h" @@ -36,7 +40,6 @@ #include "components/autofill/core/common/autofill_prefs.h" #include "components/signin/public/identity_manager/account_info.h" #include "components/strings/grit/components_chromium_strings.h" -#include "components/strings/grit/components_google_chrome_strings.h" #include "components/strings/grit/components_strings.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/extension_function.h" @@ -50,6 +53,11 @@ namespace autofill_private = extensions::api::autofill_private; namespace addressinput = i18n::addressinput; +using autofill::autofill_metrics::LogMandatoryReauthOptInOrOutUpdateEvent; +using autofill::autofill_metrics::LogMandatoryReauthSettingsPageEditCardEvent; +using autofill::autofill_metrics::MandatoryReauthAuthenticationFlowEvent; +using autofill::autofill_metrics::MandatoryReauthOptInOrOutSource; + namespace { static const char kSettingsOrigin[] = "Chrome settings"; @@ -67,89 +75,19 @@ constexpr char kFieldLengthKey[] = "isLongField"; constexpr char kFieldNameKey[] = "fieldName"; constexpr char kFieldRequired[] = "isRequired"; -// Field names for the address components. -constexpr char kFullNameField[] = "FULL_NAME"; -constexpr char kCompanyNameField[] = "COMPANY_NAME"; -constexpr char kAddressLineField[] = "ADDRESS_LINES"; -constexpr char kDependentLocalityField[] = "ADDRESS_LEVEL_3"; -constexpr char kCityField[] = "ADDRESS_LEVEL_2"; -constexpr char kStateField[] = "ADDRESS_LEVEL_1"; -constexpr char kPostalCodeField[] = "POSTAL_CODE"; -constexpr char kSortingCodeField[] = "SORTING_CODE"; -constexpr char kCountryField[] = "COUNTY_CODE"; - -// Converts an autofill::ServerFieldType to string format. Used in serilization -// of field type info to be used in JavaScript code, and hence those values -// shouldn't be modified. -const char* GetStringFromAddressField(i18n::addressinput::AddressField type) { - switch (type) { - case i18n::addressinput::RECIPIENT: - return kFullNameField; - case i18n::addressinput::ORGANIZATION: - return kCompanyNameField; - case i18n::addressinput::STREET_ADDRESS: - return kAddressLineField; - case i18n::addressinput::DEPENDENT_LOCALITY: - return kDependentLocalityField; - case i18n::addressinput::LOCALITY: - return kCityField; - case i18n::addressinput::ADMIN_AREA: - return kStateField; - case i18n::addressinput::POSTAL_CODE: - return kPostalCodeField; - case i18n::addressinput::SORTING_CODE: - return kSortingCodeField; - case i18n::addressinput::COUNTRY: - return kCountryField; - default: - NOTREACHED(); - return ""; - } -} - // Serializes the AddressUiComponent a map from string to base::Value(). base::Value::Dict AddressUiComponentAsValueMap( - const autofill::ExtendedAddressUiComponent& address_ui_component) { + const autofill::AutofillAddressUIComponent& address_ui_component) { base::Value::Dict info; info.Set(kFieldNameKey, address_ui_component.name); - info.Set(kFieldTypeKey, - GetStringFromAddressField(address_ui_component.field)); + info.Set(kFieldTypeKey, FieldTypeToStringPiece(address_ui_component.field)); info.Set(kFieldLengthKey, address_ui_component.length_hint == - i18n::addressinput::AddressUiComponent::HINT_LONG); + autofill::AutofillAddressUIComponent::HINT_LONG); info.Set(kFieldRequired, address_ui_component.is_required); return info; } -// Searches the |list| for the value at |index|. If this value is present in -// any of the rest of the list, then the item (at |index|) is removed. The -// comparison of phone number values is done on normalized versions of the phone -// number values. -void RemoveDuplicatePhoneNumberAtIndex(size_t index, - const std::string& country_code, - base::Value::List& list) { - if (list.size() <= index) { - NOTREACHED() << "List should have a value at index " << index; - return; - } - const std::string& new_value = list[index].GetString(); - - bool is_duplicate = false; - std::string app_locale = g_browser_process->GetApplicationLocale(); - for (size_t i = 0; i < list.size() && !is_duplicate; ++i) { - if (i == index) - continue; - - const std::string& existing_value = list[i].GetString(); - is_duplicate = autofill::i18n::PhoneNumbersMatch( - base::UTF8ToUTF16(new_value), base::UTF8ToUTF16(existing_value), - country_code, app_locale); - } - - if (is_duplicate) - list.erase(list.begin() + index); -} - autofill::AutofillManager* GetAutofillManager( content::WebContents* web_contents) { if (!web_contents) { @@ -160,7 +98,7 @@ autofill::AutofillManager* GetAutofillManager( ->DriverForFrame(web_contents->GetPrimaryMainFrame()); if (!autofill_driver) return nullptr; - return autofill_driver->autofill_manager(); + return &autofill_driver->GetAutofillManager(); } autofill::AutofillProfile CreateNewAutofillProfile( @@ -238,90 +176,33 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveAddressFunction::Run() { if (!existing_profile) return RespondNow(Error(kErrorDataUnavailable)); } - autofill::AutofillProfile profile = - existing_profile - ? *existing_profile - : CreateNewAutofillProfile(personal_data, address->country_code); - - if (address->full_names) { - std::string full_name; - if (!address->full_names->empty()) - full_name = address->full_names->at(0); - profile.SetInfoWithVerificationStatus( - autofill::AutofillType(autofill::NAME_FULL), - base::UTF8ToUTF16(full_name), g_browser_process->GetApplicationLocale(), - kUserVerified); - } - - if (address->honorific) { - profile.SetRawInfoWithVerificationStatus( - autofill::NAME_HONORIFIC_PREFIX, base::UTF8ToUTF16(*address->honorific), - kUserVerified); - } - - if (address->company_name) { - profile.SetRawInfoWithVerificationStatus( - autofill::COMPANY_NAME, base::UTF8ToUTF16(*address->company_name), - kUserVerified); - } - - if (address->address_lines) { - profile.SetRawInfoWithVerificationStatus( - autofill::ADDRESS_HOME_STREET_ADDRESS, - base::UTF8ToUTF16(*address->address_lines), kUserVerified); - } - - if (address->address_level1) { - profile.SetRawInfoWithVerificationStatus( - autofill::ADDRESS_HOME_STATE, - base::UTF8ToUTF16(*address->address_level1), kUserVerified); - } - - if (address->address_level2) { - profile.SetRawInfoWithVerificationStatus( - autofill::ADDRESS_HOME_CITY, - base::UTF8ToUTF16(*address->address_level2), kUserVerified); - } - - if (address->address_level3) { - profile.SetRawInfoWithVerificationStatus( - autofill::ADDRESS_HOME_DEPENDENT_LOCALITY, - base::UTF8ToUTF16(*address->address_level3), kUserVerified); - } - - if (address->postal_code) { - profile.SetRawInfoWithVerificationStatus( - autofill::ADDRESS_HOME_ZIP, base::UTF8ToUTF16(*address->postal_code), - kUserVerified); - } - - if (address->sorting_code) { - profile.SetRawInfoWithVerificationStatus( - autofill::ADDRESS_HOME_SORTING_CODE, - base::UTF8ToUTF16(*address->sorting_code), kUserVerified); - } - - if (address->country_code) { - profile.SetRawInfoWithVerificationStatus( - autofill::ADDRESS_HOME_COUNTRY, - base::UTF8ToUTF16(*address->country_code), kUserVerified); + absl::optional<base::StringPiece> country_code; + if (auto it = std::find_if( + address->fields.begin(), address->fields.end(), + [](const auto& field) { + return field.type == + autofill_private::ServerFieldType::kAddressHomeCountry; + }); + it != address->fields.end()) { + country_code = it->value; } - - if (address->phone_numbers) { - std::string phone; - if (!address->phone_numbers->empty()) - phone = address->phone_numbers->at(0); - profile.SetRawInfoWithVerificationStatus(autofill::PHONE_HOME_WHOLE_NUMBER, - base::UTF8ToUTF16(phone), - kUserVerified); - } - - if (address->email_addresses) { - std::string email; - if (!address->email_addresses->empty()) - email = address->email_addresses->at(0); - profile.SetRawInfoWithVerificationStatus( - autofill::EMAIL_ADDRESS, base::UTF8ToUTF16(email), kUserVerified); + autofill::AutofillProfile profile = + existing_profile ? *existing_profile + : CreateNewAutofillProfile(personal_data, country_code); + + // TODO(crbug.com/1441904): Fields not visible for the autofill profile's + // country must be reset. + for (const api::autofill_private::AddressField& field : address->fields) { + if (field.type == autofill_private::ServerFieldType::kNameFull) { + profile.SetInfoWithVerificationStatus( + autofill::AutofillType(autofill::NAME_FULL), + base::UTF8ToUTF16(field.value), + g_browser_process->GetApplicationLocale(), kUserVerified); + } else { + profile.SetRawInfoWithVerificationStatus( + autofill::TypeNameToFieldType(autofill_private::ToString(field.type)), + base::UTF8ToUTF16(field.value), kUserVerified); + } } if (address->language_code) @@ -369,7 +250,7 @@ AutofillPrivateGetAddressComponentsFunction::Run() { api::autofill_private::GetAddressComponents::Params::Create(args()); EXTENSION_FUNCTION_VALIDATE(parameters); - std::vector<std::vector<autofill::ExtendedAddressUiComponent>> lines; + std::vector<std::vector<autofill::AutofillAddressUIComponent>> lines; std::string language_code; autofill::GetAddressComponents( @@ -382,7 +263,7 @@ AutofillPrivateGetAddressComponentsFunction::Run() { for (auto& line : lines) { base::Value::List row_values; - for (const autofill::ExtendedAddressUiComponent& component : line) { + for (const autofill::AutofillAddressUIComponent& component : line) { row_values.Append(AddressUiComponentAsValueMap(component)); } base::Value::Dict row; @@ -508,7 +389,7 @@ ExtensionFunction::ResponseAction AutofillPrivateRemoveEntryFunction::Run() { if (!personal_data || !personal_data->IsDataLoaded()) return RespondNow(Error(kErrorDataUnavailable)); - if (personal_data->GetIBANByGUID(parameters->guid)) { + if (personal_data->GetIbanByGUID(parameters->guid)) { base::RecordAction(base::UserMetricsAction("AutofillIbanDeleted")); } @@ -518,30 +399,6 @@ ExtensionFunction::ResponseAction AutofillPrivateRemoveEntryFunction::Run() { } //////////////////////////////////////////////////////////////////////////////// -// AutofillPrivateValidatePhoneNumbersFunction - -ExtensionFunction::ResponseAction -AutofillPrivateValidatePhoneNumbersFunction::Run() { - absl::optional<api::autofill_private::ValidatePhoneNumbers::Params> - parameters = - api::autofill_private::ValidatePhoneNumbers::Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(parameters); - - api::autofill_private::ValidatePhoneParams& params = parameters->params; - - // Extract the phone numbers into a base::Value::List. - base::Value::List phone_numbers; - for (auto phone_number : params.phone_numbers) { - phone_numbers.Append(phone_number); - } - - RemoveDuplicatePhoneNumberAtIndex(params.index_of_new_number, - params.country_code, phone_numbers); - - return RespondNow(WithArguments(std::move(phone_numbers))); -} - -//////////////////////////////////////////////////////////////////////////////// // AutofillPrivateMaskCreditCardFunction ExtensionFunction::ResponseAction AutofillPrivateMaskCreditCardFunction::Run() { @@ -594,13 +451,14 @@ AutofillPrivateMigrateCreditCardsFunction::Run() { // FormDataImporter. autofill::AutofillManager* autofill_manager = GetAutofillManager(GetSenderWebContents()); - if (!autofill_manager || !autofill_manager->client()) + if (!autofill_manager) { return RespondNow(Error(kErrorDataUnavailable)); + } // Get the FormDataImporter from AutofillClient. FormDataImporter owns // LocalCardMigrationManager. autofill::FormDataImporter* form_data_importer = - autofill_manager->client()->GetFormDataImporter(); + autofill_manager->client().GetFormDataImporter(); if (!form_data_importer) return RespondNow(Error(kErrorDataUnavailable)); @@ -681,16 +539,16 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveIbanFunction::Run() { // the Chrome payment settings page. Otherwise, leaving it blank creates a new // IBAN. std::string guid = iban_entry->guid ? *iban_entry->guid : ""; - const autofill::IBAN* existing_iban = nullptr; + const autofill::Iban* existing_iban = nullptr; if (!guid.empty()) { - existing_iban = personal_data->GetIBANByGUID(guid); + existing_iban = personal_data->GetIbanByGUID(guid); if (!existing_iban) return RespondNow(Error(kErrorDataUnavailable)); } - autofill::IBAN iban = + autofill::Iban iban = existing_iban ? *existing_iban - : autofill::IBAN(base::Uuid::GenerateRandomV4().AsLowercaseString()); + : autofill::Iban(base::Uuid::GenerateRandomV4().AsLowercaseString()); iban.SetRawInfo(autofill::IBAN_VALUE, base::UTF8ToUTF16(*iban_entry->value)); @@ -698,7 +556,7 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveIbanFunction::Run() { iban.set_nickname(base::UTF8ToUTF16(*iban_entry->nickname)); if (guid.empty()) { - personal_data->AddIBAN(iban); + personal_data->AddIban(iban); base::RecordAction(base::UserMetricsAction("AutofillIbanAdded")); if (!iban.nickname().empty()) { base::RecordAction( @@ -708,7 +566,7 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveIbanFunction::Run() { } if (existing_iban->Compare(iban) != 0) { - personal_data->UpdateIBAN(iban); + personal_data->UpdateIban(iban); base::RecordAction(base::UserMetricsAction("AutofillIbanEdited")); // Record when nickname is updated. if (existing_iban->nickname() != iban.nickname()) { @@ -744,21 +602,7 @@ ExtensionFunction::ResponseAction AutofillPrivateIsValidIbanFunction::Run() { api::autofill_private::IsValidIban::Params::Create(args()); EXTENSION_FUNCTION_VALIDATE(parameters); return RespondNow(WithArguments( - autofill::IBAN::IsValid(base::UTF8ToUTF16(parameters->iban_value)))); -} - -//////////////////////////////////////////////////////////////////////////////// -// AutofillPrivateGetUpiIdListFunction - -ExtensionFunction::ResponseAction AutofillPrivateGetUpiIdListFunction::Run() { - autofill::PersonalDataManager* personal_data = - autofill::PersonalDataManagerFactory::GetForProfile( - Profile::FromBrowserContext(browser_context())); - DCHECK(personal_data && personal_data->IsDataLoaded()); - - return RespondNow( - ArgumentList(api::autofill_private::GetUpiIdList::Results::Create( - personal_data->GetUpiIds()))); + autofill::Iban::IsValid(base::UTF8ToUTF16(parameters->iban_value)))); } //////////////////////////////////////////////////////////////////////////////// @@ -783,17 +627,16 @@ ExtensionFunction::ResponseAction AutofillPrivateAddVirtualCardFunction::Run() { autofill::AutofillManager* autofill_manager = GetAutofillManager(GetSenderWebContents()); - if (!autofill_manager || !autofill_manager->client() || - !autofill_manager->client()->GetFormDataImporter() || + if (!autofill_manager || !autofill_manager->client().GetFormDataImporter() || !autofill_manager->client() - ->GetFormDataImporter() + .GetFormDataImporter() ->GetVirtualCardEnrollmentManager()) { return RespondNow(Error(kErrorDataUnavailable)); } autofill::VirtualCardEnrollmentManager* virtual_card_enrollment_manager = autofill_manager->client() - ->GetFormDataImporter() + .GetFormDataImporter() ->GetVirtualCardEnrollmentManager(); virtual_card_enrollment_manager->InitVirtualCardEnroll( @@ -824,17 +667,16 @@ AutofillPrivateRemoveVirtualCardFunction::Run() { autofill::AutofillManager* autofill_manager = GetAutofillManager(GetSenderWebContents()); - if (!autofill_manager || !autofill_manager->client() || - !autofill_manager->client()->GetFormDataImporter() || + if (!autofill_manager || !autofill_manager->client().GetFormDataImporter() || !autofill_manager->client() - ->GetFormDataImporter() + .GetFormDataImporter() ->GetVirtualCardEnrollmentManager()) { return RespondNow(Error(kErrorDataUnavailable)); } autofill::VirtualCardEnrollmentManager* virtual_card_enrollment_manager = autofill_manager->client() - ->GetFormDataImporter() + .GetFormDataImporter() ->GetVirtualCardEnrollmentManager(); virtual_card_enrollment_manager->Unenroll( @@ -856,50 +698,75 @@ AutofillPrivateAuthenticateUserAndFlipMandatoryAuthToggleFunction::Run() { return RespondNow(Error(kErrorDeviceAuthUnavailable)); } - // If `device_authenticator` is not available, then don't do anything. - scoped_refptr<device_reauth::DeviceAuthenticator> device_authenticator = - client->GetDeviceAuthenticator(); - if (!device_authenticator) { - return RespondNow(Error(kErrorDeviceAuthUnavailable)); - } - - // `device_authenticator` is a scoped_refptr, so we need to keep it alive - // until the callback that uses it is complete. - base::OnceClosure bind_device_authenticator = - base::DoNothingWithBoundArgs(device_authenticator); const std::u16string message = l10n_util::GetStringUTF16(IDS_PAYMENTS_AUTOFILL_MANDATORY_REAUTH_PROMPT); + // If `personal_data_manager` is not available or `IsDataLoaded` is false, + // then don't do anything. + autofill::PersonalDataManager* personal_data_manager = + client->GetPersonalDataManager(); + if (!personal_data_manager || !personal_data_manager->IsDataLoaded()) { + return RespondNow(Error(kErrorDataUnavailable)); + } + // We will be modifying the pref `kAutofillPaymentMethodsMandatoryReauth` // asynchronously. The pref value directly correlates to the mandatory auth // toggle. - autofill_util::AuthenticateUser( - device_authenticator, message, + // We are also logging the start of the auth flow and + // `!personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled()` denotes + // if the user is either opting in or out. + base::RecordAction(base::UserMetricsAction( + "PaymentsUserAuthTriggeredForMandatoryAuthToggle")); + LogMandatoryReauthOptInOrOutUpdateEvent( + MandatoryReauthOptInOrOutSource::kSettingsPage, + /*opt_in=*/ + !personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled(), + MandatoryReauthAuthenticationFlowEvent::kFlowStarted); + client->GetOrCreatePaymentsMandatoryReauthManager()->AuthenticateWithMessage( + message, base::BindOnce( &AutofillPrivateAuthenticateUserAndFlipMandatoryAuthToggleFunction:: UpdateMandatoryAuthTogglePref, - this) - .Then(base::IgnoreArgs(std::move(bind_device_authenticator)))); - base::RecordAction(base::UserMetricsAction( - "PaymentsUserAuthTriggeredForMandatoryAuthToggle")); + this)); + return RespondNow(NoArguments()); #else return RespondNow(Error(kErrorDeviceAuthUnavailable)); #endif // BUILDFLAG (IS_MAC) || BUILDFLAG(IS_WIN) } -// Update the Mandatory auth toggle pref after a successful user auth. +// Update the Mandatory auth toggle pref and log whether the auth was successful +// or not. void AutofillPrivateAuthenticateUserAndFlipMandatoryAuthToggleFunction:: UpdateMandatoryAuthTogglePref(bool reauth_succeeded) { #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - if (reauth_succeeded && browser_context()) { - PrefService* prefs = - Profile::FromBrowserContext(browser_context())->GetPrefs(); - autofill::prefs::SetPaymentMethodsMandatoryReauthEnabled( - prefs, !prefs->GetBoolean( - autofill::prefs::kAutofillPaymentMethodsMandatoryReauth)); + content::WebContents* sender_web_contents = GetSenderWebContents(); + if (!sender_web_contents) { + return; + } + autofill::ContentAutofillClient* client = + autofill::ContentAutofillClient::FromWebContents(sender_web_contents); + CHECK(client); + autofill::PersonalDataManager* personal_data_manager = + client->GetPersonalDataManager(); + // This function is not called in incognito mode and therefore a + // PersonalDataManager should always exist. + CHECK(personal_data_manager); + + // `opt_in` bool denotes whether the user is trying to opt in or out of the + // mandatory reauth feature. If the mandatory reauth toggle on the settings is + // currently enabled, then the `opt_in` bool will be false because the user is + // opting-out, otherwise the `opt_in` bool will be true. + const bool opt_in = + !personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled(); + LogMandatoryReauthOptInOrOutUpdateEvent( + MandatoryReauthOptInOrOutSource::kSettingsPage, opt_in, + reauth_succeeded ? MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded + : MandatoryReauthAuthenticationFlowEvent::kFlowFailed); + if (reauth_succeeded) { base::RecordAction(base::UserMetricsAction( "PaymentsUserAuthSuccessfulForMandatoryAuthToggle")); + personal_data_manager->SetPaymentMethodsMandatoryReauthEnabled(opt_in); } #endif } @@ -924,44 +791,40 @@ AutofillPrivateAuthenticateUserToEditLocalCardFunction::Run() { return RespondNow(Error(kErrorDataUnavailable)); } if (personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled()) { - // If `device_authenticator` is not available, then don't do anything. - scoped_refptr<device_reauth::DeviceAuthenticator> device_authenticator = - client->GetDeviceAuthenticator(); - if (!device_authenticator) { - return RespondNow(Error(kErrorDeviceAuthUnavailable)); - } - - // `device_authenticator` is a scoped_refptr, so we need to keep it alive - // until the callback that uses it is complete. - base::OnceClosure bind_device_authenticator = - base::DoNothingWithBoundArgs(device_authenticator); const std::u16string message = l10n_util::GetStringUTF16( IDS_PAYMENTS_AUTOFILL_EDIT_CARD_MANDATORY_REAUTH_PROMPT); base::RecordAction(base::UserMetricsAction( "PaymentsUserAuthTriggeredToShowEditLocalCardDialog")); + LogMandatoryReauthSettingsPageEditCardEvent( + MandatoryReauthAuthenticationFlowEvent::kFlowStarted); // Based on the result of the auth, we will be asynchronously returning if // the user can edit the local card. - autofill_util::AuthenticateUser( - device_authenticator, message, - base::BindOnce(&AutofillPrivateAuthenticateUserToEditLocalCardFunction:: - CanShowEditDialogForLocalCard, - this) - .Then(base::IgnoreArgs(std::move(bind_device_authenticator)))); - - // Due to async nature of AuthenticateWithMessage() on device authenticator - // we use the below check to make sure we have a `Respond` captured. If we - // didn't have this check, then we would show the edit card dialog box even - // before the user successfully completes the auth. + client->GetOrCreatePaymentsMandatoryReauthManager() + ->AuthenticateWithMessage( + message, + base::BindOnce( + &AutofillPrivateAuthenticateUserToEditLocalCardFunction:: + CanShowEditDialogForLocalCard, + this)); + + // Due to async nature of AuthenticateWithMessage() on mandatory re-auth + // manager we use the below check to make sure we have a `Respond` captured. + // If we didn't have this check, then we would show the edit card dialog box + // even before the user successfully completes the auth. return did_respond() ? AlreadyResponded() : RespondLater(); } #endif return RespondNow(WithArguments(true)); } -// Return the auth result for showing the edit card for local card. +// Return the auth result for showing the edit card dialog for local card. We +// also log whether the auth was successful or not. void AutofillPrivateAuthenticateUserToEditLocalCardFunction:: CanShowEditDialogForLocalCard(bool can_show) { + LogMandatoryReauthSettingsPageEditCardEvent( + can_show ? MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded + : MandatoryReauthAuthenticationFlowEvent::kFlowFailed); if (can_show) { base::RecordAction(base::UserMetricsAction( "PaymentsUserAuthSuccessfulToShowEditLocalCardDialog")); @@ -969,4 +832,20 @@ void AutofillPrivateAuthenticateUserToEditLocalCardFunction:: Respond(WithArguments(can_show)); } +//////////////////////////////////////////////////////////////////////////////// +// AutofillPrivateCheckIfDeviceAuthAvailableFunction + +ExtensionFunction::ResponseAction +AutofillPrivateCheckIfDeviceAuthAvailableFunction::Run() { +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + autofill::ContentAutofillClient* client = + autofill::ContentAutofillClient::FromWebContents(GetSenderWebContents()); + if (client) { + return RespondNow(WithArguments( + autofill::IsDeviceAuthAvailable(client->GetDeviceAuthenticator()))); + } +#endif // BUILDFLAG (IS_MAC) || BUILDFLAG(IS_WIN) + return RespondNow(Error(kErrorDeviceAuthUnavailable)); +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h index 8e067366973..73315366dee 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h @@ -129,23 +129,6 @@ class AutofillPrivateRemoveEntryFunction : public ExtensionFunction { ResponseAction Run() override; }; -class AutofillPrivateValidatePhoneNumbersFunction : public ExtensionFunction { - public: - AutofillPrivateValidatePhoneNumbersFunction() = default; - AutofillPrivateValidatePhoneNumbersFunction( - const AutofillPrivateValidatePhoneNumbersFunction&) = delete; - AutofillPrivateValidatePhoneNumbersFunction& operator=( - const AutofillPrivateValidatePhoneNumbersFunction&) = delete; - DECLARE_EXTENSION_FUNCTION("autofillPrivate.validatePhoneNumbers", - AUTOFILLPRIVATE_VALIDATEPHONENUMBERS) - - protected: - ~AutofillPrivateValidatePhoneNumbersFunction() override = default; - - // ExtensionFunction overrides. - ResponseAction Run() override; -}; - class AutofillPrivateMaskCreditCardFunction : public ExtensionFunction { public: AutofillPrivateMaskCreditCardFunction() = default; @@ -286,23 +269,6 @@ class AutofillPrivateIsValidIbanFunction : public ExtensionFunction { ResponseAction Run() override; }; -class AutofillPrivateGetUpiIdListFunction : public ExtensionFunction { - public: - AutofillPrivateGetUpiIdListFunction() = default; - AutofillPrivateGetUpiIdListFunction( - const AutofillPrivateGetUpiIdListFunction&) = delete; - AutofillPrivateGetUpiIdListFunction& operator=( - const AutofillPrivateGetUpiIdListFunction&) = delete; - DECLARE_EXTENSION_FUNCTION("autofillPrivate.getUpiIdList", - AUTOFILLPRIVATE_GETUPIIDLIST) - - protected: - ~AutofillPrivateGetUpiIdListFunction() override = default; - - // ExtensionFunction overrides. - ResponseAction Run() override; -}; - class AutofillPrivateAddVirtualCardFunction : public ExtensionFunction { public: AutofillPrivateAddVirtualCardFunction() = default; @@ -383,6 +349,24 @@ class AutofillPrivateAuthenticateUserToEditLocalCardFunction void CanShowEditDialogForLocalCard(bool can_show); }; +class AutofillPrivateCheckIfDeviceAuthAvailableFunction + : public ExtensionFunction { + public: + AutofillPrivateCheckIfDeviceAuthAvailableFunction() = default; + AutofillPrivateCheckIfDeviceAuthAvailableFunction( + const AutofillPrivateCheckIfDeviceAuthAvailableFunction&) = delete; + AutofillPrivateCheckIfDeviceAuthAvailableFunction& operator=( + const AutofillPrivateCheckIfDeviceAuthAvailableFunction&) = delete; + DECLARE_EXTENSION_FUNCTION("autofillPrivate.checkIfDeviceAuthAvailable", + AUTOFILLPRIVATE_CHECKIFDEVICEAUTHAVAILABLE) + + protected: + ~AutofillPrivateCheckIfDeviceAuthAvailableFunction() override = default; + + // ExtensionFunction overrides. + ResponseAction Run() override; +}; + } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_API_H_ diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api_unittest.cc new file mode 100644 index 00000000000..58101af4364 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api_unittest.cc @@ -0,0 +1,140 @@ +// Copyright 2023 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/extensions/api/autofill_private/autofill_private_api.h" + +#include <vector> + +#include "chrome/browser/autofill/autofill_uitest_util.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/ui/autofill/chrome_autofill_client.h" +#include "components/autofill/content/browser/test_autofill_client_injector.h" +#include "components/autofill/content/browser/test_content_autofill_client.h" +#include "components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h" +#include "components/autofill/core/common/autofill_prefs.h" +#include "components/device_reauth/mock_device_authenticator.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_test.h" + +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) +using autofill::autofill_metrics::MandatoryReauthAuthenticationFlowEvent; + +// There are 2 boolean params set in the test suites. +// The first param can be retrieved via `IsFeatureTurnedOn()` which determines +// if the toggle is currently turned on or off. The second param can be +// retrieved via `IsUserAuthSuccessful()` which determines if the user auth was +// successful or not. +class MandatoryReauthSettingsPageMetricsTest + : public extensions::ExtensionApiTest, + public testing::WithParamInterface<std::tuple<bool, bool>> { + public: + MandatoryReauthSettingsPageMetricsTest() = default; + MandatoryReauthSettingsPageMetricsTest( + const MandatoryReauthSettingsPageMetricsTest&) = delete; + MandatoryReauthSettingsPageMetricsTest& operator=( + const MandatoryReauthSettingsPageMetricsTest&) = delete; + ~MandatoryReauthSettingsPageMetricsTest() override = default; + + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + autofill_client()->GetPersonalDataManager()->SetPrefService( + autofill_client()->GetPrefs()); + autofill_client() + ->GetPersonalDataManager() + ->SetPaymentMethodsMandatoryReauthEnabled(IsFeatureTurnedOn()); + } + + bool IsFeatureTurnedOn() const { return std::get<0>(GetParam()); } + + bool IsUserAuthSuccessful() const { return std::get<1>(GetParam()); } + + protected: + bool RunAutofillSubtest(const std::string& subtest) { + autofill::WaitForPersonalDataManagerToBeLoaded(profile()); + + const std::string extension_url = "main.html?" + subtest; + return RunExtensionTest("autofill_private", + {.extension_url = extension_url.c_str()}, + {.load_as_component = true}); + } + + autofill::TestContentAutofillClient* autofill_client() { + return test_autofill_client_injector_ + [browser()->tab_strip_model()->GetActiveWebContents()]; + } + + private: + autofill::TestAutofillClientInjector<autofill::TestContentAutofillClient> + test_autofill_client_injector_; +}; + +// This tests the logging for mandatory reauth opt-in / opt-out flows when +// triggered from the settings page. +IN_PROC_BROWSER_TEST_P(MandatoryReauthSettingsPageMetricsTest, + SettingsPageMandatoryReauthToggleSwitching) { + base::HistogramTester histogram_tester; + + ON_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>( + autofill_client()->GetOrCreatePaymentsMandatoryReauthManager()), + AuthenticateWithMessage) + .WillByDefault( + testing::WithArg<1>([auth_success = IsUserAuthSuccessful()]( + base::OnceCallback<void(bool)> callback) { + std::move(callback).Run(auth_success); + })); + + RunAutofillSubtest("authenticateUserAndFlipMandatoryAuthToggle"); + + std::string histogram_name = base::StrCat( + {"Autofill.PaymentMethods.MandatoryReauth.OptChangeEvent.SettingsPage.", + IsFeatureTurnedOn() ? "OptOut" : "OptIn"}); + + EXPECT_THAT( + histogram_tester.GetAllSamples(histogram_name), + testing::ElementsAre( + base::Bucket(MandatoryReauthAuthenticationFlowEvent::kFlowStarted, 1), + base::Bucket( + IsUserAuthSuccessful() + ? MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded + : MandatoryReauthAuthenticationFlowEvent::kFlowFailed, + 1))); +} + +IN_PROC_BROWSER_TEST_P(MandatoryReauthSettingsPageMetricsTest, + SettingsPageMandatoryReauthEditLocalCard) { + base::HistogramTester histogram_tester; + + ON_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>( + autofill_client()->GetOrCreatePaymentsMandatoryReauthManager()), + AuthenticateWithMessage) + .WillByDefault( + testing::WithArg<1>([auth_success = IsUserAuthSuccessful()]( + base::OnceCallback<void(bool)> callback) { + std::move(callback).Run(auth_success); + })); + + RunAutofillSubtest("authenticateUserToEditLocalCard"); + + std::string histogram_name = + "Autofill.PaymentMethods.MandatoryReauth.AuthEvent.SettingsPage.EditCard"; + + std::vector<base::Bucket> expected_histogram_buckets; + if (IsFeatureTurnedOn()) { + expected_histogram_buckets = { + base::Bucket(MandatoryReauthAuthenticationFlowEvent::kFlowStarted, 1), + base::Bucket( + IsUserAuthSuccessful() + ? MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded + : MandatoryReauthAuthenticationFlowEvent::kFlowFailed, + 1)}; + } + + EXPECT_EQ(histogram_tester.GetAllSamples(histogram_name), + expected_histogram_buckets); +} + +INSTANTIATE_TEST_SUITE_P(, + MandatoryReauthSettingsPageMetricsTest, + testing::Combine(testing::Bool(), testing::Bool())); +#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc index cff47530b0d..8aaeda25f32 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc @@ -6,8 +6,8 @@ #include <memory> -#include "base/allocator/partition_allocator/pointers/raw_ptr.h" #include "base/command_line.h" +#include "base/memory/raw_ptr.h" #include "base/test/metrics/user_action_tester.h" #include "base/values.h" #include "build/build_config.h" @@ -18,10 +18,10 @@ #include "components/autofill/content/browser/test_autofill_client_injector.h" #include "components/autofill/content/browser/test_content_autofill_client.h" #include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/payments/test/mock_mandatory_reauth_manager.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/test_personal_data_manager.h" #include "components/autofill/core/common/autofill_prefs.h" -#include "components/device_reauth/mock_device_authenticator.h" #include "components/keyed_service/core/keyed_service.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_utils.h" @@ -45,6 +45,8 @@ class AutofillPrivateApiTest : public ExtensionApiTest { void SetUpOnMainThread() override { ExtensionApiTest::SetUpOnMainThread(); content::RunAllPendingInMessageLoop(); + autofill_client()->GetPersonalDataManager()->SetPrefService( + autofill_client()->GetPrefs()); } protected: @@ -62,9 +64,6 @@ class AutofillPrivateApiTest : public ExtensionApiTest { [browser()->tab_strip_model()->GetActiveWebContents()]; } - std::unique_ptr<autofill::TestPersonalDataManager> - test_personal_data_manager_; - private: autofill::TestAutofillClientInjector<autofill::TestContentAutofillClient> test_autofill_client_injector_; @@ -86,10 +85,6 @@ IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, RemoveEntry) { EXPECT_TRUE(RunAutofillSubtest("removeEntry")) << message_; } -IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, ValidatePhoneNumbers) { - EXPECT_TRUE(RunAutofillSubtest("validatePhoneNumbers")) << message_; -} - IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, AddAndUpdateAddress) { EXPECT_TRUE(RunAutofillSubtest("addAndUpdateAddress")) << message_; } @@ -153,19 +148,20 @@ IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, isValidIban) { IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, authenticateUserAndFlipMandatoryAuthToggle) { base::UserActionTester user_action_tester; - auto mock_device_authenticator = autofill_client()->GetDeviceAuthenticator(); + auto* mock_mandatory_reauth_manager = + autofill_client()->GetOrCreatePaymentsMandatoryReauthManager(); - ON_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>( - mock_device_authenticator.get()), + ON_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>( + mock_mandatory_reauth_manager), AuthenticateWithMessage) .WillByDefault( testing::WithArg<1>([](base::OnceCallback<void(bool)> callback) { std::move(callback).Run(true); })); - EXPECT_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>( - mock_device_authenticator.get()), - AuthenticateWithMessage(testing::_, testing::_)) + EXPECT_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>( + mock_mandatory_reauth_manager), + AuthenticateWithMessage) .Times(1); EXPECT_TRUE(RunAutofillSubtest("authenticateUserAndFlipMandatoryAuthToggle")) << message_; @@ -182,19 +178,20 @@ IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, autofill_client() ->GetPersonalDataManager() ->SetPaymentMethodsMandatoryReauthEnabled(true); - auto mock_device_authenticator = autofill_client()->GetDeviceAuthenticator(); + auto* mock_mandatory_reauth_manager = + autofill_client()->GetOrCreatePaymentsMandatoryReauthManager(); - ON_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>( - mock_device_authenticator.get()), + ON_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>( + mock_mandatory_reauth_manager), AuthenticateWithMessage) .WillByDefault( testing::WithArg<1>([](base::OnceCallback<void(bool)> callback) { std::move(callback).Run(true); })); - EXPECT_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>( - mock_device_authenticator.get()), - AuthenticateWithMessage(testing::_, testing::_)) + EXPECT_CALL(*static_cast<autofill::payments::MockMandatoryReauthManager*>( + mock_mandatory_reauth_manager), + AuthenticateWithMessage) .Times(1); EXPECT_TRUE(RunAutofillSubtest("authenticateUserToEditLocalCard")) << message_; diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc index 9380de9af1b..44fd4d2a245 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc @@ -100,9 +100,4 @@ void AutofillPrivateEventRouter::BroadcastCurrentData() { event_router_->BroadcastEvent(std::move(extension_event)); } -AutofillPrivateEventRouter* AutofillPrivateEventRouter::Create( - content::BrowserContext* context) { - return new AutofillPrivateEventRouter(context); -} - } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h index d8e01b94c14..7685c65d86a 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h @@ -27,16 +27,14 @@ class AutofillPrivateEventRouter : public EventRouter::Observer, public autofill::PersonalDataManagerObserver { public: - static AutofillPrivateEventRouter* Create( - content::BrowserContext* browser_context); + // Uses AutofillPrivateEventRouterFactory instead. + explicit AutofillPrivateEventRouter(content::BrowserContext* context); AutofillPrivateEventRouter(const AutofillPrivateEventRouter&) = delete; AutofillPrivateEventRouter& operator=(const AutofillPrivateEventRouter&) = delete; ~AutofillPrivateEventRouter() override = default; protected: - explicit AutofillPrivateEventRouter(content::BrowserContext* context); - // KeyedService overrides: void Shutdown() override; diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc index 79d934c0ee2..674ecfec9b2 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc @@ -40,10 +40,11 @@ AutofillPrivateEventRouterFactory::AutofillPrivateEventRouterFactory() DependsOn(autofill::PersonalDataManagerFactory::GetInstance()); } -KeyedService* AutofillPrivateEventRouterFactory::BuildServiceInstanceFor( +std::unique_ptr<KeyedService> +AutofillPrivateEventRouterFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const { // TODO(1426498): pass router's dependencies directly instead of context. - return AutofillPrivateEventRouter::Create(context); + return std::make_unique<AutofillPrivateEventRouter>(context); } bool AutofillPrivateEventRouterFactory:: diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h index 45ba793c6ad..3068c41f1ed 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h @@ -41,7 +41,7 @@ class AutofillPrivateEventRouterFactory : public ProfileKeyedServiceFactory { ~AutofillPrivateEventRouterFactory() override = default; // BrowserContextKeyedServiceFactory: - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const override; }; diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc index b06f9f98d19..a630230b9e7 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc @@ -93,26 +93,32 @@ autofill_private::AddressEntry ProfileToAddressEntry( // Add all address fields to the entry. address.guid = profile.guid(); - address.full_names = GetList(profile, autofill::NAME_FULL); - address.honorific = - GetStringFromProfile(profile, autofill::NAME_HONORIFIC_PREFIX); - address.company_name = GetStringFromProfile(profile, autofill::COMPANY_NAME); - address.address_lines = - GetStringFromProfile(profile, autofill::ADDRESS_HOME_STREET_ADDRESS); - address.address_level1 = - GetStringFromProfile(profile, autofill::ADDRESS_HOME_STATE); - address.address_level2 = - GetStringFromProfile(profile, autofill::ADDRESS_HOME_CITY); - address.address_level3 = - GetStringFromProfile(profile, autofill::ADDRESS_HOME_DEPENDENT_LOCALITY); - address.postal_code = - GetStringFromProfile(profile, autofill::ADDRESS_HOME_ZIP); - address.sorting_code = - GetStringFromProfile(profile, autofill::ADDRESS_HOME_SORTING_CODE); - address.country_code = - GetStringFromProfile(profile, autofill::ADDRESS_HOME_COUNTRY); - address.phone_numbers = GetList(profile, autofill::PHONE_HOME_WHOLE_NUMBER); - address.email_addresses = GetList(profile, autofill::EMAIL_ADDRESS); + + // TODO(crbug.com/1441904): provide all available fields instead of the hard + // coded list of fields. + std::vector<autofill::ServerFieldType> field_types = { + autofill::NAME_FULL, + autofill::NAME_HONORIFIC_PREFIX, + autofill::COMPANY_NAME, + autofill::ADDRESS_HOME_STREET_ADDRESS, + autofill::ADDRESS_HOME_STATE, + autofill::ADDRESS_HOME_CITY, + autofill::ADDRESS_HOME_DEPENDENT_LOCALITY, + autofill::ADDRESS_HOME_ZIP, + autofill::ADDRESS_HOME_SORTING_CODE, + autofill::ADDRESS_HOME_COUNTRY, + autofill::PHONE_HOME_WHOLE_NUMBER, + autofill::EMAIL_ADDRESS}; + + base::ranges::transform( + field_types, back_inserter(address.fields), [&profile](auto field_type) { + autofill_private::AddressField field; + field.type = autofill_private::ParseServerFieldType( + FieldTypeToStringPiece(field_type)); + field.value = GetStringFromProfile(profile, field_type); + return field; + }); + address.language_code = profile.language_code(); // Parse |label| so that it can be used to create address metadata. @@ -200,9 +206,10 @@ autofill_private::CreditCardEntry CreditCardToCreditCardEntry( autofill_private::CreditCardEntry card; // Add all credit card fields to the entry. - card.guid = credit_card.record_type() == autofill::CreditCard::LOCAL_CARD - ? credit_card.guid() - : credit_card.server_id(); + card.guid = + credit_card.record_type() == autofill::CreditCard::RecordType::kLocalCard + ? credit_card.guid() + : credit_card.server_id(); card.name = base::UTF16ToUTF8( credit_card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL)); card.card_number = @@ -232,9 +239,9 @@ autofill_private::CreditCardEntry CreditCardToCreditCardEntry( card.metadata->summary_label = base::UTF16ToUTF8(label_pieces.first); card.metadata->summary_sublabel = base::UTF16ToUTF8(label_pieces.second); card.metadata->is_local = - credit_card.record_type() == autofill::CreditCard::LOCAL_CARD; - card.metadata->is_cached = - credit_card.record_type() == autofill::CreditCard::FULL_SERVER_CARD; + credit_card.record_type() == autofill::CreditCard::RecordType::kLocalCard; + card.metadata->is_cached = credit_card.record_type() == + autofill::CreditCard::RecordType::kFullServerCard; // IsValid() checks if both card number and expiration date are valid. // IsServerCard() checks whether there is a duplicated server card in // |personal_data|. @@ -242,19 +249,19 @@ autofill_private::CreditCardEntry CreditCardToCreditCardEntry( credit_card.IsValid() && !personal_data.IsServerCard(&credit_card); card.metadata->is_virtual_card_enrollment_eligible = credit_card.virtual_card_enrollment_state() == - autofill::CreditCard::VirtualCardEnrollmentState::ENROLLED || + autofill::CreditCard::VirtualCardEnrollmentState::kEnrolled || credit_card.virtual_card_enrollment_state() == autofill::CreditCard::VirtualCardEnrollmentState:: - UNENROLLED_AND_ELIGIBLE; + kUnenrolledAndEligible; card.metadata->is_virtual_card_enrolled = credit_card.virtual_card_enrollment_state() == - autofill::CreditCard::VirtualCardEnrollmentState::ENROLLED; + autofill::CreditCard::VirtualCardEnrollmentState::kEnrolled; return card; } autofill_private::IbanEntry IbanToIbanEntry( - const autofill::IBAN& iban, + const autofill::Iban& iban, const autofill::PersonalDataManager& personal_data) { autofill_private::IbanEntry iban_entry; @@ -326,8 +333,9 @@ CreditCardEntryList GenerateCreditCardList( IbanEntryList GenerateIbanList( const autofill::PersonalDataManager& personal_data) { IbanEntryList list; - for (const autofill::IBAN* iban : personal_data.GetLocalIBANs()) + for (const autofill::Iban* iban : personal_data.GetLocalIbans()) { list.push_back(IbanToIbanEntry(*iban, personal_data)); + } return list; } @@ -343,7 +351,7 @@ absl::optional<api::autofill_private::AccountInfo> GetAccountInfo( api::autofill_private::AccountInfo api_account; api_account.email = account->email; api_account.is_sync_enabled_for_autofill_profiles = - personal_data.IsSyncEnabledFor(syncer::UserSelectableType::kAutofill); + personal_data.IsSyncFeatureEnabledForAutofill(); api_account.is_eligible_for_address_account_storage = personal_data.IsEligibleForAddressAccountStorage(); return std::move(api_account); diff --git a/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc b/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc index 61db738439f..e7c8aa0b93d 100644 --- a/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc +++ b/chromium/chrome/browser/extensions/api/automation/automation_apitest.cc @@ -49,6 +49,7 @@ #include "ash/public/cpp/accelerators.h" #include "ash/public/cpp/test/shell_test_api.h" #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" +#include "ui/accessibility/ax_action_handler_registry.h" #include "ui/display/manager/display_manager.h" #include "ui/display/screen.h" #include "ui/display/test/display_manager_test_api.h" // nogncheck @@ -56,13 +57,20 @@ namespace extensions { -namespace { -static const char kDomain[] = "a.com"; -static const char kSitesDir[] = "automation/sites"; -static const char kGotTree[] = "got_tree"; -} // anonymous namespace - class AutomationApiTest : public ExtensionApiTest { + public: + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + host_resolver()->AddRule("*", "127.0.0.1"); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + extensions::ExtensionApiTest::SetUpCommandLine(command_line); + command_line->AppendSwitchASCII( + extensions::switches::kAllowlistedExtensionID, + "ddchlicdkolnonkihahngkmmmjnjlkkf"); + } + protected: GURL GetURLForPath(const std::string& host, const std::string& path) { std::string port = base::NumberToString(embedded_test_server()->port()); @@ -75,6 +83,7 @@ class AutomationApiTest : public ExtensionApiTest { } void StartEmbeddedTestServer() { + static const char kSitesDir[] = "automation/sites"; base::FilePath test_data; ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data)); embedded_test_server()->ServeFilesFromDirectory( @@ -82,19 +91,6 @@ class AutomationApiTest : public ExtensionApiTest { ASSERT_TRUE(ExtensionApiTest::StartEmbeddedTestServer()); } - public: - void SetUpOnMainThread() override { - ExtensionApiTest::SetUpOnMainThread(); - host_resolver()->AddRule("*", "127.0.0.1"); - } - - void SetUpCommandLine(base::CommandLine* command_line) override { - extensions::ExtensionApiTest::SetUpCommandLine(command_line); - command_line->AppendSwitchASCII( - extensions::switches::kAllowlistedExtensionID, - "ddchlicdkolnonkihahngkmmmjnjlkkf"); - } - base::test::ScopedFeatureList scoped_feature_list_; }; @@ -108,6 +104,13 @@ class AutomationApiCanvasTest : public AutomationApiTest { } }; +#if defined(USE_AURA) + +namespace { +static const char kDomain[] = "a.com"; +static const char kGotTree[] = "got_tree"; +} // anonymous namespace + IN_PROC_BROWSER_TEST_F(AutomationApiTest, TestRendererAccessibilityEnabled) { StartEmbeddedTestServer(); const GURL url = GetURLForPath(kDomain, "/index.html"); @@ -170,19 +173,6 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, ImageLabels) { EXPECT_EQ(expected_mode, accessibility_mode); } -// Flaky on Mac: crbug.com/1248445 -#if BUILDFLAG(IS_MAC) -#define MAYBE_GetTreeByTabId DISABLED_GetTreeByTabId -#else -#define MAYBE_GetTreeByTabId GetTreeByTabId -#endif -IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_GetTreeByTabId) { - StartEmbeddedTestServer(); - ASSERT_TRUE(RunExtensionTest("automation/tests/tabs", - {.extension_url = "tab_id.html"})) - << message_; -} - IN_PROC_BROWSER_TEST_F(AutomationApiTest, Events) { StartEmbeddedTestServer(); ASSERT_TRUE(RunExtensionTest("automation/tests/tabs", @@ -247,50 +237,6 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, TableProperties) { // Flaky on Mac and Windows: crbug.com/1235249 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) -#define MAYBE_TabsAutomationBooleanPermissions \ - DISABLED_TabsAutomationBooleanPermissions -#else -#define MAYBE_TabsAutomationBooleanPermissions TabsAutomationBooleanPermissions -#endif -IN_PROC_BROWSER_TEST_F(AutomationApiTest, - MAYBE_TabsAutomationBooleanPermissions) { - StartEmbeddedTestServer(); - ASSERT_TRUE(RunExtensionTest("automation/tests/tabs_automation_boolean", - {.extension_url = "permissions.html"})) - << message_; -} - -// Flaky on Mac and Windows: crbug.com/1235249 -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) -#define MAYBE_TabsAutomationBooleanActions \ - DISABLED_TabsAutomationBooleanActions -#else -#define MAYBE_TabsAutomationBooleanActions TabsAutomationBooleanActions -#endif -IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_TabsAutomationBooleanActions) { - StartEmbeddedTestServer(); - ASSERT_TRUE(RunExtensionTest("automation/tests/tabs_automation_boolean", - {.extension_url = "actions.html"})) - << message_; -} - -// Flaky on Mac and Windows: crbug.com/1202710 -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) -#define MAYBE_TabsAutomationHostsPermissions \ - DISABLED_TabsAutomationHostsPermissions -#else -#define MAYBE_TabsAutomationHostsPermissions TabsAutomationHostsPermissions -#endif -IN_PROC_BROWSER_TEST_F(AutomationApiTest, - MAYBE_TabsAutomationHostsPermissions) { - StartEmbeddedTestServer(); - ASSERT_TRUE(RunExtensionTest("automation/tests/tabs_automation_hosts", - {.extension_url = "permissions.html"})) - << message_; -} - -// Flaky on Mac and Windows: crbug.com/1235249 -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) #define MAYBE_CloseTab DISABLED_CloseTab #else #define MAYBE_CloseTab CloseTab @@ -302,13 +248,6 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_CloseTab) { << message_; } -IN_PROC_BROWSER_TEST_F(AutomationApiTest, QuerySelector) { - StartEmbeddedTestServer(); - ASSERT_TRUE(RunExtensionTest("automation/tests/tabs", - {.extension_url = "queryselector.html"})) - << message_; -} - IN_PROC_BROWSER_TEST_F(AutomationApiTest, Find) { StartEmbeddedTestServer(); ASSERT_TRUE( @@ -418,12 +357,6 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, EnumValidity) { << message_; } -#if defined(USE_AURA) -IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopNotRequested) { - ASSERT_TRUE(RunExtensionTest("automation/tests/tabs", - {.extension_url = "desktop_not_requested.html"})) - << message_; -} #endif // defined(USE_AURA) #if !defined(USE_AURA) @@ -704,6 +637,47 @@ IN_PROC_BROWSER_TEST_F(AutomationApiTest, MAYBE_AddRemoveEventListeners) { {.extension_url = "add_remove_event_listeners.html"})) << message_; } + +class AutomationApiTestWithMockedSourceRenderer + : public AutomationApiTest, + public ui::AXActionHandlerObserver { + protected: + // This method is used to intercept AXActions dispatched from extensions. + // Because `DispatchActionResult`, from the automation API, is only used in + // specific source renderers (e.g. arc++), we mock the behavior here so we can + // test that the behavior in the automation api works correctly. + void InterceptAXActions() { + ui::AXActionHandlerRegistry* registry = + ui::AXActionHandlerRegistry ::GetInstance(); + ASSERT_TRUE(registry); + registry->AddObserver(this); + } + + private: + // ui::AXActionHandlerObserver : + void PerformAction(const ui::AXActionData& action_data) override { + extensions::AutomationEventRouter* router = + extensions::AutomationEventRouter::GetInstance(); + ASSERT_TRUE(router); + EXPECT_EQ(action_data.action, ax::mojom::Action::kScrollBackward); + router->DispatchActionResult(action_data, /*result=*/true); + } +}; + +IN_PROC_BROWSER_TEST_F(AutomationApiTestWithMockedSourceRenderer, + ActionResult) { + StartEmbeddedTestServer(); + + // Intercept AXActions for this test in order to test the behavior of + // DispatchActionResult. Here, we mock the action logic to always return true + // to return to the extension test that the action was handled and that the + // result is true. This will make sure that the passing of messages between + // processes is correct. + InterceptAXActions(); + ASSERT_TRUE(RunExtensionTest("automation/tests/desktop", + {.extension_url = "action_result.html"})) + << message_; +} #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(IS_CHROMEOS) diff --git a/chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc b/chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc index 1b18b129b2e..1190aa6f7b3 100644 --- a/chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc +++ b/chromium/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc @@ -20,6 +20,7 @@ #include "extensions/common/extension.h" #include "extensions/common/manifest_handlers/automation.h" #include "extensions/common/permissions/permissions_data.h" +#include "ui/accessibility/ax_tree_id.h" #if defined(USE_AURA) #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" diff --git a/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc b/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc index 576e97007ce..bbcf1379af1 100644 --- a/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc @@ -68,7 +68,8 @@ class BluetoothLowEnergyApiTestChromeOs : public PlatformAppBrowserTest { ash::KioskAppManager* manager() const { return ash::KioskAppManager::Get(); } - raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_; + raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh> + fake_user_manager_; std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_; ash::ScopedCrosSettingsTestHelper settings_helper_; diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc index c6f74309f73..3b7a1df74d6 100644 --- a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc +++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc @@ -284,10 +284,8 @@ void BookmarkEventRouter::BookmarkNodeRemoved( void BookmarkEventRouter::BookmarkAllUserNodesRemoved( BookmarkModel* model, const std::set<GURL>& removed_urls) { - NOTREACHED(); - // TODO(shashishekhar) Currently this notification is only used on Android, - // which does not support extensions. If Desktop needs to support this, add - // a new event to the extensions api. + // TODO(crbug.com/1468324): This used to be used only on Android, but that's + // no longer the case. We need to implement a new event to handle this. } void BookmarkEventRouter::BookmarkNodeChanged(BookmarkModel* model, diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc b/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc index 0ded9284474..9fb90a0b518 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc +++ b/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.cc @@ -124,8 +124,9 @@ void BrailleControllerImpl::WriteDots(const std::vector<uint8_t>& cells, unsigned int row_limit = std::min(rows, cells_rows); unsigned int col_limit = std::min(columns, cells_cols); for (unsigned int row = 0; row < row_limit; row++) { - for (unsigned int col = 0; col < col_limit; col++) { - sized_cells[row * columns + col] = cells[row * cells_cols + col]; + for (unsigned int col = 0; + col < col_limit && (row * columns + col) < cells.size(); col++) { + sized_cells[row * columns + col] = cells[row * columns + col]; } } diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc b/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc index 31034fa1f0d..202e006b80a 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc +++ b/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc @@ -171,9 +171,6 @@ ExtensionFunction::ResponseAction BrailleDisplayPrivateWriteDotsFunction::Run() { params_ = WriteDots::Params::Create(args()); EXTENSION_FUNCTION_VALIDATE(params_); - EXTENSION_FUNCTION_VALIDATE( - params_->cells.size() >= - static_cast<size_t>(params_->columns * params_->rows)); bool did_post_task = content::GetIOThreadTaskRunner({})->PostTaskAndReply( FROM_HERE, diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc b/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc index c447af6d75e..262764c9880 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/braille_display_private/braille_display_private_apitest.cc @@ -141,7 +141,8 @@ class MockBrlapiConnection : public BrlapiConnection { } } - raw_ptr<MockBrlapiConnectionData, ExperimentalAsh> data_; + raw_ptr<MockBrlapiConnectionData, LeakedDanglingUntriaged | ExperimentalAsh> + data_; OnDataReadyCallback on_data_ready_; }; @@ -189,16 +190,59 @@ IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, WriteDots) { ASSERT_TRUE(RunExtensionTest("braille_display_private/write_dots", {}, {.load_as_component = true})) << message_; - ASSERT_EQ(3U, connection_data_.written_content.size()); - const std::string expected_content( - connection_data_.display_columns * connection_data_.display_rows, '\0'); - for (size_t i = 0; i < connection_data_.written_content.size(); ++i) { - ASSERT_EQ(std::string(connection_data_.display_columns * - connection_data_.display_rows, - static_cast<char>(i)), - connection_data_.written_content[i]) - << "String " << i << " doesn't match"; - } + ASSERT_EQ(4U, connection_data_.written_content.size()); + + // testWriteEmptyCells. + EXPECT_EQ(std::string(11, 0), connection_data_.written_content[0]); + + // testWriteOversizedCells. + EXPECT_EQ(std::string(11, 1), connection_data_.written_content[1]); + EXPECT_EQ(std::string(11, 2), connection_data_.written_content[2]); + + // testWriteUndersizedCellsNoCrash. + EXPECT_EQ(std::string(9, 3) + std::string(2, 0), + connection_data_.written_content[3]); +} + +IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, WriteDotsMultiLine) { + connection_data_.display_columns = 20; + connection_data_.display_rows = 7; + connection_data_.cell_size = 6; + ASSERT_TRUE(RunExtensionTest("braille_display_private/write_dots_multi_line", + {}, {.load_as_component = true})) + << message_; + ASSERT_EQ(6U, connection_data_.written_content.size()); + + // testWriteEmptyCells. + EXPECT_EQ(std::string(140, 0), connection_data_.written_content[0]); + EXPECT_EQ(std::string(140, 0), connection_data_.written_content[1]); + EXPECT_EQ(std::string(140, 0), connection_data_.written_content[2]); + + // testWriteOversizedCells. + + // The test passes a grid of cells 19x9 on a display of 20x7. (cols x rows). + // Thus, the last two rows get truncated, and the last column is unfilled + // (0s). + std::string grid; + grid += std::string(19, 1) + std::string(1, 0); + grid += std::string(19, 1) + std::string(1, 0); + grid += std::string(19, 1) + std::string(1, 0); + grid += std::string(19, 1) + std::string(1, 0); + grid += std::string(19, 1) + std::string(1, 0); + grid += std::string(19, 1) + std::string(1, 0); + grid += std::string(19, 1) + std::string(1, 0); + EXPECT_EQ(grid, connection_data_.written_content[3]); + + // This one is 21x8, so only the last row is truncated. + EXPECT_EQ(std::string(140, 2), connection_data_.written_content[4]); + + // testWriteUndersizedCellsNoCrash. + + // 10x2. + std::string grid2 = std::string(10, 3) + std::string(10, 0) + + std::string(10, 3) + std::string(10, 0) + + std::string(100, 0); + EXPECT_EQ(grid2, connection_data_.written_content[5]); } IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, KeyEvents) { diff --git a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc index b3e797e594e..4097a526437 100644 --- a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc +++ b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.cc @@ -96,8 +96,9 @@ static_assert((kFilterableDataTypes & uint64_t MaskForKey(const char* key) { if (strcmp(key, extension_browsing_data_api_constants::kCacheKey) == 0) return content::BrowsingDataRemover::DATA_TYPE_CACHE; - if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0) + if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0) { return content::BrowsingDataRemover::DATA_TYPE_COOKIES; + } if (strcmp(key, extension_browsing_data_api_constants::kDownloadsKey) == 0) return content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS; if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0) @@ -311,18 +312,16 @@ ExtensionFunction::ResponseAction BrowsingDataRemoverFunction::Run() { if (origins) { OriginParsingResult result = ParseOrigins(*origins); - if (result.has_value()) { - origins_ = std::move(*result); - } else { + if (!result.has_value()) { return RespondNow(std::move(result.error())); } + origins_ = std::move(*result); } else if (exclude_origins) { OriginParsingResult result = ParseOrigins(*exclude_origins); - if (result.has_value()) { - origins_ = std::move(*result); - } else { + if (!result.has_value()) { return RespondNow(std::move(result.error())); } + origins_ = std::move(*result); } mode_ = origins ? content::BrowsingDataFilterBuilder::Mode::kDelete : content::BrowsingDataFilterBuilder::Mode::kPreserve; diff --git a/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc b/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc index 9e4879b642d..08cd5f764d7 100644 --- a/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc +++ b/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc @@ -289,7 +289,7 @@ class CertificateProviderApiTest : public extensions::ExtensionApiTest { protected: testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_; - raw_ptr<chromeos::CertificateProviderService, DanglingUntriaged> + raw_ptr<chromeos::CertificateProviderService, AcrossTasksDanglingUntriaged> cert_provider_service_ = nullptr; policy::PolicyMap policy_map_; @@ -490,9 +490,10 @@ class CertificateProviderApiMockedExtensionTest return certificate_data; } - raw_ptr<content::WebContents, DanglingUntriaged> extension_contents_ = - nullptr; - raw_ptr<const extensions::Extension, DanglingUntriaged> extension_ = nullptr; + raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged> + extension_contents_ = nullptr; + raw_ptr<const extensions::Extension, AcrossTasksDanglingUntriaged> + extension_ = nullptr; base::FilePath extension_path_; }; @@ -598,7 +599,8 @@ class CertificateProviderRequestPinTest : public CertificateProviderApiTest { extension_ = LoadExtension(extension_path); } - raw_ptr<const extensions::Extension, DanglingUntriaged> extension_ = nullptr; + raw_ptr<const extensions::Extension, AcrossTasksDanglingUntriaged> + extension_ = nullptr; std::unique_ptr<ExtensionTestMessageListener> command_request_listener_; }; diff --git a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc index 76cfedc631b..4f11c0749d7 100644 --- a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc +++ b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc @@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "base/check.h" #include "base/files/file_path.h" #include "base/functional/bind.h" #include "base/memory/scoped_refptr.h" @@ -36,8 +37,12 @@ #include "chrome/browser/guest_view/mime_handler_view/chrome_mime_handler_view_guest_delegate.h" #include "chrome/browser/guest_view/web_view/chrome_web_view_guest_delegate.h" #include "chrome/browser/guest_view/web_view/chrome_web_view_permission_helper_delegate.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/search/instant_service.h" #include "chrome/browser/search/instant_service_factory.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_navigator.h" +#include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/webui/devtools_ui.h" #include "chrome/common/buildflags.h" #include "chrome/common/url_constants.h" @@ -63,6 +68,8 @@ #include "pdf/buildflags.h" #include "printing/buildflags/buildflags.h" #include "services/network/public/mojom/fetch_api.mojom-shared.h" +#include "ui/base/page_transition_types.h" +#include "ui/base/window_open_disposition.h" #include "url/gurl.h" #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -84,18 +91,11 @@ #include "chrome/browser/extensions/clipboard_extension_helper_chromeos.h" #endif -#if BUILDFLAG(ENABLE_PDF) -#include "chrome/browser/ui/pdf/chrome_pdf_web_contents_helper_client.h" -#include "components/pdf/browser/pdf_web_contents_helper.h" -#endif - #if BUILDFLAG(ENABLE_PRINTING) #include "chrome/browser/printing/printing_init.h" #endif #if BUILDFLAG(ENABLE_SUPERVISED_USERS) -// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build -// flag to #if BUILDFLAG(IS_CHROMEOS) #include "chrome/browser/supervised_user/supervised_user_extensions_delegate_impl.h" #include "chrome/browser/supervised_user/supervised_user_service_factory.h" #endif @@ -126,10 +126,6 @@ void ChromeExtensionsAPIClient::AttachWebContentsHelpers( #if BUILDFLAG(ENABLE_PRINTING) printing::InitializePrintingForWebContents(web_contents); #endif -#if BUILDFLAG(ENABLE_PDF) - pdf::PDFWebContentsHelper::CreateForWebContentsWithClient( - web_contents, std::make_unique<ChromePDFWebContentsHelperClient>()); -#endif } bool ChromeExtensionsAPIClient::ShouldHideResponseHeader( @@ -193,17 +189,19 @@ void ChromeExtensionsAPIClient::NotifyWebRequestWithheld( // Track down the ExtensionActionRunner and the extension. Since this is // asynchronous, we could hit a null anywhere along the path. - content::RenderFrameHost* rfh = + content::RenderFrameHost* render_frame_host = content::RenderFrameHost::FromID(render_process_id, render_frame_id); - if (!rfh) + if (!render_frame_host) { return; + } // We don't count subframes and prerendering blocked actions as yet, since // there's no way to surface this to the user. Ignore these (which is also // what we do for content scripts). - if (!rfh->IsInPrimaryMainFrame()) + if (!render_frame_host->IsInPrimaryMainFrame()) { return; + } content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(rfh); + content::WebContents::FromRenderFrameHost(render_frame_host); if (!web_contents) return; extensions::ExtensionActionRunner* runner = @@ -229,7 +227,7 @@ void ChromeExtensionsAPIClient::NotifyWebRequestWithheld( if (!extension->permissions_data() ->withheld_permissions() .explicit_hosts() - .MatchesURL(rfh->GetLastCommittedURL())) { + .MatchesURL(render_frame_host->GetLastCommittedURL())) { return; } @@ -287,6 +285,20 @@ void ChromeExtensionsAPIClient::ClearActionCount( } } +void ChromeExtensionsAPIClient::OpenFileUrl( + const GURL& file_url, + content::BrowserContext* browser_context) { + CHECK(file_url.is_valid()); + CHECK(file_url.SchemeIsFile()); + Profile* profile = Profile::FromBrowserContext(browser_context); + NavigateParams navigate_params(profile, file_url, + ui::PAGE_TRANSITION_FROM_API); + navigate_params.disposition = WindowOpenDisposition::CURRENT_TAB; + navigate_params.browser = + chrome::FindTabbedBrowser(profile, /*match_original_profiles=*/false); + Navigate(&navigate_params); +} + AppViewGuestDelegate* ChromeExtensionsAPIClient::CreateAppViewGuestDelegate() const { return new ChromeAppViewGuestDelegate(); @@ -299,9 +311,8 @@ ChromeExtensionsAPIClient::CreateExtensionOptionsGuestDelegate( } std::unique_ptr<guest_view::GuestViewManagerDelegate> -ChromeExtensionsAPIClient::CreateGuestViewManagerDelegate( - content::BrowserContext* context) const { - return std::make_unique<ChromeGuestViewManagerDelegate>(context); +ChromeExtensionsAPIClient::CreateGuestViewManagerDelegate() const { + return std::make_unique<ChromeGuestViewManagerDelegate>(); } std::unique_ptr<MimeHandlerViewGuestDelegate> diff --git a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h index e13b68a8713..4d47b0b1e1e 100644 --- a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h +++ b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h @@ -49,12 +49,13 @@ class ChromeExtensionsAPIClient : public ExtensionsAPIClient { bool clear_badge_text) override; void ClearActionCount(content::BrowserContext* context, const Extension& extension) override; + void OpenFileUrl(const GURL& file_url, + content::BrowserContext* browser_context) override; AppViewGuestDelegate* CreateAppViewGuestDelegate() const override; ExtensionOptionsGuestDelegate* CreateExtensionOptionsGuestDelegate( ExtensionOptionsGuest* guest) const override; std::unique_ptr<guest_view::GuestViewManagerDelegate> - CreateGuestViewManagerDelegate( - content::BrowserContext* context) const override; + CreateGuestViewManagerDelegate() const override; std::unique_ptr<MimeHandlerViewGuestDelegate> CreateMimeHandlerViewGuestDelegate( MimeHandlerViewGuest* guest) const override; diff --git a/chromium/chrome/browser/extensions/api/commands/command_service.cc b/chromium/chrome/browser/extensions/api/commands/command_service.cc index f57c3d1897f..294b6ce11fa 100644 --- a/chromium/chrome/browser/extensions/api/commands/command_service.cc +++ b/chromium/chrome/browser/extensions/api/commands/command_service.cc @@ -29,7 +29,6 @@ #include "extensions/browser/extension_function_registry.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_system.h" -#include "extensions/browser/notification_types.h" #include "extensions/common/api/commands/commands_handler.h" #include "extensions/common/command.h" #include "extensions/common/feature_switch.h" diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc index af7c3a8a01f..471ff11a732 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_api.cc @@ -33,8 +33,8 @@ #include "extensions/browser/api/content_settings/content_settings_helpers.h" #include "extensions/browser/api/content_settings/content_settings_service.h" #include "extensions/browser/api/content_settings/content_settings_store.h" -#include "extensions/browser/extension_prefs_scope.h" #include "extensions/browser/extension_util.h" +#include "extensions/common/api/extension_types.h" #include "extensions/common/constants.h" #include "extensions/common/error_utils.h" @@ -47,6 +47,8 @@ namespace pref_helpers = extensions::preference_helpers; namespace { +using extensions::api::types::ChromeSettingScope; + bool RemoveContentType(base::Value::List& args, ContentSettingsType* content_type) { if (args.empty() || !args[0].is_string()) @@ -89,11 +91,11 @@ ContentSettingsContentSettingClearFunction::Run() { return RespondNow(Error(kUnknownErrorDoNotUse)); } - ExtensionPrefsScope scope = kExtensionPrefsScopeRegular; + ChromeSettingScope scope = ChromeSettingScope::kRegular; bool incognito = false; if (params->details.scope == api::content_settings::Scope::kIncognitoSessionOnly) { - scope = kExtensionPrefsScopeIncognitoSessionOnly; + scope = ChromeSettingScope::kIncognitoSessionOnly; incognito = true; } @@ -148,7 +150,7 @@ ContentSettingsContentSettingGetFunction::Run() { return RespondNow(Error(extension_misc::kIncognitoErrorMessage)); HostContentSettingsMap* map; - content_settings::CookieSettings* cookie_settings; + scoped_refptr<content_settings::CookieSettings> cookie_settings; Profile* profile = Profile::FromBrowserContext(browser_context()); if (incognito) { if (!profile->HasPrimaryOTRProfile()) { @@ -158,13 +160,11 @@ ContentSettingsContentSettingGetFunction::Run() { } map = HostContentSettingsMapFactory::GetForProfile( profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)); - cookie_settings = - CookieSettingsFactory::GetForProfile( - profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)) - .get(); + cookie_settings = CookieSettingsFactory::GetForProfile( + profile->GetPrimaryOTRProfile(/*create_if_needed=*/true)); } else { map = HostContentSettingsMapFactory::GetForProfile(profile); - cookie_settings = CookieSettingsFactory::GetForProfile(profile).get(); + cookie_settings = CookieSettingsFactory::GetForProfile(profile); } // TODO(crbug.com/1386190): Consider whether the following check should @@ -284,11 +284,11 @@ ContentSettingsContentSettingSetFunction::Run() { return RespondNow(Error(kUnsupportedEmbeddedException)); } - ExtensionPrefsScope scope = kExtensionPrefsScopeRegular; + ChromeSettingScope scope = ChromeSettingScope::kRegular; bool incognito = false; if (params->details.scope == api::content_settings::Scope::kIncognitoSessionOnly) { - scope = kExtensionPrefsScopeIncognitoSessionOnly; + scope = ChromeSettingScope::kIncognitoSessionOnly; incognito = true; } @@ -307,7 +307,7 @@ ContentSettingsContentSettingSetFunction::Run() { return RespondNow(Error(kIncognitoContextError)); } - if (scope == kExtensionPrefsScopeIncognitoSessionOnly && + if (scope == ChromeSettingScope::kIncognitoSessionOnly && !Profile::FromBrowserContext(browser_context())->HasPrimaryOTRProfile()) { return RespondNow(Error(extension_misc::kIncognitoSessionOnlyErrorMessage)); } diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc index 57dc41aa8f0..3a931619898 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc @@ -33,7 +33,6 @@ #include "components/keep_alive_registry/scoped_keep_alive.h" #include "components/permissions/features.h" #include "components/permissions/permission_manager.h" -#include "components/permissions/permission_result.h" #include "components/prefs/pref_service.h" #include "content/public/browser/notification_service.h" #include "content/public/common/content_switches.h" @@ -245,7 +244,7 @@ class ExtensionContentSettingsApiTest : public ExtensionApiTest { } private: - raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr; + raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_ = nullptr; std::unique_ptr<ScopedKeepAlive> keep_alive_; std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_; }; diff --git a/chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc b/chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc index 14a3e245196..cc29fa3e320 100644 --- a/chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/context_menus/extension_context_menu_browsertest.cc @@ -154,13 +154,13 @@ class ExtensionContextMenuBrowserTest // This creates a test menu for a page with |page_url| and |link_url|, looks // for an extension item with the given |label|, and returns true if the item // was found. - bool MenuHasItemWithLabel(const GURL& page_url, + bool MenuHasItemWithLabel(const GURL& frame_url, const GURL& link_url, - const GURL& frame_url, + bool is_subframe, const std::string& label) { std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), page_url, link_url, - frame_url)); + TestRenderViewContextMenu::Create(GetWebContents(), frame_url, link_url, + is_subframe)); return MenuHasExtensionItemWithLabel(menu.get(), label); } @@ -329,8 +329,7 @@ class ExtensionContextMenuLazyTest // Create and build our test context menu. std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(), - GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), page_url)); // Look for the extension item in the menu, and make sure it's |enabled|. int command_id = ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0); @@ -368,8 +367,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, Simple) { // Create and build our test context menu. std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(), - GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), page_url)); // Look for the extension item in the menu, and execute it. int command_id = ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0); @@ -431,8 +429,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuPersistentTest, UpdateOnclick) { // Create and build our test context menu. std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(), - GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), page_url)); // Look for the extension item in the menu, and execute it. MenuItem::Id id(false, MenuItem::ExtensionKey(extension->id())); @@ -448,8 +445,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuPersistentTest, UpdateOnclick) { ASSERT_TRUE(listener_update2.WaitUntilSatisfied()); // Rebuild the context menu and click on the second extension item. - menu = TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(), - GURL()); + menu = TestRenderViewContextMenu::Create(GetWebContents(), page_url); id.string_uid = "id2"; ASSERT_TRUE(FindCommandId(menu.get(), id, &command_id)); menu->ExecuteCommand(command_id, 0); @@ -489,8 +485,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, // Create and build our test context menu. std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(), - GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), page_url)); VerifyRadioItemSelectionState(menu.get(), extension->id(), "radio1", true); VerifyRadioItemSelectionState(menu.get(), extension->id(), "radio2", false); @@ -538,8 +533,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, // Create and build our test context menu. std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(), - GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), page_url)); VerifyRadioItemSelectionState(menu.get(), extension->id(), "radio1", true); VerifyRadioItemSelectionState(menu.get(), extension->id(), "radio2", false); @@ -575,17 +569,17 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, Patterns) { // Check that a document url that should match the items' patterns appears. GURL google_url("http://www.google.com"); - ASSERT_TRUE(MenuHasItemWithLabel(google_url, GURL(), GURL(), + ASSERT_TRUE(MenuHasItemWithLabel(google_url, GURL(), false, std::string("test_item1"))); - ASSERT_TRUE(MenuHasItemWithLabel(google_url, GURL(), GURL(), + ASSERT_TRUE(MenuHasItemWithLabel(google_url, GURL(), false, std::string("test_item2"))); // Now check with a non-matching url. GURL test_url("http://www.test.com"); - ASSERT_FALSE(MenuHasItemWithLabel(test_url, GURL(), GURL(), - std::string("test_item1"))); - ASSERT_FALSE(MenuHasItemWithLabel(test_url, GURL(), GURL(), - std::string("test_item2"))); + ASSERT_FALSE( + MenuHasItemWithLabel(test_url, GURL(), false, std::string("test_item1"))); + ASSERT_FALSE( + MenuHasItemWithLabel(test_url, GURL(), false, std::string("test_item2"))); } // Tests registering an item with a very long title that should get truncated in @@ -608,7 +602,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, LongTitle) { // truncated. GURL url("http://foo.com/"); std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), url)); std::u16string label; ASSERT_TRUE(GetItemLabel(menu.get(), item->id(), &label)); @@ -654,7 +648,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, TopLevel) { GURL url("http://foo.com/"); std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), url)); size_t index = 0; MenuModel* model = nullptr; @@ -742,7 +736,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuPersistentTest, Separators) { GURL url("http://www.google.com/"); std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), url)); // The top-level item should be an "automagic parent" with the extension's // name. @@ -767,8 +761,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuPersistentTest, Separators) { ASSERT_TRUE(ui_test_utils::NavigateToURL( browser(), GURL(extension->GetResourceURL("test2.html")))); EXPECT_TRUE(listener2.WaitUntilSatisfied()); - menu = - TestRenderViewContextMenu::Create(GetWebContents(), url, GURL(), GURL()); + menu = TestRenderViewContextMenu::Create(GetWebContents(), url); ASSERT_TRUE(menu->GetMenuModelAndItemIndex( ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0), &model, @@ -791,14 +784,14 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, TargetURLs) { // No target url - the item should not appear. ASSERT_FALSE( - MenuHasItemWithLabel(google_url, GURL(), GURL(), std::string("item1"))); + MenuHasItemWithLabel(google_url, GURL(), false, std::string("item1"))); // A matching target url - the item should appear. - ASSERT_TRUE(MenuHasItemWithLabel(google_url, google_url, GURL(), + ASSERT_TRUE(MenuHasItemWithLabel(google_url, google_url, false, std::string("item1"))); // A non-matching target url - the item should not appear. - ASSERT_FALSE(MenuHasItemWithLabel(google_url, non_google_url, GURL(), + ASSERT_FALSE(MenuHasItemWithLabel(google_url, non_google_url, false, std::string("item1"))); } @@ -832,13 +825,11 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuSWTest, IncognitoSplit) { // Create and build our test context menu. std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(), - GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), page_url)); WebContents* incognito_web_contents = browser_incognito->tab_strip_model()->GetActiveWebContents(); std::unique_ptr<TestRenderViewContextMenu> menu_incognito( - TestRenderViewContextMenu::Create(incognito_web_contents, page_url, - GURL(), GURL())); + TestRenderViewContextMenu::Create(incognito_web_contents, page_url)); // Look for the extension item in the menu, and execute it. int command_id = ContextMenuMatcher::ConvertToExtensionsCustomCommandId(0); @@ -864,18 +855,16 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, Frames) { ASSERT_TRUE(listener.WaitUntilSatisfied()); GURL page_url("http://www.google.com"); - GURL no_frame_url; - GURL frame_url("http://www.google.com"); - - ASSERT_TRUE(MenuHasItemWithLabel(page_url, GURL(), no_frame_url, - std::string("Page item"))); - ASSERT_FALSE(MenuHasItemWithLabel(page_url, GURL(), no_frame_url, - std::string("Frame item"))); - - ASSERT_TRUE(MenuHasItemWithLabel(page_url, GURL(), frame_url, - std::string("Page item"))); - ASSERT_TRUE(MenuHasItemWithLabel(page_url, GURL(), frame_url, - std::string("Frame item"))); + + ASSERT_TRUE( + MenuHasItemWithLabel(page_url, GURL(), false, std::string("Page item"))); + ASSERT_FALSE( + MenuHasItemWithLabel(page_url, GURL(), false, std::string("Frame item"))); + + ASSERT_TRUE( + MenuHasItemWithLabel(page_url, GURL(), true, std::string("Page item"))); + ASSERT_TRUE( + MenuHasItemWithLabel(page_url, GURL(), true, std::string("Frame item"))); } // Tests that info.frameId is correctly set when the context menu is invoked. @@ -932,18 +921,17 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, EventPage) { host_helper.WaitForHostDestroyed(); // Test that menu items appear while the page is unloaded. - ASSERT_TRUE(MenuHasItemWithLabel( - about_blank, GURL(), GURL(), std::string("Item 1"))); - ASSERT_TRUE(MenuHasItemWithLabel( - about_blank, GURL(), GURL(), std::string("Checkbox 1"))); + ASSERT_TRUE( + MenuHasItemWithLabel(about_blank, GURL(), false, std::string("Item 1"))); + ASSERT_TRUE(MenuHasItemWithLabel(about_blank, GURL(), false, + std::string("Checkbox 1"))); // Test that checked menu items retain their checkedness. extensions::ExtensionHostTestHelper checkbox_checked(profile()); host_helper.RestrictToType( extensions::mojom::ViewType::kExtensionBackgroundPage); std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), about_blank, GURL(), - GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), about_blank)); MenuItem::Id id(false, MenuItem::ExtensionKey(extension->id())); id.string_uid = "checkbox1"; @@ -1003,8 +991,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionContextMenuLazyTest, UpdateCheckboxes) { // Create and build our test context menu. std::unique_ptr<TestRenderViewContextMenu> menu( - TestRenderViewContextMenu::Create(GetWebContents(), page_url, GURL(), - GURL())); + TestRenderViewContextMenu::Create(GetWebContents(), page_url)); VerifyRadioItemSelectionState(menu.get(), extension->id(), "checkbox1", false); diff --git a/chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc b/chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc index 5f76f554f0f..f30d5c0283a 100644 --- a/chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc @@ -92,7 +92,7 @@ class CrashReportPrivateApiTest : public ExtensionApiTest { const absl::optional<MockCrashEndpoint::Report>& last_report() { return crash_endpoint_->last_report(); } - raw_ptr<const Extension, ExperimentalAsh> extension_; + raw_ptr<const Extension, DanglingUntriaged | ExperimentalAsh> extension_; std::unique_ptr<MockCrashEndpoint> crash_endpoint_; std::unique_ptr<ScopedMockChromeJsErrorReportProcessor> processor_; }; @@ -327,7 +327,7 @@ IN_PROC_BROWSER_TEST_P(CrashReportPrivateCalledFromSwaTest, ASSERT_TRUE(embedded_test_server()->Started()); // Create and launch a test web app, opens in an app window. GURL start_url = embedded_test_server()->GetURL("/test_app.html"); - auto web_app_info = std::make_unique<WebAppInstallInfo>(); + auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>(); web_app_info->start_url = start_url; web_app::AppId app_id = web_app::test::InstallWebApp(profile(), std::move(web_app_info)); diff --git a/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc b/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc index ee4f1e0a2d5..09694b76bb9 100644 --- a/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc +++ b/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc @@ -61,6 +61,7 @@ #include "extensions/common/manifest_constants.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/switches.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "url/origin.h" using content::DevToolsAgentHost; @@ -163,15 +164,15 @@ bool ExtensionMayAttachToRenderFrameHost( bool result = true; render_frame_host->ForEachRenderFrameHostWithAction( [&extension, extension_profile, error, - &result](content::RenderFrameHost* rfh) { - // If |rfh| is attached to an inner MimeHandlerViewGuest skip it. - // This is done to fix crbug.com/1293856 because an extension cannot - // inspect another extension. - if (MimeHandlerViewGuest::FromRenderFrameHost(rfh)) { + &result](content::RenderFrameHost* render_frame_host) { + // If |render_frame_host| is attached to an inner MimeHandlerViewGuest + // skip it. This is done to fix crbug.com/1293856 because an extension + // cannot inspect another extension. + if (MimeHandlerViewGuest::FromRenderFrameHost(render_frame_host)) { return content::RenderFrameHost::FrameIterationAction::kSkipChildren; } - if (rfh->GetWebUI()) { + if (render_frame_host->GetWebUI()) { *error = debugger_api_constants::kRestrictedError; result = false; return content::RenderFrameHost::FrameIterationAction::kStop; @@ -180,12 +181,12 @@ bool ExtensionMayAttachToRenderFrameHost( // We check both the last committed URL and the SiteURL because this // method may be called in the middle of a navigation where the SiteURL // has been updated but navigation hasn't committed yet. - if (!ExtensionMayAttachToURLOrInnerURL(extension, extension_profile, - rfh->GetLastCommittedURL(), - error) || + if (!ExtensionMayAttachToURLOrInnerURL( + extension, extension_profile, + render_frame_host->GetLastCommittedURL(), error) || !ExtensionMayAttachToURLOrInnerURL( extension, extension_profile, - rfh->GetSiteInstance()->GetSiteURL(), error)) { + render_frame_host->GetSiteInstance()->GetSiteURL(), error)) { result = false; return content::RenderFrameHost::FrameIterationAction::kStop; } @@ -256,10 +257,12 @@ base::LazyInstance<AttachedClientHosts>::Leaky g_attached_client_hosts = class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient, public ExtensionRegistryObserver { public: - ExtensionDevToolsClientHost(Profile* profile, - DevToolsAgentHost* agent_host, - scoped_refptr<const Extension> extension, - const Debuggee& debuggee); + ExtensionDevToolsClientHost( + Profile* profile, + DevToolsAgentHost* agent_host, + scoped_refptr<const Extension> extension, + absl::optional<WorkerId> extension_service_worker_id, + const Debuggee& debuggee); ExtensionDevToolsClientHost(const ExtensionDevToolsClientHost&) = delete; ExtensionDevToolsClientHost& operator=(const ExtensionDevToolsClientHost&) = @@ -309,6 +312,10 @@ class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient, raw_ptr<Profile> profile_; scoped_refptr<DevToolsAgentHost> agent_host_; scoped_refptr<const Extension> extension_; + // The WorkerId of the extension service worker that called attach() for this + // client host, if any. + const absl::optional<WorkerId> extension_service_worker_id_; + Debuggee debuggee_; base::CallbackListSubscription on_app_terminating_subscription_; int last_request_id_ = 0; @@ -317,6 +324,11 @@ class ExtensionDevToolsClientHost : public content::DevToolsAgentHostClient, api::debugger::DetachReason detach_reason_ = api::debugger::DetachReason::kTargetClosed; + // A service worker keepalive used to keep the associated worker alive while + // this client is attached. Only used if `extension_service_worker_id_` has a + // value. + absl::optional<base::Uuid> service_worker_keepalive_; + // Listen to extension unloaded notification. base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver> extension_registry_observation_{this}; @@ -326,10 +338,12 @@ ExtensionDevToolsClientHost::ExtensionDevToolsClientHost( Profile* profile, DevToolsAgentHost* agent_host, scoped_refptr<const Extension> extension, + absl::optional<WorkerId> extension_service_worker_id, const Debuggee& debuggee) : profile_(profile), agent_host_(agent_host), - extension_(std::move(extension)) { + extension_(std::move(extension)), + extension_service_worker_id_(std::move(extension_service_worker_id)) { CopyDebuggee(&debuggee_, debuggee); g_attached_client_hosts.Get().insert(this); @@ -366,12 +380,38 @@ bool ExtensionDevToolsClientHost::Attach() { extension_id(), extension_->name(), base::BindOnce(&ExtensionDevToolsClientHost::InfoBarDestroyed, base::Unretained(this))); + if (extension_service_worker_id_) { + ProcessManager* process_manager = ProcessManager::Get(profile_); + CHECK(process_manager); + // The service worker should definitely be registered at this point. + CHECK(process_manager->HasServiceWorker(*extension_service_worker_id_)); + service_worker_keepalive_ = + process_manager->IncrementServiceWorkerKeepaliveCount( + *extension_service_worker_id_, + content::ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout, + Activity::DEBUGGER, /*extra_data=*/std::string()); + } + return true; } ExtensionDevToolsClientHost::~ExtensionDevToolsClientHost() { ExtensionDevToolsInfoBarDelegate::NotifyExtensionDetached(extension_id()); g_attached_client_hosts.Get().erase(this); + + // Decrement the associated worker keepalive, if any. + if (service_worker_keepalive_) { + CHECK(extension_service_worker_id_); + ProcessManager* process_manager = ProcessManager::Get(profile_); + CHECK(process_manager); + // The worker may have terminated for other reasons. Only decrement the + // keepalive if it's still around. + if (process_manager->HasServiceWorker(*extension_service_worker_id_)) { + process_manager->DecrementServiceWorkerKeepaliveCount( + *extension_service_worker_id_, *service_worker_keepalive_, + Activity::DEBUGGER, /*extra_data=*/std::string()); + } + } } // DevToolsAgentHostClient implementation. @@ -677,7 +717,7 @@ ExtensionFunction::ResponseAction DebuggerAttachFunction::Run() { Profile* profile = Profile::FromBrowserContext(browser_context()); auto host = std::make_unique<ExtensionDevToolsClientHost>( - profile, agent_host_.get(), extension(), debuggee_); + profile, agent_host_.get(), extension(), worker_id(), debuggee_); if (!host->Attach()) { return RespondNow(Error(debugger_api_constants::kRestrictedError)); diff --git a/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc index e0062b9b410..46b52a820b7 100644 --- a/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc +++ b/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc @@ -402,7 +402,9 @@ IN_PROC_BROWSER_TEST_F(DebuggerApiTest, InfoBar) { EXPECT_EQ(1u, manager3->infobar_count()); // Closing tab should not affect anything. - ASSERT_TRUE(another_browser->tab_strip_model()->CloseWebContentsAt(1, 0)); + EXPECT_EQ(2, another_browser->tab_strip_model()->count()); + another_browser->tab_strip_model()->CloseWebContentsAt(1, 0); + EXPECT_EQ(1, another_browser->tab_strip_model()->count()); manager3 = nullptr; EXPECT_EQ(1u, manager1->infobar_count()); EXPECT_EQ(1u, manager2->infobar_count()); diff --git a/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h b/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h index c00912f79d1..48fb3aad373 100644 --- a/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h +++ b/chromium/chrome/browser/extensions/api/debugger/extension_dev_tools_infobar_delegate.h @@ -13,6 +13,7 @@ #include "base/memory/raw_ptr.h" #include "base/timer/timer.h" #include "components/infobars/core/confirm_infobar_delegate.h" +#include "extensions/common/extension_id.h" class GlobalConfirmInfoBar; @@ -57,7 +58,7 @@ class ExtensionDevToolsInfoBarDelegate : public ConfirmInfoBarDelegate { base::CallbackListSubscription RegisterDestroyedCallback( base::OnceClosure destroyed_callback); - const std::string extension_id_; + const ExtensionId extension_id_; const std::u16string extension_name_; // infobar_ is set after attaching an extension and is deleted 5 seconds after // detaching the extension. |infobar_| owns this object and is therefore diff --git a/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc b/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc index bc4971bc0f1..cdd579ef54a 100644 --- a/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc @@ -158,7 +158,7 @@ class SetIconAPIPrerenderingTest : public SetIconAPITest { private: void SetUp() override { - prerender_helper_.SetUp(embedded_test_server()); + prerender_helper_.RegisterServerRequestMonitor(embedded_test_server()); ExtensionApiTest::SetUp(); } diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc index 26857eb016e..849d6bdb456 100644 --- a/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc @@ -154,10 +154,12 @@ base::Value::List VectorToList(const std::vector<T>& values) { } // Returns true if |window.scriptExecuted| is true for the given frame. -bool WasFrameWithScriptLoaded(content::RenderFrameHost* rfh) { - if (!rfh) +bool WasFrameWithScriptLoaded(content::RenderFrameHost* render_frame_host) { + if (!render_frame_host) { return false; - return content::EvalJs(rfh, "!!window.scriptExecuted").ExtractBool(); + } + return content::EvalJs(render_frame_host, "!!window.scriptExecuted") + .ExtractBool(); } // Helper to wait for ruleset load in response to extension load. @@ -2380,11 +2382,11 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules( rules_1, "extension_1", {URLPattern::kAllUrlsPattern})); - const std::string extension_id_1 = last_loaded_extension_id(); + const ExtensionId extension_id_1 = last_loaded_extension_id(); ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules( rules_2, "extension_2", {URLPattern::kAllUrlsPattern})); - const std::string extension_id_2 = last_loaded_extension_id(); + const ExtensionId extension_id_2 = last_loaded_extension_id(); auto get_manifest_url = [](const ExtensionId& extension_id) { return GURL(base::StringPrintf("%s://%s/manifest.json", @@ -6649,7 +6651,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, FledgeAuctionScripts) { navigator.joinAdInterestGroup({ name: 'cars', owner: $1, - biddingLogicUrl: $2, + biddingLogicURL: $2, userBiddingSignals: [], ads: [{ renderURL: 'https://example.com/render', @@ -6682,7 +6684,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, FledgeAuctionScripts) { (async function() { let config = await navigator.runAdAuction({ seller: $1, - decisionLogicUrl: $2, + decisionLogicURL: $2, interestGroupBuyers: [$1], }); document.querySelector('fencedframe').config = @@ -6780,21 +6782,22 @@ class DeclarativeNetRequestBackForwardCacheBrowserTest GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); // 1) Navigate to A. - content::RenderFrameHost* rfh_a = + content::RenderFrameHost* render_frame_host_a = ui_test_utils::NavigateToURL(browser(), url_a); - auto delete_observer_rfh_a = - std::make_unique<content::RenderFrameDeletedObserver>(rfh_a); + auto delete_observer_render_frame_host_a = + std::make_unique<content::RenderFrameDeletedObserver>( + render_frame_host_a); // 2) Navigate to B. - content::RenderFrameHost* rfh_b = + content::RenderFrameHost* render_frame_host_b = ui_test_utils::NavigateToURL(browser(), url_b); - // Ensure that |rfh_a| is in the cache. - EXPECT_FALSE(delete_observer_rfh_a->deleted()); - EXPECT_NE(rfh_a, rfh_b); - EXPECT_EQ(rfh_a->GetLifecycleState(), + // Ensure that |render_frame_host_a| is in the cache. + EXPECT_FALSE(delete_observer_render_frame_host_a->deleted()); + EXPECT_NE(render_frame_host_a, render_frame_host_b); + EXPECT_EQ(render_frame_host_a->GetLifecycleState(), content::RenderFrameHost::LifecycleState::kInBackForwardCache); - return delete_observer_rfh_a; + return delete_observer_render_frame_host_a; } private: @@ -6811,16 +6814,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest, rule.condition->url_filter = std::string("script.js"); ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule})); - auto bfcache_rfh_delete_observer = NavigateForBackForwardCache(); + auto bfcache_render_frame_host_delete_observer = + NavigateForBackForwardCache(); const ExtensionId extension_id = last_loaded_extension_id(); // Add dynamic rule. rule.condition->url_filter = std::string("dynamic.com"); ASSERT_NO_FATAL_FAILURE(AddDynamicRules(extension_id, {rule})); - // Expect that |rfh_a| is destroyed as the cache would get cleared due to - // addition of new rule. - bfcache_rfh_delete_observer->WaitUntilDeleted(); + // Expect that |render_frame_host_a| is destroyed as the cache would get + // cleared due to addition of new rule. + bfcache_render_frame_host_delete_observer->WaitUntilDeleted(); } // Ensure that Back Forward is cleared on updating session rules. @@ -6833,16 +6837,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest, rule.condition->url_filter = std::string("script.js"); ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule})); - auto bfcache_rfh_delete_observer = NavigateForBackForwardCache(); + auto bfcache_render_frame_host_delete_observer = + NavigateForBackForwardCache(); const ExtensionId extension_id = last_loaded_extension_id(); // Add session-scoped rule to block requests to "session.example". rule.condition->url_filter = std::string("session.example"); ASSERT_NO_FATAL_FAILURE(UpdateSessionRules(extension_id, {}, {rule})); - // Expect that |rfh_a| is destroyed as the cache would get cleared due to - // addition of new rule. - bfcache_rfh_delete_observer->WaitUntilDeleted(); + // Expect that |render_frame_host_a| is destroyed as the cache would get + // cleared due to addition of new rule. + bfcache_render_frame_host_delete_observer->WaitUntilDeleted(); } // Ensure that Back Forward is cleared on updating enabled rulesets. @@ -6858,16 +6863,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest, ASSERT_NO_FATAL_FAILURE( LoadExtensionWithRulesets(rulesets, "test_extension", {} /* hosts */)); - auto bfcache_rfh_delete_observer = NavigateForBackForwardCache(); + auto bfcache_render_frame_host_delete_observer = + NavigateForBackForwardCache(); const ExtensionId extension_id = last_loaded_extension_id(); // Enable |ruleset_2|. ASSERT_NO_FATAL_FAILURE( UpdateEnabledRulesets(last_loaded_extension_id(), {}, {"ruleset_2"})); - // Expect that |rfh_a| is destroyed as the cache would get cleared due to - // addition of new ruleset. - bfcache_rfh_delete_observer->WaitUntilDeleted(); + // Expect that |render_frame_host_a| is destroyed as the cache would get + // cleared due to addition of new ruleset. + bfcache_render_frame_host_delete_observer->WaitUntilDeleted(); } // Ensure that Back Forward is cleared on new extension. @@ -6875,16 +6881,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBackForwardCacheBrowserTest, BackForwardCacheClearedOnAddExtension) { set_config_flags(ConfigFlag::kConfig_HasBackgroundScript); - auto bfcache_rfh_delete_observer = NavigateForBackForwardCache(); + auto bfcache_render_frame_host_delete_observer = + NavigateForBackForwardCache(); // Now block requests to script.js. TestRule rule = CreateGenericRule(); rule.condition->url_filter = std::string("script.js"); ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule})); - // Expect that |rfh_a| is destroyed as the cache would get cleared due to - // addition of new rule. - bfcache_rfh_delete_observer->WaitUntilDeleted(); + // Expect that |render_frame_host_a| is destroyed as the cache would get + // cleared due to addition of new rule. + bfcache_render_frame_host_delete_observer->WaitUntilDeleted(); } INSTANTIATE_TEST_SUITE_P(All, diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc index 7f08f2e1020..0d03680a22f 100644 --- a/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc @@ -184,7 +184,8 @@ TEST_P(RulesetManagerTest, MultipleRulesets) { ASSERT_EQ(0u, manager()->GetMatcherCountForTest()); - std::string extension_id_one, extension_id_two; + ExtensionId extension_id_one; + ExtensionId extension_id_two; size_t expected_matcher_count = 0; // Add the required rulesets. diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc index 362165b3186..61b0f3825cf 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc +++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.cc @@ -52,8 +52,12 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/extensions/application_launch.h" +#include "chrome/browser/ui/extensions/extensions_dialogs.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h" +#include "chrome/browser/web_applications/extension_status_utils.h" #include "chrome/common/extensions/api/developer_private.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/pref_names.h" @@ -87,7 +91,6 @@ #include "extensions/browser/extension_util.h" #include "extensions/browser/file_highlighter.h" #include "extensions/browser/management_policy.h" -#include "extensions/browser/notification_types.h" #include "extensions/browser/path_util.h" #include "extensions/browser/permissions_manager.h" #include "extensions/browser/process_manager_factory.h" @@ -159,12 +162,19 @@ const char kCannotRepairPolicyExtension[] = "Cannot repair a policy-installed extension."; const char kCannotChangeHostPermissions[] = "Cannot change host permissions for the given extension."; +const char kCannotSetPinnedWithoutAction[] = + "Cannot set pinned action state for an extension with no action."; const char kInvalidHost[] = "Invalid host."; const char kInvalidLazyBackgroundPageParameter[] = "isServiceWorker can not be set for lazy background page based extensions."; const char kInvalidRenderProcessId[] = "render_process_id can be set to -1 for only lazy background page based or " "service-worker based extensions."; +const char kFailToUninstallEnterpriseOrComponentExtensions[] = + "Cannot uninstall the enterprise or component extensions in your list."; +const char kFailToUninstallNoneExistentExtensions[] = + "Cannot uninstall non-existent extensions in your list."; +const char kUserCancelledError[] = "User cancelled uninstall"; const char kUnpackedAppsFolder[] = "apps_target"; const char kManifestFile[] = "manifest.json"; @@ -361,18 +371,6 @@ void ProcessSitesForRuntimeHostPermissions( } } -// Returns the current set of granted host permissions for the extension. Note -// that permissions that are specified but withheld will not be returned. -std::unique_ptr<const PermissionSet> GetExtensionGrantedPermissions( - content::BrowserContext* context, - const scoped_refptr<const Extension>& extension) { - ExtensionPrefs* prefs = ExtensionPrefs::Get(context); - const PermissionsManager* manager = PermissionsManager::Get(context); - return manager->HasWithheldHostPermissions(*extension) - ? prefs->GetRuntimeGrantedPermissions(extension->id()) - : prefs->GetGrantedPermissions(extension->id()); -} - // Updates num_extensions counts in `site_groups` for `granted_hosts` from one // extension. void UpdateSiteGroupCountsForExtensionHosts( @@ -509,7 +507,8 @@ std::unique_ptr<developer::ProfileInfo> DeveloperPrivateAPI::CreateProfileInfo( #if BUILDFLAG(ENABLE_SUPERVISED_USERS) supervised_user::SupervisedUserService* service = SupervisedUserServiceFactory::GetForProfile(profile); - info->is_child_account = service->AreExtensionsPermissionsEnabled(); + info->is_child_account = + service && service->AreExtensionsPermissionsEnabled(); #else info->is_child_account = false; #endif @@ -542,6 +541,7 @@ void BrowserContextKeyedAPIFactory< DependsOn(EventRouterFactory::GetInstance()); DependsOn(ExtensionSystemFactory::GetInstance()); DependsOn(PermissionsManager::GetFactory()); + DependsOn(ToolbarActionsModelFactory::GetInstance()); } // static @@ -569,6 +569,7 @@ DeveloperPrivateEventRouter::DeveloperPrivateEventRouter(Profile* profile) extension_allowlist_observer_.Observe( ExtensionSystem::Get(profile)->extension_service()->allowlist()); permissions_manager_observation_.Observe(PermissionsManager::Get(profile)); + toolbar_actions_model_observation_.Observe(ToolbarActionsModel::Get(profile)); pref_change_registrar_.Init(profile->GetPrefs()); // The unretained is safe, since the PrefChangeRegistrar unregisters the // callback on destruction. @@ -637,6 +638,12 @@ void DeveloperPrivateEventRouter::OnErrorAdded(const ExtensionError* error) { error->extension_id()); } +void DeveloperPrivateEventRouter::OnExtensionConfigurationChanged( + const std::string& extension_id) { + BroadcastItemStateChanged(developer::EVENT_TYPE_CONFIGURATION_CHANGED, + extension_id); +} + void DeveloperPrivateEventRouter::OnErrorsRemoved( const std::set<std::string>& removed_ids) { for (const std::string& id : removed_ids) { @@ -748,6 +755,21 @@ void DeveloperPrivateEventRouter::OnExtensionPermissionsUpdated( extension.id()); } +void DeveloperPrivateEventRouter::OnToolbarPinnedActionsChanged() { + // Currently, only enabled extensions are considered since they are the only + // ones that have extension actions. + // TODO(crbug.com/1477884): Since pinned info is stored as a pref, include + // disabled extensions in this event as well. + const ExtensionSet& extensions = + ExtensionRegistry::Get(profile_)->enabled_extensions(); + for (const auto& extension : extensions) { + if (ui_util::ShouldDisplayInExtensionSettings(*extension)) { + BroadcastItemStateChanged(developer::EVENT_TYPE_PINNED_ACTIONS_CHANGED, + extension->id()); + } + } +} + void DeveloperPrivateEventRouter::OnProfilePrefChanged() { base::Value::List args; args.Append(DeveloperPrivateAPI::CreateProfileInfo(profile_)->ToValue()); @@ -1162,6 +1184,26 @@ DeveloperPrivateUpdateExtensionConfigurationFunction::Run() { ExtensionPrefs::Get(browser_context()) ->SetBooleanPref(extension->id(), kPrefAcknowledgeSafetyCheckWarning, *update.acknowledge_safety_check_warning); + DeveloperPrivateEventRouter* event_router = + DeveloperPrivateAPI::Get(browser_context()) + ->developer_private_event_router(); + if (event_router) { + event_router->OnExtensionConfigurationChanged(extension->id()); + } + } + if (update.pinned_to_toolbar) { + ToolbarActionsModel* toolbar_actions_model = ToolbarActionsModel::Get( + Profile::FromBrowserContext(browser_context())); + if (!toolbar_actions_model->HasAction(extension->id())) { + return RespondNow(Error(kCannotSetPinnedWithoutAction)); + } + + bool is_action_pinned = + toolbar_actions_model->IsActionPinned(extension->id()); + if (is_action_pinned != *update.pinned_to_toolbar) { + toolbar_actions_model->SetActionVisibility(extension->id(), + !is_action_pinned); + } } return RespondNow(NoArguments()); @@ -1635,7 +1677,6 @@ ExtensionFunction::ResponseAction DeveloperPrivateLoadDirectoryFunction::Run() { if (directory_url.is_valid() && directory_url.type() != storage::kFileSystemTypeLocal && - directory_url.type() != storage::kFileSystemTypeRestrictedLocal && directory_url.type() != storage::kFileSystemTypeDragged) { return LoadByFileSystemAPI(directory_url); } @@ -2006,11 +2047,14 @@ DeveloperPrivateOpenDevToolsFunction::Run() { // NOTE(devlin): Even though the properties use "render_view_id", this // actually refers to a render frame. - content::RenderFrameHost* rfh = content::RenderFrameHost::FromID( - properties.render_process_id, properties.render_view_id); + content::RenderFrameHost* render_frame_host = + content::RenderFrameHost::FromID(properties.render_process_id, + properties.render_view_id); content::WebContents* web_contents = - rfh ? content::WebContents::FromRenderFrameHost(rfh) : nullptr; + render_frame_host + ? content::WebContents::FromRenderFrameHost(render_frame_host) + : nullptr; // It's possible that the render frame was closed since we last updated the // links. Handle this gracefully. if (!web_contents) @@ -2410,23 +2454,19 @@ DeveloperPrivateGetUserAndExtensionSitesByEtldFunction::Run() { std::vector<scoped_refptr<const Extension>> extensions_to_check; ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context()); + PermissionsManager* permissions_manager = + PermissionsManager::Get(browser_context()); // Note: we are only counting enabled extensions as the returned extension // counts will reflect how many extensions can actually run on each site at // the current moment. for (const auto& extension : registry->enabled_extensions()) { - // TODO(crbug.com/1331137): Some extensions can access certain sites even if - // the user cannot modify their permissions. These also need to be added to - // another list so the frontend knows that their site access cannot be - // modified. - PermissionsManager* manager = PermissionsManager::Get(browser_context()); - if (!ui_util::ShouldDisplayInExtensionSettings(*extension) || - !manager->CanAffectExtension(*extension)) { + if (!ui_util::ShouldDisplayInExtensionSettings(*extension)) { continue; } std::unique_ptr<const PermissionSet> granted_permissions = - GetExtensionGrantedPermissions(browser_context(), extension); + permissions_manager->GetExtensionGrantedPermissions(*extension); std::vector<URLPattern> distinct_hosts = ExtensionInfoGenerator::GetDistinctHosts( granted_permissions->effective_hosts()); @@ -2445,7 +2485,7 @@ DeveloperPrivateGetUserAndExtensionSitesByEtldFunction::Run() { // counts are accurate. for (const auto& extension : extensions_to_check) { std::unique_ptr<const PermissionSet> granted_permissions = - GetExtensionGrantedPermissions(browser_context(), extension); + permissions_manager->GetExtensionGrantedPermissions(*extension); UpdateSiteGroupCountsForExtensionHosts( &site_groups, &match_subdomains_count, granted_permissions->effective_hosts()); @@ -2492,20 +2532,22 @@ DeveloperPrivateGetMatchingExtensionsForSiteFunction::Run() { if (parsed_site.Parse(params->site) != URLPattern::ParseResult::kSuccess) return RespondNow(Error("Invalid site: " + params->site)); + constexpr bool kIncludeApiPermissions = false; + std::vector<developer::MatchingExtensionInfo> matching_extensions; URLPatternSet site_pattern({parsed_site}); - const ExtensionSet all_extensions = - ExtensionRegistry::Get(browser_context()) - ->GenerateInstalledExtensionsSet( - ExtensionRegistry::ENABLED | ExtensionRegistry::DISABLED | - ExtensionRegistry::TERMINATED | ExtensionRegistry::BLOCKLISTED); - for (const auto& extension : all_extensions) { + const ExtensionSet& enabled_extensions = + ExtensionRegistry::Get(browser_context())->enabled_extensions(); + PermissionsManager* permissions_manager = + PermissionsManager::Get(browser_context()); + for (const auto& extension : enabled_extensions) { + std::unique_ptr<const PermissionSet> granted_permissions = + permissions_manager->GetExtensionGrantedPermissions(*extension); const URLPatternSet& extension_withheld_sites = extension->permissions_data()->withheld_permissions().effective_hosts(); const URLPatternSet granted_intersection = URLPatternSet::CreateIntersection( - site_pattern, - extension->permissions_data()->GetEffectiveHostPermissions(), + site_pattern, granted_permissions->effective_hosts(), URLPatternSet::IntersectionBehavior::kDetailed); const URLPatternSet withheld_intersection = URLPatternSet::CreateIntersection( @@ -2519,16 +2561,25 @@ DeveloperPrivateGetMatchingExtensionsForSiteFunction::Run() { // have access to any sites that match `site_pattern`. developer::HostAccess host_access = developer::HOST_ACCESS_ON_CLICK; + // TODO(crbug.com/1472899): Add a version of CanUserSelectSiteAccess to + // PermissionsManager which takes in a URLPattern. + bool can_request_all_sites = + granted_permissions->ShouldWarnAllHosts(kIncludeApiPermissions) || + extension->permissions_data() + ->withheld_permissions() + .ShouldWarnAllHosts(kIncludeApiPermissions); + // If the extension has access to at least one site that matches - // `site_pattern`, return ON_ALL_SITES or ON_SPECIFIC_SITES depending on - // if the extension has any withheld sites. + // `site_pattern`, return ON_ALL_SITES if the extension can request all + // sites and has no withheld sites, or ON_SPECIFIC_SITES otherwise. if (!granted_intersection.is_empty()) { - host_access = extension_withheld_sites.is_empty() + host_access = can_request_all_sites && extension_withheld_sites.is_empty() ? developer::HOST_ACCESS_ON_ALL_SITES : developer::HOST_ACCESS_ON_SPECIFIC_SITES; } developer::MatchingExtensionInfo matching_info; + matching_info.can_request_all_sites = can_request_all_sites; matching_info.site_access = host_access; matching_info.id = extension->id(); matching_extensions.push_back(std::move(matching_info)); @@ -2627,6 +2678,85 @@ void DeveloperPrivateUpdateSiteAccessFunction::OnSiteSettingsUpdated() { Respond(NoArguments()); } +DeveloperPrivateRemoveMultipleExtensionsFunction:: + DeveloperPrivateRemoveMultipleExtensionsFunction() = default; +DeveloperPrivateRemoveMultipleExtensionsFunction:: + ~DeveloperPrivateRemoveMultipleExtensionsFunction() = default; + +ExtensionFunction::ResponseAction +DeveloperPrivateRemoveMultipleExtensionsFunction::Run() { + absl::optional<developer::RemoveMultipleExtensions::Params> params = + developer::RemoveMultipleExtensions::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + profile_ = Profile::FromBrowserContext(browser_context()); + extension_ids_ = std::move(params->extension_ids); + + // Verify the input extension list. + for (const auto& extension_id : extension_ids_) { + CHECK(profile_); + const Extension* current_extension = + ExtensionRegistry::Get(profile_)->GetExtensionById( + extension_id, ExtensionRegistry::EVERYTHING); + if (!current_extension) { + // Return early if the extension is a non-existent extension. + return RespondNow(Error(kFailToUninstallNoneExistentExtensions)); + } + // If enterprise or component extensions are found, do nothing and respond + // with an error. + if (Manifest::IsComponentLocation(current_extension->location()) || + Manifest::IsPolicyLocation(current_extension->location())) { + return RespondNow(Error(kFailToUninstallEnterpriseOrComponentExtensions)); + } + } + + if (accept_bubble_for_testing_.has_value()) { + if (*accept_bubble_for_testing_) { + OnDialogAccepted(); + return AlreadyResponded(); + } + return RespondNow(NoArguments()); + } + + Browser* browser = chrome::FindBrowserWithWebContents(GetSenderWebContents()); + CHECK(browser); + + ShowExtensionMultipleUninstallDialog( + browser->profile(), browser->window()->GetNativeWindow(), extension_ids_, + base::BindOnce( + &DeveloperPrivateRemoveMultipleExtensionsFunction::OnDialogAccepted, + this), + base::BindOnce( + &DeveloperPrivateRemoveMultipleExtensionsFunction::OnDialogCancelled, + this)); + return RespondLater(); +} + +void DeveloperPrivateRemoveMultipleExtensionsFunction::OnDialogCancelled() { + // Let the consumer end know that the Close button was clicked. + Respond(Error(kUserCancelledError)); +} + +void DeveloperPrivateRemoveMultipleExtensionsFunction::OnDialogAccepted() { + for (const auto& extension_id : extension_ids_) { + if (!browser_context()) { + return; + } + const Extension* current_extension = + ExtensionRegistry::Get(profile_)->GetExtensionById( + extension_id, ExtensionRegistry::EVERYTHING); + // Extensions can be uninstalled externally while the dialog is open. Only + // uninstall extensions that are still existent. + if (!current_extension) { + continue; + } + // If an extension fails to be uninstalled, it will not pause the + // uninstall of the other extensions on the list. + ExtensionSystem::Get(profile_)->extension_service()->UninstallExtension( + extension_id, UNINSTALL_REASON_USER_INITIATED, nullptr); + } + Respond(NoArguments()); +} + } // namespace api } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h index 3936d9649eb..c826ae40970 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h +++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h @@ -20,6 +20,7 @@ #include "chrome/browser/extensions/extension_uninstall_dialog.h" #include "chrome/browser/extensions/load_error_reporter.h" #include "chrome/browser/extensions/pack_extension_job.h" +#include "chrome/browser/ui/toolbar/toolbar_actions_model.h" #include "chrome/common/extensions/api/developer_private.h" #include "chrome/common/extensions/webstore_install_result.h" #include "components/prefs/pref_change_registrar.h" @@ -53,7 +54,7 @@ class ExtensionInfoGenerator; // A key that indicates whether the safety check warning for this // extension has been acknowledged because the user has chosen to keep // it in a past review. -constexpr PrefMap kPrefAcknowledgeSafetyCheckWarning = { +inline constexpr PrefMap kPrefAcknowledgeSafetyCheckWarning = { "ack_safety_check_warning", PrefType::kBool, PrefScope::kExtensionSpecific}; namespace api { @@ -77,7 +78,8 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, public ExtensionAllowlist::Observer, public ExtensionManagement::Observer, public WarningService::Observer, - public PermissionsManager::Observer { + public PermissionsManager::Observer, + public ToolbarActionsModel::Observer { public: explicit DeveloperPrivateEventRouter(Profile* profile); @@ -91,6 +93,10 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, void AddExtensionId(const std::string& extension_id); void RemoveExtensionId(const std::string& extension_id); + // Called when the configuration (such as user preferences) for an extension + // has changed in a way that may affect the chrome://extensions UI. + void OnExtensionConfigurationChanged(const std::string& extension_id); + private: // ExtensionRegistryObserver: void OnExtensionLoaded(content::BrowserContext* browser_context, @@ -154,6 +160,15 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, const PermissionSet& permissions, PermissionsManager::UpdateReason reason) override; + // ToolbarActionsModel::Observer: + void OnToolbarActionAdded(const ToolbarActionsModel::ActionId& id) override {} + void OnToolbarActionRemoved( + const ToolbarActionsModel::ActionId& id) override {} + void OnToolbarActionUpdated( + const ToolbarActionsModel::ActionId& id) override {} + void OnToolbarModelInitialized() override {} + void OnToolbarPinnedActionsChanged() override; + // Handles a profile preference change. void OnProfilePrefChanged(); @@ -186,6 +201,8 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, extension_allowlist_observer_{this}; base::ScopedObservation<PermissionsManager, PermissionsManager::Observer> permissions_manager_observation_{this}; + base::ScopedObservation<ToolbarActionsModel, ToolbarActionsModel::Observer> + toolbar_actions_model_observation_{this}; raw_ptr<Profile> profile_; @@ -940,6 +957,44 @@ class DeveloperPrivateUpdateSiteAccessFunction void OnSiteSettingsUpdated(); }; +class DeveloperPrivateRemoveMultipleExtensionsFunction + : public DeveloperPrivateAPIFunction { + public: + DECLARE_EXTENSION_FUNCTION("developerPrivate.removeMultipleExtensions", + DEVELOPERPRIVATE_REMOVEMULTIPLEEXTENSIONS) + DeveloperPrivateRemoveMultipleExtensionsFunction(); + + DeveloperPrivateRemoveMultipleExtensionsFunction( + const DeveloperPrivateRemoveMultipleExtensionsFunction&) = delete; + DeveloperPrivateRemoveMultipleExtensionsFunction& operator=( + const DeveloperPrivateRemoveMultipleExtensionsFunction&) = delete; + + void accept_bubble_for_testing(bool accept_bubble) { + accept_bubble_for_testing_ = accept_bubble; + } + + private: + ~DeveloperPrivateRemoveMultipleExtensionsFunction() override; + + // ExtensionFunction: + ResponseAction Run() override; + + // A callback function to run when the user accepts the action dialog. + void OnDialogAccepted(); + + // A callback function to run when the user cancels the action dialog. + void OnDialogCancelled(); + + // The IDs of the extensions to be uninstalled. + std::vector<ExtensionId> extension_ids_; + + raw_ptr<Profile> profile_; + + // If true, immediately accept the blocked action dialog by running the + // callback. + absl::optional<bool> accept_bubble_for_testing_; +}; + } // namespace api } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc index c53ab8189ee..fe1672f097c 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc @@ -22,8 +22,10 @@ #include "base/test/gtest_util.h" #include "base/test/values_test_util.h" #include "base/values.h" +#include "chrome/browser/extensions/api/developer_private/extension_info_generator.h" #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/error_console/error_console.h" +#include "chrome/browser/extensions/extension_action_test_util.h" #include "chrome/browser/extensions/extension_management.h" #include "chrome/browser/extensions/extension_management_test_util.h" #include "chrome/browser/extensions/extension_service.h" @@ -34,6 +36,7 @@ #include "chrome/browser/extensions/scripting_permissions_modifier.h" #include "chrome/browser/extensions/site_permissions_helper.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/toolbar/toolbar_actions_model.h" #include "chrome/common/extensions/api/developer_private.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/test_browser_window.h" @@ -82,6 +85,7 @@ namespace extensions { namespace { const char kGoodCrx[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; +const char kGoogleOnlyCrx[] = "jjlcocfpfbknlbgijblaapbcpbdglkhf"; constexpr char kInvalidHost[] = "invalid host"; constexpr char kInvalidHostError[] = "Invalid host."; @@ -226,13 +230,17 @@ void GetMatchingExtensionsForSite( auto MatchMatchingExtensionInfo( const std::string& extension_id, - const api::developer_private::HostAccess& host_access) { + const api::developer_private::HostAccess& host_access, + bool can_request_all_sites) { return testing::AllOf( testing::Field(&api::developer_private::MatchingExtensionInfo::id, extension_id), testing::Field( &api::developer_private::MatchingExtensionInfo::site_access, - host_access)); + host_access), + testing::Field( + &api::developer_private::MatchingExtensionInfo::can_request_all_sites, + can_request_all_sites)); } api::developer_private::ExtensionSiteAccessUpdate CreateSiteAccessUpdate( @@ -418,18 +426,27 @@ void DeveloperPrivateApiUnitTest::TestExtensionPrefSetting( function->set_source_context_type(Feature::WEBUI_CONTEXT); EXPECT_TRUE(RunFunction(function, args)) << key; EXPECT_TRUE(has_pref.Run()) << key; + } + + { + base::Value::Dict parameters; + parameters.Set("extensionId", extension_id); + parameters.Set(key, false); + + base::Value::List args; + args.Append(std::move(parameters)); ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture; - function = base::MakeRefCounted< + auto function = base::MakeRefCounted< api::DeveloperPrivateUpdateExtensionConfigurationFunction>(); EXPECT_TRUE(RunFunction(function, args)) << key; - EXPECT_TRUE(has_pref.Run()) << key; + EXPECT_FALSE(has_pref.Run()) << key; } { base::Value::Dict parameters; parameters.Set("extensionId", extension_id); - parameters.Set(key, false); + parameters.Set(key, true); base::Value::List args; args.Append(std::move(parameters)); @@ -438,7 +455,7 @@ void DeveloperPrivateApiUnitTest::TestExtensionPrefSetting( auto function = base::MakeRefCounted< api::DeveloperPrivateUpdateExtensionConfigurationFunction>(); EXPECT_TRUE(RunFunction(function, args)) << key; - EXPECT_FALSE(has_pref.Run()) << key; + EXPECT_TRUE(has_pref.Run()) << key; } } @@ -516,6 +533,7 @@ void DeveloperPrivateApiUnitTest::SetUp() { ExtensionServiceInitParams init_params; init_params.profile_is_supervised = ProfileIsSupervised(); InitializeExtensionService(init_params); + extension_action_test_util::CreateToolbarModelForProfile(profile()); browser_window_ = std::make_unique<TestBrowserWindow>(); Browser::CreateParams params(profile(), true); @@ -556,6 +574,19 @@ TEST_F(DeveloperPrivateApiUnitTest, ScriptingPermissionsModifier(profile(), base::WrapRefCounted(extension)) .SetWithholdHostPermissions(true); + // Test pinning to toolbar first as this needs the extension to be enabled. + // The other pref settings tested below may disable the extension so it will + // not have an action in the toolbar. + auto pinned_to_toolbar = [&]() { + ToolbarActionsModel* toolbar_actions_model = + ToolbarActionsModel::Get(profile()); + return toolbar_actions_model->HasAction(id) && + toolbar_actions_model->IsActionPinned(id); + }; + TestExtensionPrefSetting(base::BindLambdaForTesting(pinned_to_toolbar), + "pinnedToToolbar", id, + /*expected_default_value=*/false); + TestExtensionPrefSetting( base::BindRepeating(&HasPrefsPermission, &util::IsIncognitoEnabled, profile(), id), @@ -2012,8 +2043,16 @@ TEST_P(DeveloperPrivateApiZipFileUnitTest, InstallDroppedFileZip) { // Expect extension install directory to be immediate subdir of expected // unpacked install directory. E.g. /a/b/c/d == /a/b/c + /d. - EXPECT_EQ(extension->path(), expected_extension_install_directory_.Append( - extension->path().BaseName())); + // + // Make sure we're comparing absolute paths to avoid failures like + // https://crbug.com/1453671 on macOS 14. + base::FilePath absolute_extension_path = + base::MakeAbsoluteFilePath(extension->path()); + base::FilePath absolute_expected_extension_install_directory = + base::MakeAbsoluteFilePath(expected_extension_install_directory_.Append( + extension->path().BaseName())); + EXPECT_EQ(absolute_extension_path, + absolute_expected_extension_install_directory); // Expect extension install directory to exist and be named with the right // prefix. @@ -2481,6 +2520,50 @@ TEST_F(DeveloperPrivateApiUnitTest, }])"); } +// Test that host permissions from policy installed extensions are included in +// `getUserAndExtensionSitesByEtld` calls. +TEST_F( + DeveloperPrivateApiUnitTest, + DeveloperPrivateGetUserAndExtensionSitesByEtld_PolicyControlledExtensions) { + std::string extension_id(kGoogleOnlyCrx); + + // Set up a mock provider with a policy extension. + std::unique_ptr<MockExternalProvider> mock_provider = + std::make_unique<MockExternalProvider>( + service(), mojom::ManifestLocation::kExternalPolicyDownload); + MockExternalProvider* mock_provider_ptr = mock_provider.get(); + AddMockExternalProvider(std::move(mock_provider)); + + // google_only.crx contains only a manifest.json file that requests + // *://www.google.com/* as a permission. + mock_provider_ptr->UpdateOrAddExtension( + extension_id, "1", data_dir().AppendASCII("google_only.crx")); + // Reloading extensions should find our externally registered extension + // and install it. + { + TestExtensionRegistryObserver observer(registry()); + service()->CheckForExternalUpdates(); + EXPECT_EQ(extension_id, observer.WaitForExtensionLoaded()->id()); + } + + auto function = base::MakeRefCounted< + api::DeveloperPrivateGetUserAndExtensionSitesByEtldFunction>(); + EXPECT_TRUE(RunFunction(function, base::Value::List())) + << function->GetError(); + const base::Value::List* results = function->GetResultListForTest(); + ASSERT_EQ(1u, results->size()); + + EXPECT_THAT((*results)[0], base::test::IsJson(R"([{ + "etldPlusOne": "google.com", + "numExtensions": 1, + "sites": [{ + "siteSet": "EXTENSION_SPECIFIED", + "numExtensions": 1, + "site": "www.google.com", + }] + }])")); +} + TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateGetMatchingExtensionsForSite) { namespace developer = api::developer_private; @@ -2504,21 +2587,19 @@ TEST_F(DeveloperPrivateApiUnitTest, // "http://images.google.com/" should only match with `extension_2`. EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo( extension_2->id(), - developer::HostAccess::HOST_ACCESS_ON_ALL_SITES))); + developer::HostAccess::HOST_ACCESS_ON_SPECIFIC_SITES, + /*can_request_all_sites=*/false))); service()->DisableExtension(extension_2->id(), disable_reason::DISABLE_USER_ACTION); GetMatchingExtensionsForSite(profile(), "*://*.google.com/", &infos); - // "*://*.google.com/" should only match with both `extension_1` and - // `extension_2`. - EXPECT_THAT(infos, testing::UnorderedElementsAre( - MatchMatchingExtensionInfo( - extension_1->id(), - developer::HostAccess::HOST_ACCESS_ON_ALL_SITES), - MatchMatchingExtensionInfo( - extension_2->id(), - developer::HostAccess::HOST_ACCESS_ON_ALL_SITES))); + // "*://*.google.com/" should match with `extension_1` but not `extension_2` + // since it is disabled. + EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo( + extension_1->id(), + developer::HostAccess::HOST_ACCESS_ON_SPECIFIC_SITES, + /*can_request_all_sites=*/false))); } // Test that the host access returned by GetMatchingExtensionsForSite reflects @@ -2537,7 +2618,8 @@ TEST_F(DeveloperPrivateApiUnitTest, EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo( extension->id(), - developer::HostAccess::HOST_ACCESS_ON_ALL_SITES))); + developer::HostAccess::HOST_ACCESS_ON_ALL_SITES, + /*can_request_all_sites=*/true))); EXPECT_FALSE(PermissionsManager::Get(browser()->profile()) ->HasWithheldHostPermissions(*extension)); @@ -2545,23 +2627,25 @@ TEST_F(DeveloperPrivateApiUnitTest, modifier.SetWithholdHostPermissions(true); GetMatchingExtensionsForSite(profile(), "http://example.com/", &infos); - EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo( - extension->id(), - developer::HostAccess::HOST_ACCESS_ON_CLICK))); + EXPECT_THAT(infos, + testing::UnorderedElementsAre(MatchMatchingExtensionInfo( + extension->id(), developer::HostAccess::HOST_ACCESS_ON_CLICK, + /*can_request_all_sites=*/true))); RunAddHostPermission(profile(), *extension, "*://*.google.com/*", /*should_succeed=*/true, nullptr); GetMatchingExtensionsForSite(profile(), "http://google.com/", &infos); - EXPECT_THAT(infos, - testing::UnorderedElementsAre(MatchMatchingExtensionInfo( - extension->id(), - developer::HostAccess::HOST_ACCESS_ON_SPECIFIC_SITES))); - - GetMatchingExtensionsForSite(profile(), "http://example.com/", &infos); EXPECT_THAT(infos, testing::UnorderedElementsAre(MatchMatchingExtensionInfo( extension->id(), - developer::HostAccess::HOST_ACCESS_ON_CLICK))); + developer::HostAccess::HOST_ACCESS_ON_SPECIFIC_SITES, + /*can_request_all_sites=*/true))); + + GetMatchingExtensionsForSite(profile(), "http://example.com/", &infos); + EXPECT_THAT(infos, + testing::UnorderedElementsAre(MatchMatchingExtensionInfo( + extension->id(), developer::HostAccess::HOST_ACCESS_ON_CLICK, + /*can_request_all_sites=*/true))); } // Tests the UpdateSiteAccess function when called on an extension with no @@ -2741,6 +2825,163 @@ TEST_F(DeveloperPrivateApiUnitTest, permissions_manager->HasGrantedHostPermission(*extension_2, kGoogleCom)); } +// Test uninstalling multiple extensions. +TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateRemoveMultipleExtensions) { + scoped_refptr<const Extension> extension_1 = + ExtensionBuilder("test_1").Build(); + scoped_refptr<const Extension> extension_2 = + ExtensionBuilder("test_2").Build(); + service()->AddExtension(extension_1.get()); + service()->AddExtension(extension_2.get()); + EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_1->id())); + EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_2->id())); + + base::Value::List extension_ids_entries; + extension_ids_entries.reserve(2u); + extension_ids_entries.Append(extension_1->id()); + extension_ids_entries.Append(extension_2->id()); + std::string extension_ids_arg; + EXPECT_TRUE( + base::JSONWriter::Write(extension_ids_entries, &extension_ids_arg)); + std::string args = base::StringPrintf(R"([%s])", extension_ids_arg.c_str()); + + auto function = base::MakeRefCounted< + api::DeveloperPrivateRemoveMultipleExtensionsFunction>(); + + // Accept the multiple extension uninstallation bubble by default in unit + // tests. + function->accept_bubble_for_testing(true); + + // Run the private api to remove the installed extensions. + api_test_utils::RunFunction(function.get(), args, profile()); + + EXPECT_FALSE(registry()->enabled_extensions().Contains(extension_1->id())); + EXPECT_FALSE(registry()->enabled_extensions().Contains(extension_2->id())); + EXPECT_EQ(registry()->enabled_extensions().size(), 0u); +} + +TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateRemoveComponentExtensions) { + // Create a component extension and a regular extension, then try to remove + // them. + scoped_refptr<const Extension> component_extension = + ExtensionBuilder("component_extension") + .SetLocation(mojom::ManifestLocation::kComponent) + .Build(); + scoped_refptr<const Extension> test_extension = + ExtensionBuilder("test_extension").Build(); + service()->AddExtension(component_extension.get()); + service()->AddExtension(test_extension.get()); + + EXPECT_EQ(registry()->enabled_extensions().size(), 2u); + + // Create a list of extensions with a component extension in it. + base::Value::List extensions_list; + extensions_list.reserve(2u); + extensions_list.Append(component_extension->id()); + extensions_list.Append(test_extension->id()); + std::string args; + EXPECT_TRUE(base::JSONWriter::Write(extensions_list, &args)); + std::string component_args = base::StringPrintf(R"([%s])", args.c_str()); + auto function = base::MakeRefCounted< + api::DeveloperPrivateRemoveMultipleExtensionsFunction>(); + + // Accept the multiple extension uninstallation bubble by default in unit + // tests. + function->accept_bubble_for_testing(true); + // Verify the error message for uninstalling component and enterprise + // extensions. + EXPECT_EQ( + "Cannot uninstall the enterprise or component extensions in your list.", + api_test_utils::RunFunctionAndReturnError(function.get(), component_args, + profile())); + + // Because there is a component extension in the list, the uninstallation is + // canceled. The number of extensions remains the same. + EXPECT_EQ(registry()->enabled_extensions().size(), 2u); +} + +TEST_F(DeveloperPrivateApiUnitTest, + DeveloperPrivateRemoveEnterpriseExtensions) { + // Create an enterprise extension and a regular extension, then try to remove + // them. + scoped_refptr<const Extension> enterprise_extension = + ExtensionBuilder("enterprise_extension") + .SetLocation(mojom::ManifestLocation::kExternalPolicy) + .Build(); + scoped_refptr<const Extension> test_extension = + ExtensionBuilder("test_extension").Build(); + service()->AddExtension(enterprise_extension.get()); + service()->AddExtension(test_extension.get()); + + EXPECT_EQ(registry()->enabled_extensions().size(), 2u); + + // Create a list of extensions with an enterprise extension in it. + base::Value::List extensions_list; + extensions_list.reserve(2u); + extensions_list.Append(enterprise_extension->id()); + extensions_list.Append(test_extension->id()); + std::string args; + EXPECT_TRUE(base::JSONWriter::Write(extensions_list, &args)); + std::string enterprise_args = base::StringPrintf(R"([%s])", args.c_str()); + auto function = base::MakeRefCounted< + api::DeveloperPrivateRemoveMultipleExtensionsFunction>(); + + // Accept the multiple extension uninstallation bubble by default in unit + // tests. + function->accept_bubble_for_testing(true); + // Verify the error message for uninstalling component and enterprise + // extensions. + EXPECT_EQ( + "Cannot uninstall the enterprise or component extensions in your list.", + api_test_utils::RunFunctionAndReturnError(function.get(), enterprise_args, + profile())); + + // Because there is an enterprise extension in the list, the uninstallation is + // canceled. The number of extensions remains the same. + EXPECT_EQ(registry()->enabled_extensions().size(), 2u); +} + +// Test that an event is dispatched when the list of pinned extension actions +// has changed. +TEST_F(DeveloperPrivateApiUnitTest, + ExtensionUpdatedEventOnPinnedActionsChange) { + // We need to call DeveloperPrivateAPI::Get() in order to instantiate the + // keyed service, since it's not created by default in unit tests. + DeveloperPrivateAPI::Get(profile()); + EventRouter* event_router = EventRouter::Get(profile()); + + // The DeveloperPrivateEventRouter will only dispatch events if there's at + // least one listener to dispatch to. Create one. + const char* kEventName = + api::developer_private::OnItemStateChanged::kEventName; + event_router->AddEventListener(kEventName, render_process_host(), + crx_file::id_util::GenerateId("listener")); + + TestEventRouterObserver test_observer(event_router); + + scoped_refptr<const Extension> extension = ExtensionBuilder("test").Build(); + service()->AddExtension(extension.get()); + EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id())); + + // The event router fetches icons from a blocking thread when sending the + // update event; allow it to finish before verifying the event was dispatched. + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(WasItemChangedEventDispatched( + test_observer, extension->id(), + api::developer_private::EVENT_TYPE_PINNED_ACTIONS_CHANGED)); + + ToolbarActionsModel* toolbar_actions_model = + ToolbarActionsModel::Get(profile()); + + toolbar_actions_model->SetActionVisibility( + extension->id(), !toolbar_actions_model->IsActionPinned(extension->id())); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(WasItemChangedEventDispatched( + test_observer, extension->id(), + api::developer_private::EVENT_TYPE_PINNED_ACTIONS_CHANGED)); +} + class DeveloperPrivateApiAllowlistUnitTest : public DeveloperPrivateApiUnitTest { public: diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc b/chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc index fabe6549dda..2ac4fa20175 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc @@ -130,16 +130,26 @@ IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest, InspectEmbeddedOptionsPage) { profile()); // Verify that dev tools opened. - content::RenderFrameHost* rfh = content::RenderFrameHost::FromID( - view.render_process_id, view.render_view_id); - ASSERT_TRUE(rfh); - content::WebContents* wc = content::WebContents::FromRenderFrameHost(rfh); + content::RenderFrameHost* render_frame_host = + content::RenderFrameHost::FromID(view.render_process_id, + view.render_view_id); + ASSERT_TRUE(render_frame_host); + content::WebContents* wc = + content::WebContents::FromRenderFrameHost(render_frame_host); ASSERT_TRUE(wc); EXPECT_TRUE(DevToolsWindow::GetInstanceForInspectedWebContents(wc)); } +// TODO(https://crbug.com/1457154): Test is flaky on MSan builders. +#if defined(MEMORY_SANITIZER) +#define MAYBE_InspectInactiveServiceWorkerBackground \ + DISABLED_InspectInactiveServiceWorkerBackground +#else +#define MAYBE_InspectInactiveServiceWorkerBackground \ + InspectInactiveServiceWorkerBackground +#endif IN_PROC_BROWSER_TEST_F(DeveloperPrivateApiTest, - InspectInactiveServiceWorkerBackground) { + MAYBE_InspectInactiveServiceWorkerBackground) { ResultCatcher result_catcher; // Load an extension that is service worker-based. const Extension* extension = diff --git a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc index d1051e2fa6f..74a1d670662 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc +++ b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.cc @@ -17,6 +17,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/task/single_thread_task_runner.h" #include "chrome/browser/extensions/api/commands/command_service.h" +#include "chrome/browser/extensions/api/developer_private/developer_private_api.h" #include "chrome/browser/extensions/api/developer_private/inspectable_views_finder.h" #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" #include "chrome/browser/extensions/error_console/error_console.h" @@ -26,12 +27,12 @@ #include "chrome/browser/extensions/shared_module_service.h" #include "chrome/browser/extensions/site_permissions_helper.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/toolbar/toolbar_actions_model.h" #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/pref_names.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" -#include "chrome/grit/google_chrome_strings.h" #include "components/supervised_user/core/common/pref_names.h" #include "content/public/browser/render_frame_host.h" #include "extensions/browser/blocklist_extension_prefs.h" @@ -55,11 +56,13 @@ #include "extensions/common/manifest_handlers/icons_handler.h" #include "extensions/common/manifest_handlers/offline_enabled_info.h" #include "extensions/common/manifest_handlers/options_page_info.h" +#include "extensions/common/manifest_handlers/permissions_parser.h" #include "extensions/common/manifest_url_handlers.h" #include "extensions/common/permissions/permission_message_provider.h" #include "extensions/common/permissions/permission_message_util.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/grit/extensions_browser_resources.h" +#include "extensions/strings/grit/extensions_strings.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -142,15 +145,15 @@ developer::RuntimeError ConstructRuntimeError(const RuntimeError& error) { developer::RuntimeError result; PopulateErrorBase(error, &result); switch (error.level()) { - case logging::LOG_VERBOSE: - case logging::LOG_INFO: + case logging::LOGGING_VERBOSE: + case logging::LOGGING_INFO: result.severity = developer::ERROR_LEVEL_LOG; break; - case logging::LOG_WARNING: + case logging::LOGGING_WARNING: result.severity = developer::ERROR_LEVEL_WARN; break; - case logging::LOG_FATAL: - case logging::LOG_ERROR: + case logging::LOGGING_FATAL: + case logging::LOGGING_ERROR: result.severity = developer::ERROR_LEVEL_ERROR; break; default: @@ -311,6 +314,19 @@ developer::RuntimeHostPermissions CreateRuntimeHostPermissionsInfo( return runtime_host_permissions; } +// Returns if the extension can access site data. This checks for host +// permissions, activeTab and API permissions that will surface a warning for +// all hosts access. +bool CanAccessSiteData(PermissionsManager* permissions_manager, + const Extension& extension) { + return permissions_manager->ExtensionRequestsHostPermissionsOrActiveTab( + extension) || + PermissionsParser::GetRequiredPermissions(&extension) + .ShouldWarnAllHosts() || + PermissionsParser::GetOptionalPermissions(&extension) + .ShouldWarnAllHosts(); +} + // Populates the |permissions| data for the given |extension|. void AddPermissionsInfo(content::BrowserContext* browser_context, const Extension& extension, @@ -331,6 +347,10 @@ void AddPermissionsInfo(content::BrowserContext* browser_context, PermissionsManager* permissions_manager = PermissionsManager::Get(browser_context); + + permissions->can_access_site_data = + CanAccessSiteData(permissions_manager, extension); + bool enable_runtime_host_permissions = permissions_manager->CanAffectExtension(extension); @@ -401,6 +421,8 @@ void ExtensionInfoGenerator::CreateExtensionInfo( state = developer::EXTENSION_STATE_DISABLED; else if ((ext = registry->terminated_extensions().GetByID(id)) != nullptr) state = developer::EXTENSION_STATE_TERMINATED; + else if ((ext = registry->blocklisted_extensions().GetByID(id)) != nullptr) + state = developer::EXTENSION_STATE_BLACKLISTED; if (ext && ui_util::ShouldDisplayInExtensionSettings(*ext)) CreateExtensionInfoHelper(*ext, state); @@ -539,7 +561,8 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper( absl::optional<CWSInfoService::CWSInfo> cws_info = cws_info_service_->GetCWSInfo(extension); if (cws_info.has_value()) { - info->safety_check_text = CreateSafetyCheckDisplayString(*cws_info); + info->safety_check_text = + CreateSafetyCheckDisplayString(*cws_info, state); } } @@ -639,6 +662,11 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper( info->incognito_access.is_active = util::IsIncognitoEnabled(extension.id(), browser_context_); + // Safety check warning acknowledge status. + extension_prefs_->ReadPrefAsBoolean( + extension.id(), extensions::kPrefAcknowledgeSafetyCheckWarning, + &info->acknowledge_safety_check_warning); + // Install warnings, but only if unpacked, the error console isn't enabled // (otherwise it shows these), and we're in developer mode (normal users don't // need to see these). @@ -763,6 +791,18 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper( SitePermissionsHelper(profile).ShowAccessRequestsInToolbar( extension.id()); + // Pinned to toolbar. + // TODO(crbug.com/1477884): Currently this information is only shown for + // enabled extensions as only enabled extensions can have actions. However, + // this information can be found in prefs, so disabled extensiosn can be + // included as well. + ToolbarActionsModel* toolbar_actions_model = + ToolbarActionsModel::Get(profile); + if (toolbar_actions_model->HasAction(extension.id())) { + info->pinned_to_toolbar = + toolbar_actions_model->IsActionPinned(extension.id()); + } + // The icon. ExtensionResource icon = IconsInfo::GetIconResource(&extension, @@ -786,8 +826,8 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper( developer::SafetyCheckStrings ExtensionInfoGenerator::CreateSafetyCheckDisplayString( - CWSInfoService::CWSInfo& cws_info) { - // TODO(crbug.com/1432194): Add panel_page_string logic. + const CWSInfoService::CWSInfo& cws_info, + developer::ExtensionState state) { developer::SafetyCheckStrings display_strings; std::string detail_page_string; std::string panel_page_string; @@ -796,10 +836,16 @@ ExtensionInfoGenerator::CreateSafetyCheckDisplayString( case CWSInfoService::CWSViolationType::kMalware: detail_page_string = l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_MALWARE); + panel_page_string = l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_MALWARE); break; case CWSInfoService::CWSViolationType::kPolicy: detail_page_string = l10n_util::GetStringUTF8( IDS_SAFETY_CHECK_EXTENSIONS_POLICY_VIOLATION); + panel_page_string = state == developer::EXTENSION_STATE_ENABLED + ? l10n_util::GetStringUTF8( + IDS_EXTENSIONS_SC_POLICY_VIOLATION_ON) + : l10n_util::GetStringUTF8( + IDS_EXTENSIONS_SC_POLICY_VIOLATION_OFF); break; case CWSInfoService::CWSViolationType::kNone: case CWSInfoService::CWSViolationType::kMinorPolicy: @@ -807,6 +853,10 @@ ExtensionInfoGenerator::CreateSafetyCheckDisplayString( if (cws_info.unpublished_long_ago) { detail_page_string = l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_UNPUBLISHED); + panel_page_string = + state == developer::EXTENSION_STATE_ENABLED + ? l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_UNPUBLISHED_ON) + : l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_UNPUBLISHED_OFF); } break; } diff --git a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h index 619b73d07cf..d0e2d6ae5e1 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h +++ b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator.h @@ -74,17 +74,18 @@ class ExtensionInfoGenerator { static std::vector<URLPattern> GetDistinctHosts( const URLPatternSet& patterns); + // Construct the needed strings for the safety check on the + // extensions page. + static api::developer_private::SafetyCheckStrings + CreateSafetyCheckDisplayString(const CWSInfoService::CWSInfo& cws_info, + api::developer_private::ExtensionState state); + private: // Creates an ExtensionInfo for the given |extension| and |state|, and // asynchronously adds it to the |list|. void CreateExtensionInfoHelper(const Extension& extension, api::developer_private::ExtensionState state); - // Construct the needed strings for the safety check on the - // extensions page. - static api::developer_private::SafetyCheckStrings - CreateSafetyCheckDisplayString(CWSInfoService::CWSInfo& cws_info); - // Callback for the asynchronous image loading. void OnImageLoaded( std::unique_ptr<api::developer_private::ExtensionInfo> info, diff --git a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc index 74dd9d5eee7..e577afbff5f 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc +++ b/chromium/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc @@ -24,6 +24,7 @@ #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/cws_info_service.h" #include "chrome/browser/extensions/error_console/error_console.h" +#include "chrome/browser/extensions/extension_action_test_util.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service_test_with_install.h" #include "chrome/browser/extensions/extension_util.h" @@ -31,10 +32,11 @@ #include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/extensions/scripting_permissions_modifier.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/toolbar/toolbar_actions_model.h" #include "chrome/common/extensions/api/developer_private.h" #include "chrome/common/pref_names.h" #include "chrome/grit/chromium_strings.h" -#include "chrome/grit/google_chrome_strings.h" +#include "chrome/grit/generated_resources.h" #include "components/crx_file/id_util.h" #include "components/supervised_user/core/common/buildflags.h" #include "extensions/browser/extension_registry.h" @@ -124,6 +126,7 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestWithInstall { void SetUp() override { ExtensionServiceTestWithInstall::SetUp(); InitializeExtensionService(GetExtensionServiceInitParams()); + extension_action_test_util::CreateToolbarModelForProfile(profile()); } // Returns the initialization parameters for the extension service. @@ -141,12 +144,6 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestWithInstall { std::move(quit_closure_).Run(); } - api::developer_private::SafetyCheckStrings CreateSafetyCheckDisplayStringTest( - CWSInfoService::CWSInfo& cws_info) { - return ExtensionInfoGenerator(browser_context()) - .CreateSafetyCheckDisplayString(cws_info); - } - std::unique_ptr<developer::ExtensionInfo> GenerateExtensionInfo( const std::string& extension_id) { std::unique_ptr<developer::ExtensionInfo> info; @@ -211,6 +208,10 @@ class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestWithInstall { const base::FilePath& extension_path, mojom::ManifestLocation location) { ChromeTestExtensionLoader loader(browser_context()); + + // Unit tests are single process and as such, attempting to wait for an + // extension renderer process will cause the test to time out. + loader.set_wait_for_renderers(false); loader.set_location(location); loader.set_creation_flags(Extension::REQUIRE_KEY); scoped_refptr<const Extension> extension = @@ -298,13 +299,13 @@ TEST_F(ExtensionInfoGeneratorUnitTest, BasicInfoTest) { error_console->ReportError(std::make_unique<RuntimeError>( extension->id(), false, u"source", u"message", StackTrace(1, StackFrame(1, 1, u"source", u"function")), kContextUrl, - logging::LOG_ERROR, 1, 1)); + logging::LOGGING_ERROR, 1, 1)); error_console->ReportError(std::make_unique<ManifestError>( extension->id(), u"message", u"key", std::u16string())); error_console->ReportError(std::make_unique<RuntimeError>( extension->id(), false, u"source", u"message", StackTrace(1, StackFrame(1, 1, u"source", u"function")), kContextUrl, - logging::LOG_WARNING, 1, 1)); + logging::LOGGING_WARNING, 1, 1)); // It's not feasible to validate every field here, because that would be // a duplication of the logic in the method itself. Instead, test a handful @@ -325,6 +326,7 @@ TEST_F(ExtensionInfoGeneratorUnitTest, BasicInfoTest) { EXPECT_TRUE(info->incognito_access.is_enabled); EXPECT_FALSE(info->incognito_access.is_active); EXPECT_TRUE(base::StartsWith(info->icon_url, "data:image/png;base64,")); + EXPECT_FALSE(*info->pinned_to_toolbar); // Strip out the kHostReadWrite permission created by the extension requesting // host permissions above; runtime host permissions mean these are always @@ -357,6 +359,7 @@ TEST_F(ExtensionInfoGeneratorUnitTest, BasicInfoTest) { ++i; } EXPECT_TRUE(info->permissions.runtime_host_permissions); + EXPECT_TRUE(info->permissions.can_access_site_data); ASSERT_EQ(2u, info->runtime_errors.size()); const api::developer_private::RuntimeError& runtime_error = @@ -477,10 +480,11 @@ TEST_F(ExtensionInfoGeneratorUnitTest, GenerateExtensionsJSONData) { #if !BUILDFLAG(IS_CHROMEOS_ASH) // Test Extension2 - extension_path = data_dir().AppendASCII("good") - .AppendASCII("Extensions") - .AppendASCII("hpiknbiabeeppbpihjehijgoemciehgk") - .AppendASCII("2"); + extension_path = data_dir() + .AppendASCII("good") + .AppendASCII("Extensions") + .AppendASCII("hpiknbiabeeppbpihjehijgoemciehgk") + .AppendASCII("2"); { // It's OK to have duplicate URLs, so long as the IDs are different. @@ -512,27 +516,60 @@ TEST_F(ExtensionInfoGeneratorUnitTest, GenerateExtensionsJSONData) { // Test the safety check display strings TEST_F(ExtensionInfoGeneratorUnitTest, SafetyCheckStringsTest) { - CWSInfoService::CWSInfo cws_info; - cws_info.is_present = true; - cws_info.violation_type = CWSInfoService::CWSViolationType::kMalware; - cws_info.unpublished_long_ago = true; - developer::SafetyCheckStrings display_strings = - CreateSafetyCheckDisplayStringTest(cws_info); - EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_MALWARE), - display_strings.detail_string); - cws_info.is_present = true; - cws_info.violation_type = CWSInfoService::CWSViolationType::kPolicy; - cws_info.unpublished_long_ago = true; - display_strings = CreateSafetyCheckDisplayStringTest(cws_info); - EXPECT_EQ( - l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_POLICY_VIOLATION), - display_strings.detail_string); - cws_info.is_present = true; - cws_info.violation_type = CWSInfoService::CWSViolationType::kNone; - cws_info.unpublished_long_ago = true; - display_strings = CreateSafetyCheckDisplayStringTest(cws_info); - EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_UNPUBLISHED), - display_strings.detail_string); + { + CWSInfoService::CWSInfo cws_info; + cws_info.is_present = true; + cws_info.violation_type = CWSInfoService::CWSViolationType::kMalware; + cws_info.unpublished_long_ago = true; + developer::SafetyCheckStrings display_strings = + ExtensionInfoGenerator::CreateSafetyCheckDisplayString( + cws_info, developer::EXTENSION_STATE_DISABLED); + EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_MALWARE), + display_strings.detail_string); + EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_MALWARE), + display_strings.panel_string); + } + { + CWSInfoService::CWSInfo cws_info; + cws_info.is_present = true; + cws_info.violation_type = CWSInfoService::CWSViolationType::kPolicy; + cws_info.unpublished_long_ago = true; + developer::SafetyCheckStrings display_strings = + ExtensionInfoGenerator::CreateSafetyCheckDisplayString( + cws_info, developer::EXTENSION_STATE_DISABLED); + EXPECT_EQ( + l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_POLICY_VIOLATION), + display_strings.detail_string); + EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_POLICY_VIOLATION_OFF), + display_strings.panel_string); + } + { + CWSInfoService::CWSInfo cws_info; + cws_info.is_present = true; + cws_info.violation_type = CWSInfoService::CWSViolationType::kPolicy; + developer::SafetyCheckStrings display_strings = + ExtensionInfoGenerator::CreateSafetyCheckDisplayString( + cws_info, developer::EXTENSION_STATE_ENABLED); + EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_POLICY_VIOLATION_ON), + display_strings.panel_string); + } + { + CWSInfoService::CWSInfo cws_info; + cws_info.is_present = true; + cws_info.violation_type = CWSInfoService::CWSViolationType::kNone; + cws_info.unpublished_long_ago = true; + developer::SafetyCheckStrings display_strings = + ExtensionInfoGenerator::CreateSafetyCheckDisplayString( + cws_info, developer::EXTENSION_STATE_DISABLED); + EXPECT_EQ(l10n_util::GetStringUTF8(IDS_SAFETY_CHECK_EXTENSIONS_UNPUBLISHED), + display_strings.detail_string); + EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_UNPUBLISHED_OFF), + display_strings.panel_string); + display_strings = ExtensionInfoGenerator::CreateSafetyCheckDisplayString( + cws_info, developer::EXTENSION_STATE_ENABLED); + EXPECT_EQ(l10n_util::GetStringUTF8(IDS_EXTENSIONS_SC_UNPUBLISHED_ON), + display_strings.panel_string); + } } TEST_F(ExtensionInfoGeneratorUnitTest, SafetyCheckEmptyStringTest) { @@ -541,7 +578,8 @@ TEST_F(ExtensionInfoGeneratorUnitTest, SafetyCheckEmptyStringTest) { cws_info.violation_type = CWSInfoService::CWSViolationType::kNone; cws_info.unpublished_long_ago = false; developer::SafetyCheckStrings display_strings; - display_strings = CreateSafetyCheckDisplayStringTest(cws_info); + display_strings = ExtensionInfoGenerator::CreateSafetyCheckDisplayString( + cws_info, developer::EXTENSION_STATE_DISABLED); EXPECT_EQ(display_strings.detail_string, ""); EXPECT_EQ(display_strings.panel_string, ""); } @@ -602,6 +640,7 @@ TEST_F(ExtensionInfoGeneratorUnitTest, RuntimeHostPermissions) { "no urls", base::Value::List(), ManifestLocation::kInternal); info = GenerateExtensionInfo(no_urls_extension->id()); EXPECT_FALSE(info->permissions.runtime_host_permissions); + EXPECT_FALSE(info->permissions.can_access_site_data); } // Tests that specific_site_controls is correctly populated when permissions @@ -882,6 +921,27 @@ TEST_F(ExtensionInfoGeneratorUnitTest, ActiveTabFileUrls) { EXPECT_FALSE(info->file_access.is_active); } +// Test that `permissions.can_access_site_data` is set to true for extensions +// with API permissions that can access site data, without specifying host +// permissions. +TEST_F(ExtensionInfoGeneratorUnitTest, + CanAccessSiteDataWithoutHostPermissions) { + scoped_refptr<const Extension> active_tab_extension = + CreateExtension("activeTab", base::Value::List().Append("activeTab"), + ManifestLocation::kInternal); + scoped_refptr<const Extension> debugger_extension = + CreateExtension("activeTab", base::Value::List().Append("debugger"), + ManifestLocation::kInternal); + + std::unique_ptr<developer::ExtensionInfo> active_tab_info = + GenerateExtensionInfo(active_tab_extension->id()); + std::unique_ptr<developer::ExtensionInfo> debugger_info = + GenerateExtensionInfo(debugger_extension->id()); + + EXPECT_TRUE(active_tab_info->permissions.can_access_site_data); + EXPECT_TRUE(debugger_info->permissions.can_access_site_data); +} + // Tests that blocklisted extensions are returned by the ExtensionInfoGenerator. TEST_F(ExtensionInfoGeneratorUnitTest, Blocklisted) { const scoped_refptr<const Extension> extension1 = CreateExtension( @@ -911,6 +971,11 @@ TEST_F(ExtensionInfoGeneratorUnitTest, Blocklisted) { ASSERT_NE(nullptr, info2); EXPECT_EQ(developer::EXTENSION_STATE_BLACKLISTED, info1->state); EXPECT_EQ(developer::EXTENSION_STATE_ENABLED, info2->state); + + // Verify getExtensionInfo() returns data on blocklisted extensions. + auto info3 = GenerateExtensionInfo(id1); + ASSERT_NE(nullptr, info3); + EXPECT_EQ(developer::EXTENSION_STATE_BLACKLISTED, info3->state); } // Test generating extension action commands properly. @@ -987,6 +1052,32 @@ TEST_F(ExtensionInfoGeneratorUnitTest, EXPECT_FALSE(info->disable_reasons.parent_disabled_permissions); } +// Test that the generator returns if the extension can be pinned to the toolbar +// and if it can, whether or not it's pinned. +TEST_F(ExtensionInfoGeneratorUnitTest, IsPinnedToToolbar) { + // By default, the extension is not pinned to the toolbar but can be. + const scoped_refptr<const Extension> extension = CreateExtension( + "test1", base::Value::List(), ManifestLocation::kInternal); + std::unique_ptr<developer::ExtensionInfo> info = + GenerateExtensionInfo(extension->id()); + EXPECT_FALSE(*info->pinned_to_toolbar); + + // Pin the extension to the toolbar and test that this is reflected in the + // generated info. + ToolbarActionsModel* toolbar_actions_model = + ToolbarActionsModel::Get(profile()); + toolbar_actions_model->SetActionVisibility(extension->id(), true); + info = GenerateExtensionInfo(extension->id()); + EXPECT_TRUE(*info->pinned_to_toolbar); + + // Disable the extension. Since disabled extensions have no action, the + // `pinned_to_toolbar` field should not exist. + service()->DisableExtension(extension->id(), + disable_reason::DISABLE_USER_ACTION); + info = GenerateExtensionInfo(extension->id()); + EXPECT_FALSE(info->pinned_to_toolbar.has_value()); +} + #if BUILDFLAG(ENABLE_SUPERVISED_USERS) // Tests for supervised users (child accounts). Supervised users are not allowed // to install apps or extensions unless their parent approves. diff --git a/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc index fc00e8031ce..ee717af4004 100644 --- a/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/values.h" +#include "chrome/browser/download/download_browsertest_utils.h" #include "chrome/browser/extensions/api/downloads/downloads_api.h" #include <stddef.h> @@ -205,7 +206,7 @@ class DownloadsEventsListener : public EventRouter::TestObserver { } private: - raw_ptr<Profile, DanglingUntriaged> profile_; + raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_; std::string event_name_; std::string json_args_; base::Value args_; @@ -231,7 +232,8 @@ class DownloadsEventsListener : public EventRouter::TestObserver { } // extensions::EventRouter::TestObserver: - void OnDidDispatchEventToProcess(const extensions::Event& event) override {} + void OnDidDispatchEventToProcess(const extensions::Event& event, + int process_id) override {} bool WaitFor(Profile* profile, const std::string& event_name, @@ -274,7 +276,7 @@ class DownloadsEventsListener : public EventRouter::TestObserver { base::Time last_wait_; std::unique_ptr<Event> waiting_for_; base::circular_deque<std::unique_ptr<Event>> events_; - raw_ptr<Profile, DanglingUntriaged> profile_; + raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_; }; // Object waiting for a download open event. @@ -420,6 +422,7 @@ class DownloadExtensionTest : public ExtensionApiTest { DownloadTestFileActivityObserver observer(incognito_browser_->profile()); observer.EnableFileChooser(false); } + SetPromptForDownload(incognito_browser_, false); current_browser_ = incognito_browser_; if (events_listener_.get()) events_listener_->UpdateProfile(current_browser()->profile()); @@ -758,8 +761,8 @@ class DownloadExtensionTest : public ExtensionApiTest { raw_ptr<const Extension, DanglingUntriaged> extension_; raw_ptr<const Extension, DanglingUntriaged> second_extension_; - raw_ptr<Browser, DanglingUntriaged> incognito_browser_; - raw_ptr<Browser, DanglingUntriaged> current_browser_; + raw_ptr<Browser, AcrossTasksDanglingUntriaged> incognito_browser_; + raw_ptr<Browser, AcrossTasksDanglingUntriaged> current_browser_; std::unique_ptr<DownloadsEventsListener> events_listener_; std::unique_ptr<net::test_server::ControllableHttpResponse> first_download_; diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc index 65b7a109039..54dd7a0e223 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc @@ -71,6 +71,8 @@ class EnterpriseDeviceAttributesApiAshTest DeviceSettingsTestBase::SetUp(); + testing_profile_ = profile_manager_.CreateTestingProfile(kAccountId); + switch (GetParam()) { case TestProfileChoice::kSigninProfile: TestingProfile* signin_profile; @@ -114,7 +116,7 @@ class EnterpriseDeviceAttributesApiAshTest AccountId account_id = AccountId::FromUserEmail(kAccountId); user_manager_->AddUserWithAffiliationAndTypeAndProfile( account_id, is_affiliated, user_manager::USER_TYPE_REGULAR, - profile_.get()); + testing_profile_); user_manager_->LoginUser(account_id); } @@ -128,8 +130,9 @@ class EnterpriseDeviceAttributesApiAshTest } } - private: + protected: TestingProfileManager profile_manager_; + raw_ptr<TestingProfile> testing_profile_; std::unique_ptr<crosapi::CrosapiManager> manager_; std::unique_ptr<policy::FakeDeviceAttributes> device_attributes_; }; @@ -140,7 +143,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, GetDirectoryDeviceIdFunction) { absl::optional<base::Value> result = api_test_utils::RunFunctionAndReturnSingleResult( - function.get(), /*args=*/"[]", profile_.get()); + function.get(), /*args=*/"[]", testing_profile_); ASSERT_TRUE(result->is_string()); EXPECT_EQ( IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeDirectoryApiId : "", @@ -153,7 +156,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, GetDeviceSerialNumberFunction) { absl::optional<base::Value> result = api_test_utils::RunFunctionAndReturnSingleResult( - function.get(), /*args=*/"[]", profile_.get()); + function.get(), /*args=*/"[]", testing_profile_); ASSERT_TRUE(result->is_string()); EXPECT_EQ(IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeSerialNumber : "", result->GetString()); @@ -165,7 +168,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, GetDeviceAssetIdFunction) { absl::optional<base::Value> result = api_test_utils::RunFunctionAndReturnSingleResult( - function.get(), /*args=*/"[]", profile_.get()); + function.get(), /*args=*/"[]", testing_profile_); ASSERT_TRUE(result->is_string()); EXPECT_EQ(IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeAssetId : "", result->GetString()); @@ -178,7 +181,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, absl::optional<base::Value> result = api_test_utils::RunFunctionAndReturnSingleResult( - function.get(), /*args=*/"[]", profile_.get()); + function.get(), /*args=*/"[]", testing_profile_); ASSERT_TRUE(result->is_string()); EXPECT_EQ( IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeAnnotatedLocation : "", @@ -191,7 +194,7 @@ TEST_P(EnterpriseDeviceAttributesApiAshTest, GetDeviceHostnameFunction) { absl::optional<base::Value> result = api_test_utils::RunFunctionAndReturnSingleResult( - function.get(), /*args=*/"[]", profile_.get()); + function.get(), /*args=*/"[]", testing_profile_); ASSERT_TRUE(result->is_string()); EXPECT_EQ(IsSigninProfileOrBelongsToAffiliatedUser() ? kFakeHostname : "", result->GetString()); diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc index 26a26ad5314..20d4c806a37 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_lacros_apitest.cc @@ -1,4 +1,4 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. +// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc index a44a8347968..fd58a396cbb 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc @@ -172,12 +172,13 @@ class EPKChallengeKeyTestBase : public BrowserWithTestWindowTest { scoped_refptr<const extensions::Extension> extension_; ash::StubInstallAttributes stub_install_attributes_; // fake_user_manager_ is owned by user_manager_enabler_. - raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_ = - nullptr; + raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh> + fake_user_manager_ = nullptr; user_manager::ScopedUserManager user_manager_enabler_; ash::platform_keys::MockKeyPermissionsManager key_permissions_manager_; - raw_ptr<PrefService, ExperimentalAsh> prefs_ = nullptr; - raw_ptr<ash::attestation::MockTpmChallengeKey, ExperimentalAsh> + raw_ptr<PrefService, DanglingUntriaged | ExperimentalAsh> prefs_ = nullptr; + raw_ptr<ash::attestation::MockTpmChallengeKey, + DanglingUntriaged | ExperimentalAsh> mock_tpm_challenge_key_ = nullptr; }; diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc index 216beea8fd3..82f98dca30d 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_apitest_nss.cc @@ -26,6 +26,7 @@ #include "chrome/browser/net/nss_service_factory.h" #include "chrome/browser/policy/extension_force_install_mixin.h" #include "chrome/common/chrome_paths.h" +#include "chromeos/ash/components/chaps_util/test_util.h" #include "components/policy/core/common/mock_configuration_policy_provider.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -271,8 +272,7 @@ class EnterprisePlatformKeysTest // Allows tests to generate software-backed keys by configuring fake ChapsUtil // instances to be created in its constructor (and undoing the change in its // destructor). - ash::platform_keys::test_util::ScopedChapsUtilOverride - scoped_chaps_util_override_; + chromeos::ScopedChapsUtilOverride scoped_chaps_util_override_; }; } // namespace @@ -446,8 +446,7 @@ class EnterprisePlatformKeysLoginScreenTest // Allows tests to generate software-backed keys by configuring fake ChapsUtil // instances to be created in its constructor (and undoing the change in its // destructor). - ash::platform_keys::test_util::ScopedChapsUtilOverride - scoped_chaps_util_override_; + chromeos::ScopedChapsUtilOverride scoped_chaps_util_override_; }; IN_PROC_BROWSER_TEST_P(EnterprisePlatformKeysLoginScreenTest, Basic) { diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc index cd924613ff0..7b32da90c64 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc @@ -73,10 +73,10 @@ class EPKPChallengeKeyTestBase : public BrowserWithTestWindowTest { scoped_refptr<const Extension> extension_; // fake_user_manager_ is owned by user_manager_enabler_. - raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_ = - nullptr; + raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh> + fake_user_manager_ = nullptr; user_manager::ScopedUserManager user_manager_enabler_; - raw_ptr<PrefService, ExperimentalAsh> prefs_ = nullptr; + raw_ptr<PrefService, DanglingUntriaged | ExperimentalAsh> prefs_ = nullptr; }; class EPKPChallengeMachineKeyTest : public EPKPChallengeKeyTestBase { diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc index bf62e292d1d..d652d410065 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.cc @@ -30,7 +30,7 @@ #endif #if BUILDFLAG(IS_MAC) -#include "base/mac/foundation_util.h" +#include "base/apple/foundation_util.h" #include "chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.h" #include "crypto/apple_keychain.h" #endif @@ -170,7 +170,7 @@ int32_t ReadEncryptedSecret(std::string* password, bool force_recreate) { crypto::AppleKeychain keychain; UInt32 password_length = 0; void* password_data = nullptr; - base::ScopedCFTypeRef<SecKeychainItemRef> item_ref; + base::apple::ScopedCFTypeRef<SecKeychainItemRef> item_ref; status = keychain.FindGenericPassword( strlen(kServiceName), kServiceName, strlen(kAccountName), kAccountName, &password_length, &password_data, item_ref.InitializeInto()); diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc index 3cbbdf4cb79..594b680912d 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc @@ -590,7 +590,7 @@ IN_PROC_BROWSER_TEST_F(EnterpriseReportingPrivateApiTest, kOptions = base::StringPrintf( R"( - const test_hive = 'HKEY_LOCAL_MACHINE'; + const test_hive = 'HKEY_CURRENT_USER'; const registry_path = '%s'; const invalid_path = 'SOFTWARE\\Chromium\\DeviceTrust\\Invalid'; const valid_key = '%s'; @@ -624,9 +624,9 @@ IN_PROC_BROWSER_TEST_F(EnterpriseReportingPrivateApiTest, registry_path.c_str(), valid_key.c_str()); registry_util::RegistryOverrideManager registry_override_manager_; - registry_override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE); + registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER); - base::win::RegKey key(HKEY_LOCAL_MACHINE, + base::win::RegKey key(HKEY_CURRENT_USER, base::SysUTF8ToWide(registry_path).c_str(), KEY_ALL_ACCESS); ASSERT_TRUE(key.WriteValue(base::SysUTF8ToWide(valid_key).c_str(), 37) == diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc index 7841b19868e..d09fabda838 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/keychain_data_helper_mac.cc @@ -7,8 +7,8 @@ #include <CoreFoundation/CoreFoundation.h> #include <Security/Security.h> -#include "base/mac/foundation_util.h" -#include "base/mac/scoped_cftyperef.h" +#include "base/apple/foundation_util.h" +#include "base/apple/scoped_cftyperef.h" #include "base/strings/sys_string_conversions.h" namespace extensions { @@ -30,7 +30,7 @@ OSStatus CreateTargetAccess(CFStringRef service_name, return status; } - base::ScopedCFTypeRef<CFArrayRef> acl_list; + base::apple::ScopedCFTypeRef<CFArrayRef> acl_list; status = SecAccessCopyACLList(*access_ref, acl_list.InitializeInto()); if (status != noErr) { return status; @@ -39,8 +39,8 @@ OSStatus CreateTargetAccess(CFStringRef service_name, for (CFIndex i = 0; i < CFArrayGetCount(acl_list); ++i) { SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(acl_list, i); - base::ScopedCFTypeRef<CFArrayRef> app_list; - base::ScopedCFTypeRef<CFStringRef> description; + base::apple::ScopedCFTypeRef<CFArrayRef> app_list; + base::apple::ScopedCFTypeRef<CFStringRef> description; SecKeychainPromptSelector dummy_prompt_selector; status = SecACLCopyContents(acl, app_list.InitializeInto(), description.InitializeInto(), @@ -93,7 +93,7 @@ OSStatus WriteKeychainItem(const std::string& service_name, const_cast<char*>(account_name.data())}}; SecKeychainAttributeList attribute_list = {std::size(attributes), attributes}; - base::ScopedCFTypeRef<SecAccessRef> access_ref; + base::apple::ScopedCFTypeRef<SecAccessRef> access_ref; OSStatus status = CreateTargetAccess(base::SysUTF8ToCFStringRef(service_name), access_ref.InitializeInto()); if (status != noErr) { @@ -107,7 +107,7 @@ OSStatus WriteKeychainItem(const std::string& service_name, OSStatus VerifyKeychainForItemUnlocked(SecKeychainItemRef item_ref, bool* unlocked) { - base::ScopedCFTypeRef<SecKeychainRef> keychain; + base::apple::ScopedCFTypeRef<SecKeychainRef> keychain; OSStatus status = SecKeychainItemCopyKeychain(item_ref, keychain.InitializeInto()); if (status != noErr) { @@ -118,7 +118,7 @@ OSStatus VerifyKeychainForItemUnlocked(SecKeychainItemRef item_ref, } OSStatus VerifyDefaultKeychainUnlocked(bool* unlocked) { - base::ScopedCFTypeRef<SecKeychainRef> keychain; + base::apple::ScopedCFTypeRef<SecKeychainRef> keychain; OSStatus status = SecKeychainCopyDefault(keychain.InitializeInto()); if (status != noErr) { return status; diff --git a/chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc b/chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc index 5c3f547ae65..079f1f43e07 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc @@ -858,8 +858,9 @@ IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveFencedFrameTest, manager->GetRenderFrameHostsForExtension(extension->id()); const auto& it = base::ranges::find_if( hosts, &content::RenderFrameHost::IsInPrimaryMainFrame); - content::RenderFrameHost* primary_rfh = (it != hosts.end()) ? *it : nullptr; - ASSERT_TRUE(primary_rfh); + content::RenderFrameHost* primary_render_frame_host = + (it != hosts.end()) ? *it : nullptr; + ASSERT_TRUE(primary_render_frame_host); // Navigate the popup's fenced frame to a (cross-site) web page via its // parent, and wait for that page to send a message, which will ensure that @@ -867,19 +868,20 @@ IN_PROC_BROWSER_TEST_F(BrowserActionInteractiveFencedFrameTest, GURL foo_url(https_server.GetURL("a.test", "/popup_fencedframe.html")); content::TestNavigationObserver observer( - content::WebContents::FromRenderFrameHost(primary_rfh)); + content::WebContents::FromRenderFrameHost(primary_render_frame_host)); std::string script = "document.querySelector('fencedframe').config = new FencedFrameConfig('" + foo_url.spec() + "')"; - EXPECT_TRUE(ExecJs(primary_rfh, script)); + EXPECT_TRUE(ExecJs(primary_render_frame_host, script)); observer.WaitForNavigationFinished(); - content::RenderFrameHost* fenced_frame_rfh = - fenced_frame_test_helper().GetMostRecentlyAddedFencedFrame(primary_rfh); - ASSERT_TRUE(fenced_frame_rfh); + content::RenderFrameHost* fenced_frame_render_frame_host = + fenced_frame_test_helper().GetMostRecentlyAddedFencedFrame( + primary_render_frame_host); + ASSERT_TRUE(fenced_frame_render_frame_host); // Confirm that the new page (popup_fencedframe.html) is actually loaded. - content::DOMMessageQueue dom_message_queue(fenced_frame_rfh); + content::DOMMessageQueue dom_message_queue(fenced_frame_render_frame_host); std::string json; EXPECT_TRUE(dom_message_queue.WaitForMessage(&json)); EXPECT_EQ("\"DONE\"", json); diff --git a/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc index 0ca967e7b98..d268ada6388 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc @@ -23,6 +23,7 @@ #include "chrome/browser/ui/toolbar/toolbar_actions_model.h" #include "chrome/test/base/ui_test_utils.h" #include "components/sessions/content/session_tab_helper.h" +#include "components/version_info/channel.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" @@ -33,10 +34,12 @@ #include "extensions/browser/extension_action_manager.h" #include "extensions/browser/extension_icon_image.h" #include "extensions/browser/process_manager.h" +#include "extensions/browser/service_worker/service_worker_test_utils.h" #include "extensions/browser/state_store.h" #include "extensions/common/api/extension_action/action_info.h" #include "extensions/common/api/extension_action/action_info_test_util.h" #include "extensions/common/extension.h" +#include "extensions/common/features/feature_channel.h" #include "extensions/common/manifest_constants.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/result_catcher.h" @@ -104,7 +107,7 @@ class TestStateStoreObserver : public StateStore::TestObserver { } private: - std::string extension_id_; + ExtensionId extension_id_; std::map<std::string, int> updated_values_; base::ScopedObservation<StateStore, StateStore::TestObserver> @@ -1869,6 +1872,73 @@ IN_PROC_BROWSER_TEST_P(ActionAndBrowserActionAPITest, EXPECT_EQ("", action->GetExplicitlySetBadgeText(tab_id2)); } +class ExtensionActionStableChannelApiTest : public ExtensionActionAPITest { + public: + ExtensionActionStableChannelApiTest() = default; + ~ExtensionActionStableChannelApiTest() override = default; + + private: + ScopedCurrentChannel scoped_current_channel_{version_info::Channel::STABLE}; +}; + +// Tests that the action.openPopup() API is available to policy-installed +// extensions on stable. Since this is controlled through our features files +// (which are tested separately), this is more of a smoke test than an +// end-to-end test. +// TODO(https://crbug.com/1245093): Remove this test when the API is available +// for all extensions on stable. +IN_PROC_BROWSER_TEST_F(ExtensionActionStableChannelApiTest, + OpenPopupAvailabilityOnStableChannel) { + TestExtensionDir test_dir; + static constexpr char kManifest[] = + R"({ + "name": "Test", + "manifest_version": 3, + "version": "0.1", + "background": {"service_worker": "background.js"}, + "action": {} + })"; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), + "chrome.test.sendMessage('ready');"); + + auto is_open_popup_defined = [this](const Extension& extension) { + static constexpr char kScript[] = + R"(chrome.test.sendScriptResult(!!chrome.action.openPopup);)"; + return BackgroundScriptExecutor::ExecuteScript( + profile(), extension.id(), kScript, + BackgroundScriptExecutor::ResultCapture::kSendScriptResult); + }; + + // Technically, we don't need the "ready" listener here, but this ensures we + // don't cross streams with the policy extension loaded below (where we do + // need the listener). + ExtensionTestMessageListener non_policy_listener("ready"); + const Extension* non_policy_extension = + LoadExtension(test_dir.UnpackedPath()); + ASSERT_TRUE(non_policy_extension); + ASSERT_TRUE(non_policy_listener.WaitUntilSatisfied()); + + // Somewhat annoying: due to how our test helpers are written, + // `EXPECT_EQ(false, base::Value)` works, but EXPECT_FALSE(base::Value) does + // not. + EXPECT_EQ(false, is_open_popup_defined(*non_policy_extension)); + + // Unlike `LoadExtension()`, `InstallExtension()` doesn't wait for the service + // worker to be ready, so we need a few manual waiters. + base::FilePath packed_path = test_dir.Pack(); + service_worker_test_utils::TestRegistrationObserver registration_observer( + profile()); + ExtensionTestMessageListener policy_listener("ready"); + const Extension* policy_extension = InstallExtension( + packed_path, 1, mojom::ManifestLocation::kExternalPolicyDownload); + ASSERT_TRUE(policy_extension); + ASSERT_TRUE(policy_listener.WaitUntilSatisfied()); + registration_observer.WaitForRegistrationStored(); + + EXPECT_EQ(true, is_open_popup_defined(*policy_extension)); +} + INSTANTIATE_TEST_SUITE_P(All, MultiActionAPITest, testing::Values(ActionInfo::TYPE_ACTION, diff --git a/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc b/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc index eb76373357c..115b9112f04 100644 --- a/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc +++ b/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc @@ -32,7 +32,7 @@ #if BUILDFLAG(IS_MAC) #include <CoreFoundation/CoreFoundation.h> -#include "base/mac/foundation_util.h" +#include "base/apple/foundation_util.h" #endif #if BUILDFLAG(IS_CHROMEOS) diff --git a/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc b/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc index 219218e4914..403dac6742b 100644 --- a/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc +++ b/chromium/chrome/browser/extensions/api/file_system/chrome_file_system_delegate_ash.cc @@ -12,6 +12,7 @@ #include "base/functional/callback.h" #include "base/path_service.h" #include "chrome/browser/ash/file_manager/volume_manager.h" +#include "chrome/browser/ash/fileapi/file_system_backend.h" #include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/child_process_security_policy.h" @@ -25,7 +26,6 @@ #include "extensions/browser/extension_util.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" -#include "storage/browser/file_system/file_system_backend.h" #include "storage/browser/file_system/file_system_context.h" #include "storage/browser/file_system/file_system_url.h" #include "storage/browser/file_system/isolated_context.h" @@ -91,8 +91,7 @@ void OnConsentReceived(content::BrowserContext* browser_context, scoped_refptr<storage::FileSystemContext> file_system_context = util::GetStoragePartitionForExtensionId(origin.host(), browser_context) ->GetFileSystemContext(); - storage::ExternalFileSystemBackend* const backend = - file_system_context->external_backend(); + auto* const backend = ash::FileSystemBackend::Get(*file_system_context); DCHECK(backend); base::FilePath virtual_path; @@ -225,8 +224,7 @@ void ChromeFileSystemDelegateAsh::RequestFileSystem( scoped_refptr<storage::FileSystemContext> file_system_context = util::GetStoragePartitionForExtensionId(extension.id(), browser_context) ->GetFileSystemContext(); - storage::ExternalFileSystemBackend* const backend = - file_system_context->external_backend(); + auto* const backend = ash::FileSystemBackend::Get(*file_system_context); DCHECK(backend); base::FilePath virtual_path; diff --git a/chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc b/chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc index 8c0f3be44b1..7cf734507db 100644 --- a/chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc +++ b/chromium/chrome/browser/extensions/api/file_system/consent_provider_unittest.cc @@ -123,13 +123,13 @@ class FileSystemApiConsentProviderTest : public testing::Test { void TearDown() override { scoped_user_manager_enabler_.reset(); user_manager_ = nullptr; - testing_pref_service_.reset(); TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr); + testing_pref_service_.reset(); } protected: std::unique_ptr<TestingPrefServiceSimple> testing_pref_service_; - raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> + raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh> user_manager_; // Owned by the scope enabler. std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_enabler_; content::BrowserTaskEnvironment task_environment_; diff --git a/chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc b/chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc index 0b1b8777b0c..deeba61820c 100644 --- a/chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc +++ b/chromium/chrome/browser/extensions/api/file_system/file_entry_picker.cc @@ -41,7 +41,9 @@ FileEntryPicker::FileEntryPicker( base::FilePath::StringType(), owning_window, /*params=*/nullptr, caller); } -FileEntryPicker::~FileEntryPicker() = default; +FileEntryPicker::~FileEntryPicker() { + select_file_dialog_->ListenerDestroyed(); +} void FileEntryPicker::FileSelected(const base::FilePath& path, int index, diff --git a/chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc b/chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc index d925aefc663..55ebcb0b37f 100644 --- a/chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/file_system/file_system_apitest_chromeos.cc @@ -104,7 +104,7 @@ class ScopedAddListenerObserver : public EventRouter::Observer { } private: - const std::string extension_id_; + const ExtensionId extension_id_; base::OnceClosure callback_; const raw_ptr<EventRouter, ExperimentalAsh> event_router_; }; @@ -189,7 +189,7 @@ class FileSystemApiTestForDrive : public PlatformAppBrowserTest { base::ScopedTempDir drivefs_root_; base::FilePath drivefs_mount_point_; std::unique_ptr<drive::FakeDriveFsHelper> fake_drivefs_helper_; - raw_ptr<drive::DriveIntegrationService, ExperimentalAsh> + raw_ptr<drive::DriveIntegrationService, DanglingUntriaged | ExperimentalAsh> integration_service_ = nullptr; drive::DriveIntegrationServiceFactory::FactoryCallback create_drive_integration_service_; @@ -245,7 +245,8 @@ class FileSystemApiTestForRequestFileSystem : public PlatformAppBrowserTest { protected: base::ScopedTempDir temp_dir_; - raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_; + raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged | ExperimentalAsh> + fake_user_manager_; std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_; // Creates a testing file system in a testing directory. diff --git a/chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc index 566480dfdbf..6c46d85d16f 100644 --- a/chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc +++ b/chromium/chrome/browser/extensions/api/font_settings/font_settings_api.cc @@ -38,6 +38,7 @@ #include "extensions/browser/extension_prefs_helper.h" #include "extensions/browser/extension_prefs_helper_factory.h" #include "extensions/browser/extension_system.h" +#include "extensions/common/api/types.h" #include "extensions/common/error_utils.h" #if BUILDFLAG(IS_WIN) @@ -47,6 +48,7 @@ namespace extensions { namespace fonts = api::font_settings; +using extensions::api::types::ChromeSettingScope; namespace { @@ -278,7 +280,7 @@ ExtensionFunction::ResponseAction FontSettingsClearFontFunction::Run() { EXTENSION_FUNCTION_VALIDATE(profile->GetPrefs()->FindPreference(pref_path)); ExtensionPrefsHelper::Get(profile)->RemoveExtensionControlledPref( - extension_id(), pref_path, kExtensionPrefsScopeRegular); + extension_id(), pref_path, ChromeSettingScope::kRegular); return RespondNow(NoArguments()); } @@ -332,7 +334,7 @@ ExtensionFunction::ResponseAction FontSettingsSetFontFunction::Run() { EXTENSION_FUNCTION_VALIDATE(profile->GetPrefs()->FindPreference(pref_path)); ExtensionPrefsHelper::Get(profile)->SetExtensionControlledPref( - extension_id(), pref_path, kExtensionPrefsScopeRegular, + extension_id(), pref_path, ChromeSettingScope::kRegular, base::Value(params->details.font_id)); return RespondNow(NoArguments()); } @@ -383,7 +385,7 @@ ExtensionFunction::ResponseAction ClearFontPrefExtensionFunction::Run() { return RespondNow(Error(kSetFromIncognitoError)); ExtensionPrefsHelper::Get(profile)->RemoveExtensionControlledPref( - extension_id(), GetPrefName(), kExtensionPrefsScopeRegular); + extension_id(), GetPrefName(), ChromeSettingScope::kRegular); return RespondNow(NoArguments()); } @@ -419,7 +421,7 @@ ExtensionFunction::ResponseAction SetFontPrefExtensionFunction::Run() { EXTENSION_FUNCTION_VALIDATE(value); ExtensionPrefsHelper::Get(profile)->SetExtensionControlledPref( - extension_id(), GetPrefName(), kExtensionPrefsScopeRegular, + extension_id(), GetPrefName(), ChromeSettingScope::kRegular, value->Clone()); return RespondNow(NoArguments()); } diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc index f7a31c16327..593f8c1cbe4 100644 --- a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc +++ b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc @@ -6,26 +6,16 @@ #include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" -#include "base/strings/escape.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/extensions/api/identity/identity_api.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/account_consistency_mode_manager.h" -#include "chrome/browser/signin/account_reconcilor_factory.h" #include "chrome/browser/signin/google_accounts_private_api_host.h" #include "chrome/browser/signin/identity_manager_factory.h" -#include "chrome/common/chrome_features.h" -#include "components/signin/core/browser/account_reconcilor.h" -#include "components/signin/public/base/multilogin_parameters.h" -#include "components/signin/public/identity_manager/identity_manager.h" -#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" #include "content/public/browser/storage_partition.h" -#include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_auth_util.h" -#include "google_apis/gaia/gaia_urls.h" #include "net/cookies/cookie_util.h" #include "services/network/public/mojom/cookie_manager.mojom.h" -#include "url/gurl.h" #include "url/url_constants.h" #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -67,29 +57,11 @@ GaiaRemoteConsentFlow::~GaiaRemoteConsentFlow() { void GaiaRemoteConsentFlow::Start() { if (!web_flow_) { - web_flow_ = std::make_unique<WebAuthFlow>( - this, profile_, resolution_data_.url, WebAuthFlow::INTERACTIVE, - WebAuthFlow::GET_AUTH_TOKEN, user_gesture_); -#if BUILDFLAG(IS_CHROMEOS_LACROS) - // `profile_` may be nullptr in tests. - if (profile_ && - !base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) { - AccountReconcilorFactory::GetForProfile(profile_) - ->GetConsistencyCookieManager() - ->AddExtraCookieManager(GetCookieManagerForPartition()); - } -#endif + web_flow_ = + std::make_unique<WebAuthFlow>(this, profile_, resolution_data_.url, + WebAuthFlow::INTERACTIVE, user_gesture_); } - if (base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) { - StartWebFlow(); - return; - } - - SetAccountsInCookie(); -} - -void GaiaRemoteConsentFlow::StartWebFlow() { network::mojom::CookieManager* cookie_manager = GetCookieManagerForPartition(); net::CookieOptions options; @@ -104,34 +76,6 @@ void GaiaRemoteConsentFlow::StartWebFlow() { web_flow_started_ = true; } -void GaiaRemoteConsentFlow::OnSetAccountsComplete( - signin::SetAccountsInCookieResult result) { - // No need to inject account cookies when the flow is displayed in a browser - // tab. - DCHECK(!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)); - - set_accounts_in_cookie_task_.reset(); - if (web_flow_started_) { - return; - } - - if (result != signin::SetAccountsInCookieResult::kSuccess) { - GaiaRemoteConsentFlowFailed( - GaiaRemoteConsentFlow::Failure::SET_ACCOUNTS_IN_COOKIE_FAILED); - return; - } - - identity_api_set_consent_result_subscription_ = - IdentityAPI::GetFactoryInstance() - ->Get(profile_) - ->RegisterOnSetConsentResultCallback( - base::BindRepeating(&GaiaRemoteConsentFlow::OnConsentResultSet, - base::Unretained(this))); - - scoped_observation_.Observe(IdentityManagerFactory::GetForProfile(profile_)); - StartWebFlow(); -} - void GaiaRemoteConsentFlow::ReactToConsentResult( const std::string& consent_result) { bool consent_approved = false; @@ -151,21 +95,6 @@ void GaiaRemoteConsentFlow::ReactToConsentResult( delegate_->OnGaiaRemoteConsentFlowApproved(consent_result, gaia_id); } -void GaiaRemoteConsentFlow::OnConsentResultSet( - const std::string& consent_result, - const std::string& window_id) { - // JS hook in a browser tab calls `ReactToConsentResult()` directly. - DCHECK(!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)); - - if (!web_flow_ || window_id != web_flow_->GetAppWindowKey()) { - return; - } - - identity_api_set_consent_result_subscription_ = {}; - - ReactToConsentResult(consent_result); -} - void GaiaRemoteConsentFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) { GaiaRemoteConsentFlow::Failure gaia_failure; @@ -173,9 +102,6 @@ void GaiaRemoteConsentFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) { case WebAuthFlow::WINDOW_CLOSED: gaia_failure = GaiaRemoteConsentFlow::WINDOW_CLOSED; break; - case WebAuthFlow::USER_NAVIGATED_AWAY: - gaia_failure = GaiaRemoteConsentFlow::USER_NAVIGATED_AWAY; - break; case WebAuthFlow::LOAD_FAILED: case WebAuthFlow::TIMED_OUT: gaia_failure = GaiaRemoteConsentFlow::LOAD_FAILED; @@ -192,115 +118,22 @@ void GaiaRemoteConsentFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) { GaiaRemoteConsentFlowFailed(gaia_failure); } -content::StoragePartition* GaiaRemoteConsentFlow::GetStoragePartition() { - content::StoragePartition* storage_partition = web_flow_->GetGuestPartition(); - if (!storage_partition) { - // `web_flow_` doesn't have a guest partition only when the Auth Through - // Browser Tab flow is used. - DCHECK(base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)); - storage_partition = profile_->GetDefaultStoragePartition(); - } - - return storage_partition; -} - -std::unique_ptr<GaiaAuthFetcher> -GaiaRemoteConsentFlow::CreateGaiaAuthFetcherForPartition( - GaiaAuthConsumer* consumer, - const gaia::GaiaSource& source) { - return std::make_unique<GaiaAuthFetcher>( - consumer, source, - GetStoragePartition()->GetURLLoaderFactoryForBrowserProcess()); -} - network::mojom::CookieManager* GaiaRemoteConsentFlow::GetCookieManagerForPartition() { - return GetStoragePartition()->GetCookieManagerForBrowserProcess(); -} - -void GaiaRemoteConsentFlow::OnEndBatchOfRefreshTokenStateChanges() { - // No need to copy added accounts when showing the flow in a browser tab. - DCHECK(!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)); - -// On ChromeOS, new accounts are added through the account manager. They need to -// be pushed to the partition used by this flow explicitly. -// On Desktop, sign-in happens on the Web and a new account is directly added to -// this partition's cookie jar. An extra update triggered from here might change -// cookies order in the middle of the flow. This may lead to a bug like -// https://crbug.com/1112343. -#if BUILDFLAG(IS_CHROMEOS_ASH) - DCHECK(ash::IsAccountManagerAvailable(profile_)); - SetAccountsInCookie(); -#elif BUILDFLAG(IS_CHROMEOS_LACROS) - if (AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile_)) - SetAccountsInCookie(); -#endif + return profile_->GetDefaultStoragePartition() + ->GetCookieManagerForBrowserProcess(); } void GaiaRemoteConsentFlow::SetWebAuthFlowForTesting( std::unique_ptr<WebAuthFlow> web_auth_flow) { DetachWebAuthFlow(); web_flow_ = std::move(web_auth_flow); -#if BUILDFLAG(IS_CHROMEOS_LACROS) - // `profile_` may be nullptr in tests. - if (profile_) { - AccountReconcilorFactory::GetForProfile(profile_) - ->GetConsistencyCookieManager() - ->AddExtraCookieManager(GetCookieManagerForPartition()); - } -#endif } WebAuthFlow* GaiaRemoteConsentFlow::GetWebAuthFlowForTesting() const { return web_flow_.get(); } -void GaiaRemoteConsentFlow::SetAccountsInCookie() { - // No need to inject account cookies when the flow is displayed in a browser - // tab. - DCHECK(!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)); - - // Reset a task that is already in flight because it contains stale - // information. - if (set_accounts_in_cookie_task_) - set_accounts_in_cookie_task_.reset(); - - auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_); - std::vector<CoreAccountId> accounts; - if (IdentityAPI::GetFactoryInstance() - ->Get(profile_) - ->AreExtensionsRestrictedToPrimaryAccount()) { - CoreAccountId primary_account_id = - identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync); - accounts.push_back(primary_account_id); - } else { - auto chrome_accounts_with_refresh_tokens = - identity_manager->GetAccountsWithRefreshTokens(); - for (const auto& chrome_account : chrome_accounts_with_refresh_tokens) { - // An account in persistent error state would make multilogin fail. - // Showing only a subset of accounts seems to be a better alternative than - // failing with an error. - if (identity_manager->HasAccountWithRefreshTokenInPersistentErrorState( - chrome_account.account_id)) { - continue; - } - accounts.push_back(chrome_account.account_id); - } - } - - // base::Unretained() is safe here because this class owns - // |set_accounts_in_cookie_task_| that will eventually invoke this callback. - set_accounts_in_cookie_task_ = - identity_manager->GetAccountsCookieMutator() - ->SetAccountsInCookieForPartition( - this, - {gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER, - accounts}, - gaia::GaiaSource::kChrome, - base::BindOnce(&GaiaRemoteConsentFlow::OnSetAccountsComplete, - base::Unretained(this))); -} - void GaiaRemoteConsentFlow::GaiaRemoteConsentFlowFailed(Failure failure) { RecordResultHistogram(failure); delegate_->OnGaiaRemoteConsentFlowFailed(failure); @@ -310,26 +143,11 @@ void GaiaRemoteConsentFlow::DetachWebAuthFlow() { if (!web_flow_) return; -#if BUILDFLAG(IS_CHROMEOS_LACROS) - // `profile_` may be nullptr in tests. - if (profile_ && - !base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) { - AccountReconcilorFactory::GetForProfile(profile_) - ->GetConsistencyCookieManager() - ->RemoveExtraCookieManager(GetCookieManagerForPartition()); - } -#endif web_flow_.release()->DetachDelegateAndDelete(); } void GaiaRemoteConsentFlow::OnNavigationFinished( content::NavigationHandle* navigation_handle) { - // No need to create the receiver if we are not displaying the auth page - // through a Browser Tgab. - if (!base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) { - return; - } - GoogleAccountsPrivateApiHost::CreateReceiver( base::BindRepeating(&GaiaRemoteConsentFlow::ReactToConsentResult, weak_factory.GetWeakPtr()), diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h index e316a1dba95..b25f8fcd0fe 100644 --- a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h +++ b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h @@ -5,26 +5,17 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_GAIA_REMOTE_CONSENT_FLOW_H_ #define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_GAIA_REMOTE_CONSENT_FLOW_H_ -#include "base/callback_list.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" -#include "base/scoped_observation.h" #include "chrome/browser/extensions/api/identity/extension_token_key.h" #include "chrome/browser/extensions/api/identity/web_auth_flow.h" #include "components/signin/public/identity_manager/accounts_cookie_mutator.h" -#include "components/signin/public/identity_manager/identity_manager.h" -#include "components/signin/public/identity_manager/set_accounts_in_cookie_result.h" #include "content/public/browser/storage_partition.h" -#include "google_apis/gaia/core_account_id.h" -#include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_mint_token_flow.h" namespace extensions { -class GaiaRemoteConsentFlow - : public WebAuthFlow::Delegate, - public signin::AccountsCookieMutator::PartitionDelegate, - public signin::IdentityManager::Observer { +class GaiaRemoteConsentFlow : public WebAuthFlow::Delegate { public: // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. @@ -32,10 +23,12 @@ class GaiaRemoteConsentFlow NONE = 0, WINDOW_CLOSED = 1, LOAD_FAILED = 2, - SET_ACCOUNTS_IN_COOKIE_FAILED = 3, + // Deprecated: + // SET_ACCOUNTS_IN_COOKIE_FAILED = 3, INVALID_CONSENT_RESULT = 4, NO_GRANT = 5, - USER_NAVIGATED_AWAY = 6, + // Deprecated: + // USER_NAVIGATED_AWAY = 6, CANNOT_CREATE_WINDOW = 7, kMaxValue = CANNOT_CREATE_WINDOW }; @@ -65,14 +58,6 @@ class GaiaRemoteConsentFlow // Starts the flow by setting accounts in cookie. void Start(); - // Set accounts in cookie completion callback. - void OnSetAccountsComplete(signin::SetAccountsInCookieResult result); - - // setConsentResult() JavaScript callback when using an App Window to display - // the Auth page. - void OnConsentResultSet(const std::string& consent_result, - const std::string& window_id); - // Handles `consent_result` value when using either a Browser Tab or an App // Window to display the Auth page. void ReactToConsentResult(const std::string& consent_result); @@ -82,29 +67,15 @@ class GaiaRemoteConsentFlow void OnNavigationFinished( content::NavigationHandle* navigation_handle) override; - // signin::AccountsCookieMutator::PartitionDelegate: - std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcherForPartition( - - GaiaAuthConsumer* consumer, - const gaia::GaiaSource& source) override; - network::mojom::CookieManager* GetCookieManagerForPartition() override; - - // signin::IdentityManager::Observer: - void OnEndBatchOfRefreshTokenStateChanges() override; - void SetWebAuthFlowForTesting(std::unique_ptr<WebAuthFlow> web_auth_flow); WebAuthFlow* GetWebAuthFlowForTesting() const; private: - void StartWebFlow(); - - void SetAccountsInCookie(); - void GaiaRemoteConsentFlowFailed(Failure failure); void DetachWebAuthFlow(); - content::StoragePartition* GetStoragePartition(); + network::mojom::CookieManager* GetCookieManagerForPartition(); const raw_ptr<Delegate> delegate_; const raw_ptr<Profile> profile_; @@ -114,13 +85,6 @@ class GaiaRemoteConsentFlow std::unique_ptr<WebAuthFlow> web_flow_; bool web_flow_started_; - std::unique_ptr<signin::AccountsCookieMutator::SetAccountsInCookieTask> - set_accounts_in_cookie_task_; - base::CallbackListSubscription identity_api_set_consent_result_subscription_; - base::ScopedObservation<signin::IdentityManager, - signin::IdentityManager::Observer> - scoped_observation_{this}; - base::WeakPtrFactory<GaiaRemoteConsentFlow> weak_factory{this}; }; diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc index 0a0d118c20f..82d8d84bd83 100644 --- a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc @@ -5,11 +5,9 @@ #include "chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h" #include "base/strings/strcat.h" -#include "chrome/browser/extensions/api/identity/identity_private_api.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/ui/browser.h" -#include "chrome/common/chrome_features.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/signin/public/base/consent_level.h" #include "components/signin/public/identity_manager/identity_manager.h" @@ -17,7 +15,6 @@ #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_navigation_observer.h" -#include "extensions/browser/api_test_utils.h" #include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/fake_gaia.h" #include "google_apis/gaia/gaia_auth_test_util.h" @@ -57,9 +54,7 @@ class MockGaiaRemoteConsentFlowDelegate const std::string& gaia_id)); }; -class GaiaRemoteConsentFlowParamBrowserTest - : public InProcessBrowserTest, - public testing::WithParamInterface<bool> { +class GaiaRemoteConsentFlowParamBrowserTest : public InProcessBrowserTest { public: GaiaRemoteConsentFlowParamBrowserTest() : fake_gaia_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) { @@ -79,9 +74,6 @@ class GaiaRemoteConsentFlowParamBrowserTest fake_gaia_test_server()->AddDefaultHandlers(GetChromeTestDataDir()); fake_gaia_test_server_.RegisterRequestHandler(base::BindRepeating( &FakeGaia::HandleRequest, base::Unretained(&fake_gaia_))); - - scoped_feature_list_.InitWithFeatureState( - features::kWebAuthFlowInBrowserTab, GetParam()); } void SetUp() override { @@ -158,30 +150,14 @@ class GaiaRemoteConsentFlowParamBrowserTest } void SimulateConsentResult(const std::string& consent_value) { - // When the auth flow is using the browser tab, we are able to properly test - // the JS injected script since we rely on the Gaia Origin to filter out - // unwanted urls, and in the test we are overriding the value of Gaia - // Origin, so we can bypass the filter for testing. - if (base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) { - // JS function is properly called but returns nullptr. - ASSERT_EQ(nullptr, content::EvalJs( - flow()->GetWebAuthFlowForTesting()->web_contents(), - "window.OAuthConsent.setConsentResult(\"" + - consent_value + "\")")); - return; - } - - // Since we cannot bypass the filter that is added in the internal extension - // (in it's manifest) we do not directly test the JS function but instead - // the layer right above in the API through - // `IdentityPrivateSetConsentResultFunction`. - std::string consent_result = - "[\"" + consent_value + "\", \"" + - flow()->GetWebAuthFlowForTesting()->GetAppWindowKey() + "\"]"; - scoped_refptr<ExtensionFunction> func = - base::MakeRefCounted<IdentityPrivateSetConsentResultFunction>(); - ASSERT_TRUE( - api_test_utils::RunFunction(func.get(), consent_result, profile())); + // We are able to properly test the JS injected script since we rely on the + // Gaia Origin to filter out unwanted urls, and in the test we are + // overriding the value of Gaia Origin, so we can bypass the filter for + // testing. JS function is properly called but returns nullptr. + ASSERT_EQ(nullptr, content::EvalJs( + flow()->GetWebAuthFlowForTesting()->web_contents(), + "window.OAuthConsent.setConsentResult(\"" + + consent_value + "\")")); } MockGaiaRemoteConsentFlowDelegate& mock() { @@ -207,7 +183,7 @@ class GaiaRemoteConsentFlowParamBrowserTest base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest, +IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest, SimulateInvalidConsent) { LaunchAndWaitGaiaRemoteConsentFlow(); @@ -217,7 +193,7 @@ IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest, SimulateConsentResult("invalid_consent"); } -IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest, SimulateNoGrant) { +IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest, SimulateNoGrant) { LaunchAndWaitGaiaRemoteConsentFlow(); EXPECT_CALL(mock(), OnGaiaRemoteConsentFlowFailed( @@ -227,7 +203,7 @@ IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest, SimulateNoGrant) { SimulateConsentResult(declined_consent); } -IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest, +IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest, SimulateAccessGranted) { LaunchAndWaitGaiaRemoteConsentFlow(); @@ -238,15 +214,5 @@ IN_PROC_BROWSER_TEST_P(GaiaRemoteConsentFlowParamBrowserTest, SimulateConsentResult(approved_consent); } -INSTANTIATE_TEST_SUITE_P( - , - GaiaRemoteConsentFlowParamBrowserTest, - testing::Bool(), - [](const testing::TestParamInfo< - GaiaRemoteConsentFlowParamBrowserTest::ParamType>& info) { - return base::StrCat({"WebAuthFlowInBrowserTab_", - info.param ? "FeatureOn" : "FeatureOff"}); - }); - } // namespace extensions #endif // !BUILDFLAG(IS_CHROMEOS_LACROS) diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc index 8ce505e6ce8..fa754041c1c 100644 --- a/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc +++ b/chromium/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_unittest.cc @@ -7,11 +7,7 @@ #include <memory> #include <vector> -#include "base/run_loop.h" #include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.h" -#include "build/chromeos_buildflags.h" -#include "chrome/common/chrome_features.h" #include "content/public/test/browser_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -21,52 +17,35 @@ namespace extensions { const char kResultHistogramName[] = "Signin.Extensions.GaiaRemoteConsentFlowResult"; -const char kWindowKey[] = "window_key"; const char kGaiaId[] = "fake_gaia_id"; const char kConsentResult[] = "CAESCUVOQ1JZUFRFRBoMZmFrZV9nYWlhX2lk"; -class FakeWebAuthFlowWithWindowKey : public WebAuthFlow { +class FakeWebAuthFlow : public WebAuthFlow { public: - explicit FakeWebAuthFlowWithWindowKey(WebAuthFlow::Delegate* delegate, - std::string window_key) + explicit FakeWebAuthFlow(WebAuthFlow::Delegate* delegate) : WebAuthFlow(delegate, nullptr, GURL(), WebAuthFlow::INTERACTIVE, - WebAuthFlow::GET_AUTH_TOKEN, - /*user_gesture=*/true), - fake_window_key_(window_key) {} + /*user_gesture=*/true) {} - ~FakeWebAuthFlowWithWindowKey() override = default; + ~FakeWebAuthFlow() override = default; void Start() override {} - - const std::string& GetAppWindowKey() const override { - return fake_window_key_; - } - - private: - const std::string fake_window_key_; }; class TestGaiaRemoteConsentFlow : public GaiaRemoteConsentFlow { public: TestGaiaRemoteConsentFlow(GaiaRemoteConsentFlow::Delegate* delegate, const ExtensionTokenKey& token_key, - const RemoteConsentResolutionData& resolution_data, - const std::string& window_key) + const RemoteConsentResolutionData& resolution_data) : GaiaRemoteConsentFlow(delegate, nullptr, token_key, resolution_data, - /*user_gesture=*/true), - window_key_(window_key) { - SetWebAuthFlowForTesting( - std::make_unique<FakeWebAuthFlowWithWindowKey>(this, window_key_)); + /*user_gesture=*/true) { + SetWebAuthFlowForTesting(std::make_unique<FakeWebAuthFlow>(this)); } - - private: - const std::string window_key_; }; class MockGaiaRemoteConsentFlowDelegate @@ -90,13 +69,11 @@ class IdentityGaiaRemoteConsentFlowTest : public testing::Test { // deleted. } - std::unique_ptr<TestGaiaRemoteConsentFlow> CreateTestFlow( - const std::string& window_key) { - return CreateTestFlow(window_key, &delegate_); + std::unique_ptr<TestGaiaRemoteConsentFlow> CreateTestFlow() { + return CreateTestFlow(&delegate_); } std::unique_ptr<TestGaiaRemoteConsentFlow> CreateTestFlow( - const std::string& window_key, GaiaRemoteConsentFlow::Delegate* delegate) { CoreAccountInfo user_info; user_info.account_id = CoreAccountId::FromGaiaId("account_id"); @@ -107,8 +84,8 @@ class IdentityGaiaRemoteConsentFlowTest : public testing::Test { std::set<std::string>()); RemoteConsentResolutionData resolution_data; resolution_data.url = GURL("https://example.com/auth/"); - return std::make_unique<TestGaiaRemoteConsentFlow>( - delegate, token_key, resolution_data, window_key); + return std::make_unique<TestGaiaRemoteConsentFlow>(delegate, token_key, + resolution_data); } base::HistogramTester* histogram_tester() { return &histogram_tester_; } @@ -120,7 +97,7 @@ class IdentityGaiaRemoteConsentFlowTest : public testing::Test { }; TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult) { - std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey); + std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(); EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowApproved(kConsentResult, kGaiaId)); flow->ReactToConsentResult(kConsentResult); @@ -129,11 +106,9 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult) { } TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult_TwoWindows) { - std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey); - const char kWindowKey2[] = "window_key2"; + std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(); testing::StrictMock<MockGaiaRemoteConsentFlowDelegate> delegate2; - std::unique_ptr<TestGaiaRemoteConsentFlow> flow2 = - CreateTestFlow(kWindowKey2, &delegate2); + std::unique_ptr<TestGaiaRemoteConsentFlow> flow2 = CreateTestFlow(&delegate2); const char kConsentResult2[] = "CAESCkVOQ1JZUFRFRDI"; EXPECT_CALL(delegate2, OnGaiaRemoteConsentFlowApproved(kConsentResult2, "")); @@ -148,7 +123,7 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult_TwoWindows) { TEST_F(IdentityGaiaRemoteConsentFlowTest, InvalidConsentResult) { const char kInvalidConsentResult[] = "abc"; - std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey); + std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(); EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::INVALID_CONSENT_RESULT)); @@ -159,7 +134,7 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, InvalidConsentResult) { TEST_F(IdentityGaiaRemoteConsentFlowTest, NoGrant) { const char kNoGrantConsentResult[] = "CAA"; - std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey); + std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(); EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::NO_GRANT)); flow->ReactToConsentResult(kNoGrantConsentResult); @@ -168,7 +143,7 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, NoGrant) { } TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_WindowClosed) { - std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey); + std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(); EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::WINDOW_CLOSED)); flow->OnAuthFlowFailure(WebAuthFlow::Failure::WINDOW_CLOSED); @@ -177,7 +152,7 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_WindowClosed) { } TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_LoadFailed) { - std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey); + std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(); EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::LOAD_FAILED)); flow->OnAuthFlowFailure(WebAuthFlow::Failure::LOAD_FAILED); @@ -185,39 +160,4 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_LoadFailed) { GaiaRemoteConsentFlow::LOAD_FAILED, 1); } -// The following tests are only meaningful if the feature -// `kWebAuthFlowInBrowserTab` is disabled -class IdentityGaiaRemoteConsentFlowWebAuthFlowInBrowserTabOffTest - : public IdentityGaiaRemoteConsentFlowTest { - public: - IdentityGaiaRemoteConsentFlowWebAuthFlowInBrowserTabOffTest() { - scoped_feature_list_.InitAndDisableFeature( - features::kWebAuthFlowInBrowserTab); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -TEST_F(IdentityGaiaRemoteConsentFlowWebAuthFlowInBrowserTabOffTest, - ConsentResult_WrongWindowIgnored) { - std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey); - // No call is expected. - flow->OnConsentResultSet(kConsentResult, "another_window_key"); -} - -TEST_F(IdentityGaiaRemoteConsentFlowWebAuthFlowInBrowserTabOffTest, - SetAccountsFailure) { - std::unique_ptr<TestGaiaRemoteConsentFlow> flow = CreateTestFlow(kWindowKey); - EXPECT_CALL( - delegate_, - OnGaiaRemoteConsentFlowFailed( - GaiaRemoteConsentFlow::Failure::SET_ACCOUNTS_IN_COOKIE_FAILED)); - flow->OnSetAccountsComplete( - signin::SetAccountsInCookieResult::kPersistentError); - histogram_tester()->ExpectUniqueSample( - kResultHistogramName, - GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED, 1); -} - } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/identity_api.cc b/chromium/chrome/browser/extensions/api/identity/identity_api.cc index 4a8d4ba3679..3e2ff468d4a 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_api.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_api.cc @@ -98,16 +98,6 @@ void IdentityAPI::EraseStaleGaiaIdsForAllExtensions() { } } -void IdentityAPI::SetConsentResult(const std::string& result, - const std::string& window_id) { - on_set_consent_result_callback_list_.Notify(result, window_id); -} - -base::CallbackListSubscription IdentityAPI::RegisterOnSetConsentResultCallback( - const base::RepeatingCallback<OnSetConsentResultSignature>& callback) { - return on_set_consent_result_callback_list_.Add(callback); -} - void IdentityAPI::Shutdown() { on_shutdown_callback_list_.Notify(); identity_manager_->RemoveObserver(this); diff --git a/chromium/chrome/browser/extensions/api/identity/identity_api.h b/chromium/chrome/browser/extensions/api/identity/identity_api.h index 1986a6cf897..657d14a0a87 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_api.h +++ b/chromium/chrome/browser/extensions/api/identity/identity_api.h @@ -40,9 +40,6 @@ namespace extensions { class IdentityAPI : public BrowserContextKeyedAPI, public signin::IdentityManager::Observer { public: - using OnSetConsentResultSignature = void(const std::string&, - const std::string&); - explicit IdentityAPI(content::BrowserContext* context); ~IdentityAPI() override; @@ -63,12 +60,6 @@ class IdentityAPI : public BrowserContextKeyedAPI, // longer signed in to Chrome for all extensions. void EraseStaleGaiaIdsForAllExtensions(); - // Consent result. - void SetConsentResult(const std::string& result, - const std::string& window_id); - base::CallbackListSubscription RegisterOnSetConsentResultCallback( - const base::RepeatingCallback<OnSetConsentResultSignature>& callback); - // BrowserContextKeyedAPI: void Shutdown() override; static BrowserContextKeyedAPIFactory<IdentityAPI>* GetFactoryInstance(); @@ -126,8 +117,6 @@ class IdentityAPI : public BrowserContextKeyedAPI, OnSignInChangedCallback on_signin_changed_callback_for_testing_; - base::RepeatingCallbackList<OnSetConsentResultSignature> - on_set_consent_result_callback_list_; base::OnceCallbackList<void()> on_shutdown_callback_list_; }; diff --git a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc index 09e44df583f..8f7b190234a 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc @@ -78,9 +78,11 @@ #include "content/public/test/test_utils.h" #include "extensions/browser/api/extensions_api_client.h" #include "extensions/browser/api_test_utils.h" +#include "extensions/browser/pref_names.h" #include "extensions/common/api/oauth2.h" #include "extensions/common/extension_builder.h" #include "extensions/common/extension_features.h" +#include "extensions/common/extension_id.h" #include "extensions/common/manifest_handlers/oauth2_manifest_handler.h" #include "google_apis/gaia/oauth2_mint_token_flow.h" #include "net/cookies/cookie_util.h" @@ -113,10 +115,6 @@ #endif using extensions::ExtensionsAPIClient; -using guest_view::GuestViewBase; -using guest_view::GuestViewManager; -using guest_view::TestGuestViewManager; -using guest_view::TestGuestViewManagerFactory; using testing::_; using testing::Return; @@ -342,6 +340,14 @@ void SimulateUrlRedirect(const std::string& url_prefix, "apply_consent(\"" + url_prefix + "\");")); } +// Similar to SimulateUrlRedirect, but uses provided url instead of the pattern +void SimulateCustomUrlRedirect(const std::string& redirect_url, + content::WebContents* auth_web_contents) { + ASSERT_EQ(nullptr, + content::EvalJs(auth_web_contents, "window.location.replace(\"" + + redirect_url + "\");")); +} + } // namespace class FakeGetAuthTokenFunction : public IdentityGetAuthTokenFunction { @@ -1061,7 +1067,7 @@ class GetAuthTokenFunctionTest base::test::ScopedFeatureList feature_list_; base::HistogramTester histogram_tester_; - std::string extension_id_; + ExtensionId extension_id_; std::set<std::string> oauth_scopes_; std::unique_ptr<ExtensionFunction::ScopedUserGestureForTests> user_gesture_; }; @@ -1645,24 +1651,6 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, - InteractiveApprovalSetAccountsInCookieFailed) { - SignIn("primary@example.com"); - scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); - func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); - func->push_mint_token_result(TestOAuth2MintTokenFlow::REMOTE_CONSENT_SUCCESS); - func->set_scope_ui_failure( - GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED); - std::string error = utils::RunFunctionAndReturnError( - func.get(), "[{\"interactive\": true}]", browser()->profile()); - EXPECT_EQ(std::string(errors::kSetAccountsInCookieFailure), error); - EXPECT_FALSE(func->login_ui_shown()); - EXPECT_TRUE(func->scope_ui_shown()); - histogram_tester()->ExpectUniqueSample( - kGetAuthTokenResultHistogramName, - IdentityGetAuthTokenError::State::kSetAccountsInCookieFailure, 1); -} - -IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveApprovalInvalidConsentResult) { SignIn("primary@example.com"); scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); @@ -3555,7 +3543,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) { scoped_refptr<IdentityLaunchWebAuthFlowFunction> function = CreateLaunchWebAuthFlowFunction(); - function->InitFinalRedirectURLPrefixForTest("abcdefghij"); + function->InitFinalRedirectURLDomainsForTest("abcdefghij"); absl::optional<base::Value> value = utils::RunFunctionAndReturnSingleResult( function.get(), "[{\"interactive\": false," @@ -3580,7 +3568,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, scoped_refptr<IdentityLaunchWebAuthFlowFunction> function = CreateLaunchWebAuthFlowFunction(); - function->InitFinalRedirectURLPrefixForTest("abcdefghij"); + function->InitFinalRedirectURLDomainsForTest("abcdefghij"); std::string args = base::StringPrintf( R"([{ "interactive": false, @@ -3602,7 +3590,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, scoped_refptr<IdentityLaunchWebAuthFlowFunction> function = CreateLaunchWebAuthFlowFunction(); - function->InitFinalRedirectURLPrefixForTest("abcdefghij"); + function->InitFinalRedirectURLDomainsForTest("abcdefghij"); absl::optional<base::Value> value = utils::RunFunctionAndReturnSingleResult( function.get(), "[{\"interactive\": true," @@ -3625,7 +3613,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, scoped_refptr<IdentityLaunchWebAuthFlowFunction> function = CreateLaunchWebAuthFlowFunction(); - function->InitFinalRedirectURLPrefixForTest("abcdefghij"); + function->InitFinalRedirectURLDomainsForTest("abcdefghij"); std::string args = "[{\"interactive\": true, \"url\": \"" + auth_url.spec() + "\"}]"; absl::optional<base::Value> value = utils::RunFunctionAndReturnSingleResult( @@ -3639,63 +3627,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, IdentityLaunchWebAuthFlowFunction::Error::kNone, 1); } -class LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam - : public LaunchWebAuthFlowFunctionTest, - public testing::WithParamInterface<bool> { - public: - LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam() { - scoped_feature_list_.InitWithFeatureState( - features::kWebAuthFlowInBrowserTab, use_tab_feature_enabled()); - } - - void SetUp() override { - GuestViewManager::set_factory_for_testing(&factory_); - LaunchWebAuthFlowFunctionTest::SetUp(); - } - - void TearDown() override { - LaunchWebAuthFlowFunctionTest::TearDown(); - GuestViewManager::set_factory_for_testing(nullptr); - } - - protected: - bool use_tab_feature_enabled() { return GetParam(); } - - void CloseGuestView() { - TestGuestViewManager* guest_view_manager = GetGuestViewManager(); - auto* guest_view = guest_view_manager->WaitForSingleGuestViewCreated(); - ASSERT_TRUE(guest_view); - - guest_view_manager->WaitUntilAttached(guest_view); - - auto* embedder_web_contents = guest_view->embedder_web_contents(); - ASSERT_TRUE(embedder_web_contents); - embedder_web_contents->Close(); - } - - private: - TestGuestViewManagerFactory factory_; - base::test::ScopedFeatureList scoped_feature_list_; - - TestGuestViewManager* GetGuestViewManager() { - TestGuestViewManager* manager = static_cast<TestGuestViewManager*>( - TestGuestViewManager::FromBrowserContext(browser()->profile())); - // Test code may access the TestGuestViewManager before it would be created - // during creation of the first guest. - if (!manager) { - manager = static_cast<TestGuestViewManager*>( - GuestViewManager::CreateWithDelegate( - browser()->profile(), - ExtensionsAPIClient::Get()->CreateGuestViewManagerDelegate( - browser()->profile()))); - } - return manager; - } -}; - -IN_PROC_BROWSER_TEST_P( - LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam, - UserCloseWindow) { +IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) { std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer(); GURL auth_url(https_server->GetURL("/interaction_required.html")); @@ -3703,30 +3635,21 @@ IN_PROC_BROWSER_TEST_P( CreateLaunchWebAuthFlowFunction(); content::TestNavigationObserver url_obvserver(auth_url); - if (use_tab_feature_enabled()) { - url_obvserver.StartWatchingNewWebContents(); - } + url_obvserver.StartWatchingNewWebContents(); std::string args = "[{\"interactive\": true, \"url\": \"" + auth_url.spec() + "\"}]"; RunFunctionAsync(function.get(), args); + url_obvserver.Wait(); + + Browser* popup_browser = chrome::FindBrowserWithWebContents( + function->GetWebAuthFlowForTesting()->web_contents()); + TabStripModel* tabs = popup_browser->tab_strip_model(); + EXPECT_NE(browser(), popup_browser); + ASSERT_EQ(tabs->GetActiveWebContents()->GetURL(), auth_url); // Close the opened auth web contents. - // Depending on the feature `WebAuthFlowInBrowserTab`, close the opened - // GuestView (simulating the AppWindow), or close the new tab through the - // created WebContents. - if (use_tab_feature_enabled()) { - url_obvserver.Wait(); - - Browser* popup_browser = chrome::FindBrowserWithWebContents( - function->GetWebAuthFlowForTesting()->web_contents()); - TabStripModel* tabs = popup_browser->tab_strip_model(); - EXPECT_NE(browser(), popup_browser); - ASSERT_EQ(tabs->GetActiveWebContents()->GetURL(), auth_url); - tabs->CloseWebContentsAt(tabs->active_index(), 0); - } else { - CloseGuestView(); - } + tabs->CloseWebContentsAt(tabs->active_index(), 0); EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function.get())); histogram_tester()->ExpectUniqueSample( @@ -3734,25 +3657,27 @@ IN_PROC_BROWSER_TEST_P( IdentityLaunchWebAuthFlowFunction::Error::kUserRejected, 1); } -INSTANTIATE_TEST_SUITE_P( - , - LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam, - testing::Bool(), - [](const testing::TestParamInfo< - LaunchWebAuthFlowFunctionTestWithWebAuthFlowInBrowserTabParam:: - ParamType>& info) { - return base::StrCat( - {info.param ? "With" : "Without", "WebAuthFlowInBrowserTab"}); - }); - -class LaunchWebAuthFlowFunctionTestWithNewTab - : public LaunchWebAuthFlowFunctionTest { - public: - LaunchWebAuthFlowFunctionTestWithNewTab() { - scoped_feature_list_.InitAndEnableFeature( - features::kWebAuthFlowInBrowserTab); - } +// Regression test for http://b/290733700. +IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, + SchemeOtherThanHttpOrHttpsNotAllowed) { + // Only http and https schemes are allowed. + GURL invalid_auth_url("chrome-untrusted://some_chrome_url"); + scoped_refptr<IdentityLaunchWebAuthFlowFunction> function = + CreateLaunchWebAuthFlowFunction(); + + std::string args = + "[{\"interactive\": true, \"url\": \"" + invalid_auth_url.spec() + "\"}]"; + RunFunctionAsync(function.get(), args); + EXPECT_EQ(std::string(errors::kInvalidURLScheme), + WaitForError(function.get())); + histogram_tester()->ExpectUniqueSample( + kLaunchWebAuthFlowResultHistogramName, + IdentityLaunchWebAuthFlowFunction::Error::kInvalidURLScheme, 1); +} + +class LaunchWebAuthFlowFunctionTestWithBrowserTab + : public LaunchWebAuthFlowFunctionTest { protected: void RunFunctionAndWaitForNavigation( IdentityLaunchWebAuthFlowFunction* function, @@ -3763,19 +3688,16 @@ class LaunchWebAuthFlowFunctionTestWithNewTab RunFunctionAsync(function, args); url_observer.Wait(); } - - private: - base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, +IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab, PageNavigateFromInitURLToFinalURL) { std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer(); scoped_refptr<IdentityLaunchWebAuthFlowFunction> function = CreateLaunchWebAuthFlowFunction(); const std::string extension_id("abcdefghij"); - function->InitFinalRedirectURLPrefixForTest(extension_id); + function->InitFinalRedirectURLDomainsForTest(extension_id); const GURL auth_url(https_server->GetURL("/consent_page.html")); const GURL final_url("https://" + extension_id + ".chromiumapp.org/"); @@ -3790,7 +3712,38 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, base::Value output; WaitForOneResult(function.get(), &output); EXPECT_FALSE(function->GetWebAuthFlowForTesting()); - EXPECT_TRUE(output.GetString().find(final_url.spec()) != std::string::npos); + EXPECT_EQ(GURL(output.GetString()).Resolve("/"), final_url); + histogram_tester()->ExpectUniqueSample( + kLaunchWebAuthFlowResultHistogramName, + IdentityLaunchWebAuthFlowFunction::Error::kNone, 1); +} + +IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab, + PageNavigateFromInitURLToCustomFinalURL) { + std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer(); + scoped_refptr<IdentityLaunchWebAuthFlowFunction> function = + CreateLaunchWebAuthFlowFunction(); + + const GURL auth_url(https_server->GetURL("/consent_page.html")); + const GURL final_url("example://example.com/"); + + const std::string args = + "[{\"interactive\": true, \"url\": \"" + auth_url.spec() + "\"}]"; + + browser()->profile()->GetPrefs()->SetDict( + extensions::pref_names::kOAuthRedirectUrls, + base::Value::Dict().Set(function->extension()->id(), + base::Value::List().Append(final_url.spec()))); + RunFunctionAndWaitForNavigation(function.get(), auth_url, args); + + SimulateCustomUrlRedirect( + final_url.spec() + "#some_information", + function->GetWebAuthFlowForTesting()->web_contents()); + + base::Value output; + WaitForOneResult(function.get(), &output); + EXPECT_FALSE(function->GetWebAuthFlowForTesting()); + EXPECT_EQ(GURL(output.GetString()).Resolve("/"), final_url); histogram_tester()->ExpectUniqueSample( kLaunchWebAuthFlowResultHistogramName, IdentityLaunchWebAuthFlowFunction::Error::kNone, 1); @@ -3798,7 +3751,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, // TODO(crbug/1421278): This test should be adapted after the implementation of // the bug. Multiple TODOs in the test to fix. -IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, +IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab, SimilarExtensionAndArgsShouldGenerateSameFlow) { std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer(); scoped_refptr<IdentityLaunchWebAuthFlowFunction> function1 = @@ -3807,8 +3760,8 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, CreateLaunchWebAuthFlowFunction(); const std::string extension_id("final_url"); - function1->InitFinalRedirectURLPrefixForTest(extension_id); - function2->InitFinalRedirectURLPrefixForTest(extension_id); + function1->InitFinalRedirectURLDomainsForTest(extension_id); + function2->InitFinalRedirectURLDomainsForTest(extension_id); const GURL auth_url(https_server->GetURL("/consent_page.html")); const GURL final_url("https://" + extension_id + ".chromiumapp.org/"); @@ -3847,7 +3800,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, IdentityLaunchWebAuthFlowFunction::Error::kNone, 1); } -IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, +IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab, DifferentExtensionsShouldGenerateDifferentFlows) { std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer(); scoped_refptr<IdentityLaunchWebAuthFlowFunction> function1 = @@ -3856,9 +3809,9 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, CreateLaunchWebAuthFlowFunction(); const std::string extension_id1("extension1"); - function1->InitFinalRedirectURLPrefixForTest(extension_id1); + function1->InitFinalRedirectURLDomainsForTest(extension_id1); const std::string extension_id2("extension2"); - function2->InitFinalRedirectURLPrefixForTest(extension_id2); + function2->InitFinalRedirectURLDomainsForTest(extension_id2); const GURL auth_url(https_server->GetURL("/consent_page.html")); // Different final_urls. @@ -3906,7 +3859,7 @@ IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithNewTab, // TODO(crbug/1421278): This test should be adapted after the implementation of // the bug. IN_PROC_BROWSER_TEST_F( - LaunchWebAuthFlowFunctionTestWithNewTab, + LaunchWebAuthFlowFunctionTestWithBrowserTab, ExtensionWithDifferentArgsShouldGenerateDifferentFlowsInAQueue) { std::unique_ptr<net::EmbeddedTestServer> https_server = LaunchHttpsServer(); scoped_refptr<IdentityLaunchWebAuthFlowFunction> function1 = @@ -3915,7 +3868,7 @@ IN_PROC_BROWSER_TEST_F( CreateLaunchWebAuthFlowFunction(); const std::string extension_id("extension"); - function1->InitFinalRedirectURLPrefixForTest(extension_id); + function1->InitFinalRedirectURLDomainsForTest(extension_id); const GURL auth_url1(https_server->GetURL("/consent_page.html")); const GURL auth_url2(https_server->GetURL("/interaction_required.html")); @@ -3933,8 +3886,8 @@ IN_PROC_BROWSER_TEST_F( function1->GetWebAuthFlowForTesting()->web_contents(); content::WebContents* consent_web_contents2 = function2->GetWebAuthFlowForTesting()->web_contents(); - // TODO(crbug/1421278): `function2->GetWebAuthFlowForTesting()` should be null - // after the changes since it would be in a queue. + // TODO(crbug/1421278): `function2->GetWebAuthFlowForTesting()` should be + // null after the changes since it would be in a queue. EXPECT_NE(consent_web_contents1, consent_web_contents2); const std::string& current_consent_url2 = @@ -4007,58 +3960,6 @@ IN_PROC_BROWSER_TEST_F(ClearAllCachedAuthTokensFunctionTest, id_api()->token_cache()->GetToken(token_key).status()); } -class ClearAllCachedAuthTokensFunctionTestWithPartitionParam - : public ClearAllCachedAuthTokensFunctionTest, - public testing::WithParamInterface<WebAuthFlow::Partition> { - public: - network::mojom::CookieManager* GetCookieManager() { - Profile* profile = browser()->profile(); - return profile - ->GetStoragePartition( - WebAuthFlow::GetWebViewPartitionConfig(GetParam(), profile)) - ->GetCookieManagerForBrowserProcess(); - } - - // Returns the list of cookies in the cookie manager. - net::CookieList GetCookies() { - net::CookieList result; - base::RunLoop get_all_cookies_loop; - GetCookieManager()->GetAllCookies(base::BindLambdaForTesting( - [&get_all_cookies_loop, &result](const net::CookieList& cookie_list) { - result = cookie_list; - get_all_cookies_loop.Quit(); - })); - get_all_cookies_loop.Run(); - return result; - } -}; - -IN_PROC_BROWSER_TEST_P(ClearAllCachedAuthTokensFunctionTestWithPartitionParam, - CleanWebAuthFlowCookies) { - auto test_cookie = net::CanonicalCookie::CreateUnsafeCookieForTesting( - "test_name", "test_value", "test.com", "/", base::Time(), base::Time(), - base::Time(), base::Time(), true, false, - net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT, false); - base::test::TestFuture<bool> future; - GetCookieManager()->SetCanonicalCookie( - *test_cookie, - net::cookie_util::SimulatedCookieSource(*test_cookie, url::kHttpsScheme), - net::CookieOptions(), - base::BindOnce(net::cookie_util::IsCookieAccessResultInclude) - .Then(future.GetCallback())); - EXPECT_TRUE(future.Get()); - - EXPECT_FALSE(GetCookies().empty()); - ASSERT_TRUE(RunClearAllCachedAuthTokensFunction()); - EXPECT_TRUE(GetCookies().empty()); -} - -INSTANTIATE_TEST_SUITE_P( - All, - ClearAllCachedAuthTokensFunctionTestWithPartitionParam, - ::testing::Values(WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::Partition::GET_AUTH_TOKEN)); - class OnSignInChangedEventTest : public IdentityTestWithSignin { protected: void SetUpOnMainThread() override { diff --git a/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc b/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc index 06a3fb6c919..0ab43bb2f4a 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.cc @@ -4,26 +4,13 @@ #include "chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h" -#include "base/functional/bind.h" -#include "base/location.h" -#include "base/task/single_thread_task_runner.h" #include "chrome/browser/extensions/api/identity/identity_api.h" #include "chrome/browser/extensions/api/identity/identity_constants.h" #include "chrome/browser/extensions/api/identity/web_auth_flow.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/extensions/api/identity.h" -#include "content/public/browser/storage_partition.h" -#include "services/network/public/mojom/cookie_manager.mojom.h" namespace extensions { -namespace { - -constexpr WebAuthFlow::Partition kPartitionsToClean[] = { - WebAuthFlow::GET_AUTH_TOKEN, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW}; - -} - IdentityClearAllCachedAuthTokensFunction:: IdentityClearAllCachedAuthTokensFunction() = default; IdentityClearAllCachedAuthTokensFunction:: @@ -39,35 +26,7 @@ IdentityClearAllCachedAuthTokensFunction::Run() { id_api->EraseGaiaIdForExtension(extension()->id()); id_api->token_cache()->EraseAllTokensForExtension(extension()->id()); - for (WebAuthFlow::Partition partition : kPartitionsToClean) { - profile - ->GetStoragePartition( - WebAuthFlow::GetWebViewPartitionConfig(partition, profile)) - ->GetCookieManagerForBrowserProcess() - ->DeleteCookies( - network::mojom::CookieDeletionFilter::New(), - base::BindOnce( - &IdentityClearAllCachedAuthTokensFunction::OnCookiesDeleted, - this)); - } - - // This object is retained by the DeleteCookies callbacks. - return RespondLater(); -} - -void IdentityClearAllCachedAuthTokensFunction::OnCookiesDeleted( - uint32_t num_deleted) { - ++cleaned_partitions_; - - if (cleaned_partitions_ < std::size(kPartitionsToClean)) - return; - - // Post a task to ensure Respond() is not synchronously called from Run(). The - // object is retained by this task. - base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, - base::BindOnce(&IdentityClearAllCachedAuthTokensFunction::Respond, this, - NoArguments())); + return RespondNow(NoArguments()); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h b/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h index 066987d20f1..774f4d48cde 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h +++ b/chromium/chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h @@ -21,10 +21,6 @@ class IdentityClearAllCachedAuthTokensFunction : public ExtensionFunction { // ExtensionFunction: ResponseAction Run() override; - - void OnCookiesDeleted(uint32_t num_deleted); - - size_t cleaned_partitions_ = 0; }; } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/identity_constants.cc b/chromium/chrome/browser/extensions/api/identity/identity_constants.cc index 37d177dd0b8..ec84ec0adf8 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_constants.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_constants.cc @@ -25,11 +25,13 @@ const char kInvalidRedirect[] = "Did not redirect to the right URL."; const char kOffTheRecord[] = "Identity API is disabled in incognito windows."; const char kPageLoadFailure[] = "Authorization page could not be loaded."; const char kPageLoadTimedOut[] = "Authorization page load timed out."; -const char kSetAccountsInCookieFailure[] = "Account cookies could not be set."; const char kInvalidConsentResult[] = "Returned an invalid consent result."; const char kCanceled[] = "canceled"; const char kCannotCreateWindow[] = "Couldn't create a browser window to display an authorization page."; +const char kInvalidURLScheme[] = + "The auth url has an invalid scheme. Only http:// and https:// schemes are " + "allowed."; const int kCachedRemoteConsentTTLSeconds = 1; } // namespace identity_constants diff --git a/chromium/chrome/browser/extensions/api/identity/identity_constants.h b/chromium/chrome/browser/extensions/api/identity/identity_constants.h index 91039aa4a2e..e8aa98376e0 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_constants.h +++ b/chromium/chrome/browser/extensions/api/identity/identity_constants.h @@ -22,10 +22,10 @@ extern const char kInvalidRedirect[]; extern const char kOffTheRecord[]; extern const char kPageLoadFailure[]; extern const char kPageLoadTimedOut[]; -extern const char kSetAccountsInCookieFailure[]; extern const char kInvalidConsentResult[]; extern const char kCanceled[]; extern const char kCannotCreateWindow[]; +extern const char kInvalidURLScheme[]; extern const int kCachedRemoteConsentTTLSeconds; } // namespace identity_constants diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc index e65baad32fc..670a7790de3 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc @@ -64,8 +64,6 @@ std::string IdentityGetAuthTokenError::ToString() const { return identity_constants::kOffTheRecord; case State::kRemoteConsentPageLoadFailure: return identity_constants::kPageLoadFailure; - case State::kSetAccountsInCookieFailure: - return identity_constants::kSetAccountsInCookieFailure; case State::kInvalidConsentResult: return identity_constants::kInvalidConsentResult; case State::kCanceled: diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h index 1624c64f0b4..84b4694931d 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h +++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h @@ -41,7 +41,7 @@ class IdentityGetAuthTokenError { kOffTheRecord = 22, // kPageLoadFailure = 23, // Deprecated kRemoteConsentPageLoadFailure = 24, - kSetAccountsInCookieFailure = 25, + // kSetAccountsInCookieFailure = 25, // Deprecated kInvalidConsentResult = 26, kCanceled = 27, kInteractivityDenied = 28, diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc index 87dc7d1caae..04470955181 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc @@ -13,6 +13,7 @@ #include "base/metrics/histogram_functions.h" #include "base/notreached.h" #include "base/strings/strcat.h" +#include "base/strings/string_piece.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" @@ -60,12 +61,12 @@ bool IsBrowserSigninAllowed(Profile* profile) { return profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed); } -std::string GetOAuth2MintTokenFlowVersion() { - return std::string(version_info::GetVersionNumber()); +base::StringPiece GetOAuth2MintTokenFlowVersion() { + return version_info::GetVersionNumber(); } -std::string GetOAuth2MintTokenFlowChannel() { - return std::string(version_info::GetChannelString(chrome::GetChannel())); +base::StringPiece GetOAuth2MintTokenFlowChannel() { + return version_info::GetChannelString(chrome::GetChannel()); } void RecordFunctionResult(const IdentityGetAuthTokenError& error, @@ -237,7 +238,7 @@ void IdentityGetAuthTokenFunction::OnReceivedExtensionAccountInfo( #if BUILDFLAG(IS_CHROMEOS) if (g_browser_process->browser_policy_connector() ->IsDeviceEnterpriseManaged()) { - if (profiles::IsPublicSession()) { + if (profiles::IsManagedGuestSession()) { CompleteFunctionWithError(IdentityGetAuthTokenError( IdentityGetAuthTokenError::State::kNotAllowlistedInPublicSession)); return; @@ -456,8 +457,9 @@ void IdentityGetAuthTokenFunction::StartMintToken( switch (cache_status) { case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND: #if BUILDFLAG(IS_CHROMEOS) - // Always force minting token for ChromeOS kiosk app and public session. - if (profiles::IsPublicSession()) { + // Always force minting token for ChromeOS kiosk app and managed guest + // session. + if (profiles::IsManagedGuestSession()) { CompleteFunctionWithError( IdentityGetAuthTokenError(IdentityGetAuthTokenError::State:: kNotAllowlistedInPublicSession)); @@ -656,16 +658,10 @@ void IdentityGetAuthTokenFunction::OnGaiaRemoteConsentFlowFailed( switch (failure) { case GaiaRemoteConsentFlow::WINDOW_CLOSED: - case GaiaRemoteConsentFlow::USER_NAVIGATED_AWAY: error = IdentityGetAuthTokenError( IdentityGetAuthTokenError::State::kRemoteConsentFlowRejected); break; - case GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED: - error = IdentityGetAuthTokenError( - IdentityGetAuthTokenError::State::kSetAccountsInCookieFailure); - break; - case GaiaRemoteConsentFlow::LOAD_FAILED: error = IdentityGetAuthTokenError( IdentityGetAuthTokenError::State::kRemoteConsentPageLoadFailure); @@ -876,13 +872,13 @@ IdentityGetAuthTokenFunction::CreateMintTokenFlow() { GetSigninScopedDeviceIdForProfile(GetProfile()); auto mint_token_flow = std::make_unique<OAuth2MintTokenFlow>( this, - OAuth2MintTokenFlow::Parameters( + OAuth2MintTokenFlow::Parameters::CreateForExtensionFlow( extension()->id(), oauth2_client_id_, - std::vector<std::string>(token_key_.scopes.begin(), - token_key_.scopes.end()), - enable_granular_permissions_, signin_scoped_device_id, - GetSelectedUserId(), consent_result_, GetOAuth2MintTokenFlowVersion(), - GetOAuth2MintTokenFlowChannel(), gaia_mint_token_mode_)); + std::vector<base::StringPiece>(token_key_.scopes.begin(), + token_key_.scopes.end()), + gaia_mint_token_mode_, enable_granular_permissions_, + GetOAuth2MintTokenFlowVersion(), GetOAuth2MintTokenFlowChannel(), + signin_scoped_device_id, GetSelectedUserId(), consent_result_)); return mint_token_flow; } diff --git a/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc b/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc index 810633b5b67..350fd13cfca 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.cc @@ -14,6 +14,8 @@ #include "chrome/browser/extensions/api/identity/identity_constants.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/identity.h" +#include "components/prefs/pref_service.h" +#include "extensions/browser/pref_names.h" namespace extensions { @@ -26,7 +28,6 @@ IdentityLaunchWebAuthFlowFunction::Error WebAuthFlowFailureToError( WebAuthFlow::Failure failure) { switch (failure) { case WebAuthFlow::WINDOW_CLOSED: - case WebAuthFlow::USER_NAVIGATED_AWAY: return IdentityLaunchWebAuthFlowFunction::Error::kUserRejected; case WebAuthFlow::INTERACTION_REQUIRED: return IdentityLaunchWebAuthFlowFunction::Error::kInteractionRequired; @@ -62,6 +63,8 @@ std::string ErrorToString(IdentityLaunchWebAuthFlowFunction::Error error) { return identity_constants::kPageLoadTimedOut; case IdentityLaunchWebAuthFlowFunction::Error::kCannotCreateWindow: return identity_constants::kCannotCreateWindow; + case IdentityLaunchWebAuthFlowFunction::Error::kInvalidURLScheme: + return identity_constants::kInvalidURLScheme; } } @@ -99,6 +102,13 @@ ExtensionFunction::ResponseAction IdentityLaunchWebAuthFlowFunction::Run() { EXTENSION_FUNCTION_VALIDATE(params); GURL auth_url(params->details.url); + if (!auth_url.SchemeIsHTTPOrHTTPS()) { + Error error = Error::kInvalidURLScheme; + + RecordHistogramFunctionResult(error); + return RespondNow(ExtensionFunction::Error(ErrorToString(error))); + } + WebAuthFlow::Mode mode = params->details.interactive && *params->details.interactive ? WebAuthFlow::INTERACTIVE @@ -120,14 +130,18 @@ ExtensionFunction::ResponseAction IdentityLaunchWebAuthFlowFunction::Run() { // Set up acceptable target URLs. (Does not include chrome-extension // scheme for this version of the API.) - InitFinalRedirectURLPrefix(extension()->id()); + InitFinalRedirectURLDomains( + extension()->id(), + Profile::FromBrowserContext(browser_context()) + ->GetPrefs() + ->GetDict(extensions::pref_names::kOAuthRedirectUrls) + .FindList(extension()->id())); AddRef(); // Balanced in OnAuthFlowSuccess/Failure. auth_flow_ = std::make_unique<WebAuthFlow>( - this, profile, auth_url, mode, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, - user_gesture(), abort_on_load_for_non_interactive, - timeout_for_non_interactive); + this, profile, auth_url, mode, user_gesture(), + abort_on_load_for_non_interactive, timeout_for_non_interactive); // An extension might call `launchWebAuthFlow()` with any URL. Add an infobar // to attribute displayed URL to the extension. auth_flow_->SetShouldShowInfoBar(extension()->name()); @@ -142,16 +156,26 @@ bool IdentityLaunchWebAuthFlowFunction::ShouldKeepWorkerAliveIndefinitely() { return true; } -void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefixForTest( +void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLDomainsForTest( const std::string& extension_id) { - InitFinalRedirectURLPrefix(extension_id); + InitFinalRedirectURLDomains(extension_id, nullptr); } -void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefix( - const std::string& extension_id) { - if (final_url_prefix_.is_empty()) { - final_url_prefix_ = GURL(base::StringPrintf( - kChromiumDomainRedirectUrlPattern, extension_id.c_str())); +void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLDomains( + const std::string& extension_id, + const base::Value::List* redirect_urls) { + if (!final_url_domains_.empty()) { + return; + } + final_url_domains_.emplace_back(base::StringPrintf( + kChromiumDomainRedirectUrlPattern, extension_id.c_str())); + if (redirect_urls) { + for (const auto& value : *redirect_urls) { + GURL domain(value.GetString()); + if (domain.is_valid()) { + final_url_domains_.push_back(domain.Resolve("/")); + } + } } } @@ -168,14 +192,16 @@ void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure( void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange( const GURL& redirect_url) { - if (redirect_url.GetWithEmptyPath() == final_url_prefix_) { - RecordHistogramFunctionResult( - IdentityLaunchWebAuthFlowFunction::Error::kNone); - Respond(WithArguments(redirect_url.spec())); - if (auth_flow_) - auth_flow_.release()->DetachDelegateAndDelete(); - Release(); // Balanced in RunAsync. + if (!base::Contains(final_url_domains_, redirect_url.Resolve("/"))) { + return; + } + RecordHistogramFunctionResult( + IdentityLaunchWebAuthFlowFunction::Error::kNone); + Respond(WithArguments(redirect_url.spec())); + if (auth_flow_) { + auth_flow_.release()->DetachDelegateAndDelete(); } + Release(); // Balanced in RunAsync. } WebAuthFlow* IdentityLaunchWebAuthFlowFunction::GetWebAuthFlowForTesting() { diff --git a/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h b/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h index 9b4aace4145..2c52713b892 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h +++ b/chromium/chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h @@ -38,13 +38,14 @@ class IdentityLaunchWebAuthFlowFunction : public ExtensionFunction, kUnexpectedError = 5, kPageLoadTimedOut = 6, kCannotCreateWindow = 7, - kMaxValue = kCannotCreateWindow, + kInvalidURLScheme = 8, + kMaxValue = kInvalidURLScheme, }; IdentityLaunchWebAuthFlowFunction(); // Tests may override extension_id. - void InitFinalRedirectURLPrefixForTest(const std::string& extension_id); + void InitFinalRedirectURLDomainsForTest(const std::string& extension_id); WebAuthFlow* GetWebAuthFlowForTesting(); @@ -60,10 +61,11 @@ class IdentityLaunchWebAuthFlowFunction : public ExtensionFunction, void OnAuthFlowTitleChange(const std::string& title) override {} // Helper to initialize final URL prefix. - void InitFinalRedirectURLPrefix(const std::string& extension_id); + void InitFinalRedirectURLDomains(const std::string& extension_id, + const base::Value::List* redirect_urls); std::unique_ptr<WebAuthFlow> auth_flow_; - GURL final_url_prefix_; + std::vector<GURL> final_url_domains_; }; } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/identity_private_api.cc b/chromium/chrome/browser/extensions/api/identity/identity_private_api.cc deleted file mode 100644 index 6737801a056..00000000000 --- a/chromium/chrome/browser/extensions/api/identity/identity_private_api.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020 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/extensions/api/identity/identity_private_api.h" - -#include "chrome/browser/extensions/api/identity/identity_api.h" - -namespace extensions { - -IdentityPrivateSetConsentResultFunction:: - IdentityPrivateSetConsentResultFunction() = default; -IdentityPrivateSetConsentResultFunction:: - ~IdentityPrivateSetConsentResultFunction() = default; - -ExtensionFunction::ResponseAction -IdentityPrivateSetConsentResultFunction::Run() { - absl::optional<Params> params = Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(params); - - IdentityAPI::GetFactoryInstance() - ->Get(browser_context()) - ->SetConsentResult(params->result, params->window_id); - - return RespondNow(NoArguments()); -} - -} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/identity_private_api.h b/chromium/chrome/browser/extensions/api/identity/identity_private_api.h deleted file mode 100644 index a0c6cf921af..00000000000 --- a/chromium/chrome/browser/extensions/api/identity/identity_private_api.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2020 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_EXTENSIONS_API_IDENTITY_IDENTITY_PRIVATE_API_H_ -#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_PRIVATE_API_H_ - -#include "chrome/common/extensions/api/identity_private.h" -#include "extensions/browser/extension_function.h" - -namespace extensions { - -class IdentityPrivateSetConsentResultFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("identityPrivate.setConsentResult", - IDENTITYPRIVATE_SETCONSENTRESULT) - - IdentityPrivateSetConsentResultFunction(); - - private: - using Params = api::identity_private::SetConsentResult::Params; - ~IdentityPrivateSetConsentResultFunction() override; - - ExtensionFunction::ResponseAction Run() override; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_PRIVATE_API_H_ diff --git a/chromium/chrome/browser/extensions/api/identity/identity_private_apitest.cc b/chromium/chrome/browser/extensions/api/identity/identity_private_apitest.cc deleted file mode 100644 index 9fc9fc686be..00000000000 --- a/chromium/chrome/browser/extensions/api/identity/identity_private_apitest.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2020 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/extensions/api/identity/identity_private_api.h" - -#include "base/memory/scoped_refptr.h" -#include "base/run_loop.h" -#include "chrome/browser/extensions/api/identity/identity_api.h" -#include "chrome/browser/extensions/extension_apitest.h" -#include "content/public/test/browser_test.h" -#include "extensions/browser/api_test_utils.h" - -namespace extensions { - -struct SetConsentResultParams { - std::string consent_result; - std::string window_id; -}; - -class IdentityPrivateApiTest : public ExtensionBrowserTest { - protected: - void SetUpOnMainThread() override { - ExtensionBrowserTest::SetUpOnMainThread(); - callback_loop_ = std::make_unique<base::RunLoop>(); - // base::Unretained(this) is safe because the callback will be unregistered - // on |callback_subscription_| destruction. - callback_subscription_ = identity_api()->RegisterOnSetConsentResultCallback( - base::BindRepeating(&IdentityPrivateApiTest::OnSetConsentResult, - base::Unretained(this))); - } - - IdentityAPI* identity_api() { - return IdentityAPI::GetFactoryInstance()->Get(profile()); - } - - SetConsentResultParams WaitForConsentResult() { - callback_loop_->Run(); - return {consent_result_, window_id_}; - } - - private: - void OnSetConsentResult(const std::string& consent_result, - const std::string& window_id) { - consent_result_ = consent_result; - window_id_ = window_id; - callback_loop_->Quit(); - } - - std::string consent_result_; - std::string window_id_; - std::unique_ptr<base::RunLoop> callback_loop_; - base::CallbackListSubscription callback_subscription_; -}; - -IN_PROC_BROWSER_TEST_F(IdentityPrivateApiTest, SetConsentResult) { - scoped_refptr<ExtensionFunction> func = - base::MakeRefCounted<IdentityPrivateSetConsentResultFunction>(); - bool success = api_test_utils::RunFunction( - func.get(), - std::string("[\"consent_result_value\", \"window_id_value\"]"), - profile()); - ASSERT_TRUE(success); - SetConsentResultParams params = WaitForConsentResult(); - EXPECT_EQ(params.consent_result, "consent_result_value"); - EXPECT_EQ(params.window_id, "window_id_value"); -} - -} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h b/chromium/chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h deleted file mode 100644 index cc378b42fa7..00000000000 --- a/chromium/chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2023 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_EXTENSIONS_API_IDENTITY_TEST_SCOPED_SHOULD_ANIMATE_WEB_AUTH_FLOW_INFO_BAR_H_ -#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_TEST_SCOPED_SHOULD_ANIMATE_WEB_AUTH_FLOW_INFO_BAR_H_ - -#include "chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h" -#include "third_party/abseil-cpp/absl/types/optional.h" - -namespace extensions { - -class TestScopedShouldAnimateWebAuthFlowInfoBar { - public: - explicit TestScopedShouldAnimateWebAuthFlowInfoBar(bool should_animate) { - previous_state_ = WebAuthFlowInfoBarDelegate::should_animate_for_testing_; - WebAuthFlowInfoBarDelegate::should_animate_for_testing_ = should_animate; - } - - ~TestScopedShouldAnimateWebAuthFlowInfoBar() { - WebAuthFlowInfoBarDelegate::should_animate_for_testing_ = previous_state_; - } - - private: - absl::optional<bool> previous_state_; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_TEST_SCOPED_SHOULD_ANIMATE_WEB_AUTH_FLOW_INFO_BAR_H_ diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc index ee6bfbcbbb0..affc352d603 100644 --- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc +++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc @@ -7,31 +7,16 @@ #include <memory> #include <utility> -#include "base/base64.h" #include "base/feature_list.h" #include "base/functional/bind.h" -#include "base/location.h" -#include "base/notreached.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" #include "base/task/single_thread_task_runner.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "base/trace_event/trace_event.h" #include "chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h" -#include "chrome/browser/extensions/component_loader.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_navigator.h" -#include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" -#include "chrome/common/chrome_features.h" -#include "chrome/common/extensions/api/identity_private.h" -#include "chrome/common/extensions/extension_constants.h" -#include "chrome/grit/browser_resources.h" -#include "components/guest_view/browser/guest_view_base.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" @@ -39,75 +24,22 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" -#include "crypto/random.h" -#include "extensions/browser/app_window/app_window.h" -#include "extensions/browser/event_router.h" -#include "extensions/browser/extension_system.h" -#include "extensions/browser/guest_view/web_view/web_view_guest.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" #include "ui/base/page_transition_types.h" -#include "ui/base/window_open_disposition.h" #include "url/gurl.h" #include "url/url_constants.h" -using content::RenderViewHost; using content::WebContents; using content::WebContentsObserver; -using guest_view::GuestViewBase; namespace extensions { -namespace { - -// Returns whether `partition` should be persisted on disk. -bool ShouldPersistStorage(WebAuthFlow::Partition partition) { - switch (partition) { - case WebAuthFlow::LAUNCH_WEB_AUTH_FLOW: - return base::FeatureList::IsEnabled(kPersistentStorageForWebAuthFlow); - case WebAuthFlow::GET_AUTH_TOKEN: - return false; - } - - NOTREACHED() << "Unexpected partition value " << partition; - return false; -} - -// Returns a unique identifier of the storage partition corresponding to -// `partition`. -std::string GetStoragePartitionId(WebAuthFlow::Partition partition) { - switch (partition) { - case WebAuthFlow::LAUNCH_WEB_AUTH_FLOW: - return "launchWebAuthFlow"; - case WebAuthFlow::GET_AUTH_TOKEN: - return "getAuthFlow"; - } - - NOTREACHED() << "Unexpected partition value " << partition; - return std::string(); -} - -// Returns a partition name suitable to use in the `webview.partition` -// parameter. -std::string GetPartitionNameForWebView(WebAuthFlow::Partition partition) { - std::string persist_prefix = - ShouldPersistStorage(partition) ? "persist:" : ""; - return persist_prefix + GetStoragePartitionId(partition); -} -} // namespace - -namespace identity_private = api::identity_private; - -BASE_FEATURE(kPersistentStorageForWebAuthFlow, - "PersistentStorageForWebAuthFlow", - base::FEATURE_DISABLED_BY_DEFAULT); - WebAuthFlow::WebAuthFlow( Delegate* delegate, Profile* profile, const GURL& provider_url, Mode mode, - Partition partition, bool user_gesture, AbortOnLoad abort_on_load_for_non_interactive, absl::optional<base::TimeDelta> timeout_for_non_interactive) @@ -115,7 +47,6 @@ WebAuthFlow::WebAuthFlow( profile_(profile), provider_url_(provider_url), mode_(mode), - partition_(partition), user_gesture_(user_gesture), abort_on_load_for_non_interactive_(abort_on_load_for_non_interactive), timeout_for_non_interactive_(timeout_for_non_interactive), @@ -130,7 +61,7 @@ WebAuthFlow::WebAuthFlow( WebAuthFlow::~WebAuthFlow() { DCHECK(!delegate_); - if (using_auth_with_browser_tab_ && web_contents()) { + if (web_contents()) { web_contents()->Close(); } @@ -140,12 +71,6 @@ WebAuthFlow::~WebAuthFlow() { // below may generate notifications. WebContentsObserver::Observe(nullptr); - if (!app_window_key_.empty()) { - AppWindowRegistry::Get(profile_)->RemoveObserver(this); - - if (app_window_ && app_window_->web_contents()) - app_window_->web_contents()->Close(); - } TRACE_EVENT_NESTABLE_ASYNC_END0("identity", "WebAuthFlow", this); } @@ -161,54 +86,12 @@ void WebAuthFlow::Start() { DCHECK(profile_); DCHECK(!profile_->IsOffTheRecord()); - if (base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) { - using_auth_with_browser_tab_ = true; - - content::WebContents::CreateParams params(profile_); - web_contents_ = content::WebContents::Create(params); - WebContentsObserver::Observe(web_contents_.get()); + content::WebContents::CreateParams params(profile_); + web_contents_ = content::WebContents::Create(params); + WebContentsObserver::Observe(web_contents_.get()); - content::NavigationController::LoadURLParams load_params(provider_url_); - web_contents_->GetController().LoadURLWithParams(load_params); - - MaybeStartTimeout(); - return; - } - - AppWindowRegistry::Get(profile_)->AddObserver(this); - - // Attach a random ID string to the window so we can recognize it - // in OnAppWindowAdded. - std::string random_bytes; - crypto::RandBytes(base::WriteInto(&random_bytes, 33), 32); - base::Base64Encode(random_bytes, &app_window_key_); - - // identityPrivate.onWebFlowRequest(app_window_key, provider_url_, mode_) - base::Value::List args; - args.Append(app_window_key_); - args.Append(provider_url_.spec()); - if (mode_ == WebAuthFlow::INTERACTIVE) - args.Append("interactive"); - else - args.Append("silent"); - args.Append(GetPartitionNameForWebView(partition_)); - - auto event = - std::make_unique<Event>(events::IDENTITY_PRIVATE_ON_WEB_FLOW_REQUEST, - identity_private::OnWebFlowRequest::kEventName, - std::move(args), profile_); - ExtensionSystem* system = ExtensionSystem::Get(profile_); - - extensions::ComponentLoader* component_loader = - system->extension_service()->component_loader(); - if (!component_loader->Exists(extension_misc::kIdentityApiUiAppId)) { - component_loader->Add( - IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST, - base::FilePath(FILE_PATH_LITERAL("identity_scope_approval_dialog"))); - } - - EventRouter::Get(profile_)->DispatchEventWithLazyListener( - extension_misc::kIdentityApiUiAppId, std::move(event)); + content::NavigationController::LoadURLParams load_params(provider_url_); + web_contents_->GetController().LoadURLWithParams(load_params); MaybeStartTimeout(); } @@ -219,67 +102,8 @@ void WebAuthFlow::DetachDelegateAndDelete() { this); } -content::StoragePartition* WebAuthFlow::GetGuestPartition() { - // When using the Auth through the Browser Tab, the guest partition shouldn't - // be used, consider using `Profile::GetDefaultStoragePartition()` instead. - if (base::FeatureList::IsEnabled(features::kWebAuthFlowInBrowserTab)) { - return nullptr; - } - - return profile_->GetStoragePartition( - GetWebViewPartitionConfig(partition_, profile_)); -} - -const std::string& WebAuthFlow::GetAppWindowKey() const { - return app_window_key_; -} - -// static -content::StoragePartitionConfig WebAuthFlow::GetWebViewPartitionConfig( - Partition partition, - content::BrowserContext* browser_context) { - // This has to mirror the logic in WebViewGuest::CreateWebContents for - // creating the correct StoragePartitionConfig. - auto result = content::StoragePartitionConfig::Create( - browser_context, extension_misc::kIdentityApiUiAppId, - GetStoragePartitionId(partition), - /*in_memory=*/!ShouldPersistStorage(partition)); - result.set_fallback_to_partition_domain_for_blob_urls( - browser_context->IsOffTheRecord() - ? content::StoragePartitionConfig::FallbackMode:: - kFallbackPartitionInMemory - : content::StoragePartitionConfig::FallbackMode:: - kFallbackPartitionOnDisk); - return result; -} - -void WebAuthFlow::OnAppWindowAdded(AppWindow* app_window) { - if (app_window->window_key() == app_window_key_ && - app_window->extension_id() == extension_misc::kIdentityApiUiAppId) { - app_window_ = app_window; - WebContentsObserver::Observe(app_window->web_contents()); - } -} - -void WebAuthFlow::OnAppWindowRemoved(AppWindow* app_window) { - if (app_window->window_key() == app_window_key_ && - app_window->extension_id() == extension_misc::kIdentityApiUiAppId) { - app_window_ = nullptr; - WebContentsObserver::Observe(nullptr); - - if (delegate_) - delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); - } -} - -bool WebAuthFlow::IsObservingProviderWebContents() const { - return web_contents() && - (embedded_window_created_ || using_auth_with_browser_tab_); -} - void WebAuthFlow::DisplayInfoBar() { DCHECK(web_contents()); - DCHECK(using_auth_with_browser_tab_); info_bar_delegate_ = WebAuthFlowInfoBarDelegate::Create( web_contents(), info_bar_parameters_.extension_display_name); @@ -291,11 +115,6 @@ void WebAuthFlow::CloseInfoBar() { } } -bool WebAuthFlow::IsDisplayingAuthPageInTab() const { - // If web_contents_ is nullptr, then the auth page tab is opened. - return using_auth_with_browser_tab_ && !web_contents_; -} - bool WebAuthFlow::DisplayAuthPageInPopupWindow() { if (Browser::GetCreationStatusForProfile(profile_) != Browser::CreationStatus::kOk) { @@ -318,15 +137,14 @@ bool WebAuthFlow::DisplayAuthPageInPopupWindow() { } void WebAuthFlow::BeforeUrlLoaded(const GURL& url) { - if (delegate_ && IsObservingProviderWebContents()) { + if (delegate_) { delegate_->OnAuthFlowURLChange(url); } } void WebAuthFlow::AfterUrlLoaded() { initial_url_loaded_ = true; - if (delegate_ && IsObservingProviderWebContents() && - mode_ == WebAuthFlow::SILENT) { + if (delegate_ && mode_ == WebAuthFlow::SILENT) { if (abort_on_load_for_non_interactive_ == AbortOnLoad::kYes) { non_interactive_timeout_timer_->Stop(); delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED); @@ -338,27 +156,11 @@ void WebAuthFlow::AfterUrlLoaded() { // If `web_contents_` is nullptr, this means that the interactive tab has // already been opened once. - if (delegate_ && using_auth_with_browser_tab_ && web_contents_ && - mode_ == WebAuthFlow::INTERACTIVE) { - switch (features::kWebAuthFlowInBrowserTabMode.Get()) { - case features::WebAuthFlowInBrowserTabMode::kNewTab: { - // Displays the auth page in a new tab attached to an existing/new - // browser. - chrome::ScopedTabbedBrowserDisplayer browser_displayer(profile_); - NavigateParams params(browser_displayer.browser(), - std::move(web_contents_)); - Navigate(¶ms); - break; - } - case features::WebAuthFlowInBrowserTabMode::kPopupWindow: { - bool is_auth_page_displayed = DisplayAuthPageInPopupWindow(); - if (!is_auth_page_displayed) { - delegate_->OnAuthFlowFailure( - WebAuthFlow::Failure::CANNOT_CREATE_WINDOW); - return; - } - break; - } + if (delegate_ && web_contents_ && mode_ == WebAuthFlow::INTERACTIVE) { + bool is_auth_page_displayed = DisplayAuthPageInPopupWindow(); + if (!is_auth_page_displayed) { + delegate_->OnAuthFlowFailure(WebAuthFlow::Failure::CANNOT_CREATE_WINDOW); + return; } if (info_bar_parameters_.should_show) { @@ -394,26 +196,6 @@ void WebAuthFlow::OnTimeout() { } } -void WebAuthFlow::InnerWebContentsCreated( - content::WebContents* inner_web_contents) { - DCHECK(app_window_); - - if (!delegate_ || embedded_window_created_) - return; - - // Switch from watching the app window to the guest inside it. - embedded_window_created_ = true; - WebContentsObserver::Observe(inner_web_contents); -} - -void WebAuthFlow::PrimaryMainFrameRenderProcessGone( - base::TerminationStatus status) { - // When in `using_auth_with_browser_tab_` mode, - // `WebAuthFlow::WebContentsDestroyed()` takes care of this flow. - if (delegate_ && !using_auth_with_browser_tab_) - delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED); -} - void WebAuthFlow::WebContentsDestroyed() { WebContentsObserver::Observe(nullptr); if (delegate_) { @@ -432,22 +214,6 @@ void WebAuthFlow::DidStopLoading() { void WebAuthFlow::DidStartNavigation( content::NavigationHandle* navigation_handle) { - // If the navigation is initiated by the user, the tab will exit the auth - // flow screen, this should result in a declined authentication and deleting - // the flow. - // These conditions do not apply for the Popup Window, where the url bar is - // deactivated and the user cannot navigate away directly, to allow going back - // and forth within the same flow. - if (IsDisplayingAuthPageInTab() && - features::kWebAuthFlowInBrowserTabMode.Get() != - features::WebAuthFlowInBrowserTabMode::kPopupWindow && - !navigation_handle->IsRendererInitiated()) { - // Stop observing the web contents since it is not part of the flow anymore. - WebContentsObserver::Observe(nullptr); - delegate_->OnAuthFlowFailure(Failure::USER_NAVIGATED_AWAY); - return; - } - if (navigation_handle->IsInPrimaryMainFrame()) { BeforeUrlLoaded(navigation_handle->GetURL()); } diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.h b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.h index acacaf3b44d..3fe9f2b11bd 100644 --- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.h +++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.h @@ -11,11 +11,8 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" -#include "content/public/browser/storage_partition_config.h" #include "content/public/browser/web_contents_observer.h" -#include "extensions/browser/app_window/app_window_registry.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#include "ui/gfx/geometry/rect.h" #include "url/gurl.h" class Profile; @@ -25,22 +22,14 @@ class OneShotTimer; class TickClock; } // namespace base -namespace content { -class StoragePartition; -} - namespace extensions { class WebAuthFlowInfoBarDelegate; -// When enabled, cookies in the `launchWebAuthFlow()` partition are persisted -// across browser restarts. -BASE_DECLARE_FEATURE(kPersistentStorageForWebAuthFlow); - // Controller class for web based auth flows. The WebAuthFlow creates -// a dialog window in the scope approval component app by firing an -// event. A webview embedded in the dialog will navigate to the -// |provider_url| passed to the WebAuthFlow constructor. +// a browser popup window (or a new tab based on the feature setting) +// with a webview that will navigate to the |provider_url| passed to the +// WebAuthFlow constructor. // // The WebAuthFlow monitors the WebContents of the webview, and // notifies its delegate interface any time the WebContents navigates @@ -54,24 +43,17 @@ BASE_DECLARE_FEATURE(kPersistentStorageForWebAuthFlow); // // A WebAuthFlow can be started in Mode::SILENT, which never displays // a window. If a window would be required, the flow fails. -class WebAuthFlow : public content::WebContentsObserver, - public AppWindowRegistry::Observer { +class WebAuthFlow : public content::WebContentsObserver { public: enum Mode { INTERACTIVE, // Show UI to the user if necessary. SILENT // No UI should be shown. }; - enum Partition { - GET_AUTH_TOKEN, // Use the getAuthToken() partition. - LAUNCH_WEB_AUTH_FLOW // Use the launchWebAuthFlow() partition. - }; - enum Failure { WINDOW_CLOSED, // Window closed by user (app or tab). INTERACTION_REQUIRED, // Non-redirect page load in silent mode. LOAD_FAILED, - USER_NAVIGATED_AWAY, // The user navigated away from the auth page. TIMED_OUT, CANNOT_CREATE_WINDOW // Couldn't create a browser window. }; @@ -110,7 +92,6 @@ class WebAuthFlow : public content::WebContentsObserver, Profile* profile, const GURL& provider_url, Mode mode, - Partition partition, bool user_gesture, AbortOnLoad abort_on_load_for_non_interactive = AbortOnLoad::kYes, absl::optional<base::TimeDelta> timeout_for_non_interactive = @@ -131,19 +112,6 @@ class WebAuthFlow : public content::WebContentsObserver, // Prevents further calls to the delegate and deletes the flow. void DetachDelegateAndDelete(); - // Returns a StoragePartition of the guest webview. Used to inject cookies - // into Gaia page. Can override for testing. - virtual content::StoragePartition* GetGuestPartition(); - - // Returns an ID string attached to the window. Can override for testing. - virtual const std::string& GetAppWindowKey() const; - - // Returns the StoragePartitionConfig for a given |partition| used in the - // WebAuthFlow. - static content::StoragePartitionConfig GetWebViewPartitionConfig( - Partition partition, - content::BrowserContext* browser_context); - // This call will make the interactive mode, that opens up a browser tab for // auth, display an Infobar that shows the extension name. void SetShouldShowInfoBar(const std::string& extension_display_name); @@ -152,16 +120,8 @@ class WebAuthFlow : public content::WebContentsObserver, base::WeakPtr<WebAuthFlowInfoBarDelegate> GetInfoBarDelegateForTesting(); private: - // ::AppWindowRegistry::Observer implementation. - void OnAppWindowAdded(AppWindow* app_window) override; - void OnAppWindowRemoved(AppWindow* app_window) override; - // WebContentsObserver implementation. void DidStopLoading() override; - void InnerWebContentsCreated( - content::WebContents* inner_web_contents) override; - void PrimaryMainFrameRenderProcessGone( - base::TerminationStatus status) override; void WebContentsDestroyed() override; void TitleWasSet(content::NavigationEntry* entry) override; void DidStartNavigation( @@ -177,31 +137,17 @@ class WebAuthFlow : public content::WebContentsObserver, void MaybeStartTimeout(); void OnTimeout(); - bool IsObservingProviderWebContents() const; - bool DisplayAuthPageInPopupWindow(); void DisplayInfoBar(); void CloseInfoBar(); - bool IsDisplayingAuthPageInTab() const; - raw_ptr<Delegate> delegate_ = nullptr; const raw_ptr<Profile> profile_; const GURL provider_url_; const Mode mode_; - const Partition partition_; const bool user_gesture_; - // Variables used only if displaying the auth flow in an app window. - raw_ptr<AppWindow> app_window_ = nullptr; - std::string app_window_key_; - bool embedded_window_created_ = false; - - // Variables used only if displaying the auth flow in a browser tab. - // - // Checks that the auth with browser tab is activated. - bool using_auth_with_browser_tab_ = false; // WebContents used to initialize the authentication. It is not displayed // and not owned by browser window. This WebContents is observed by // `this`. When this value becomes nullptr, this means that the browser tab diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc index d2322bcd205..98fbef9f164 100644 --- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_browsertest.cc @@ -8,10 +8,8 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" -#include "base/test/test_future.h" #include "base/test/test_mock_time_task_runner.h" #include "base/time/time.h" -#include "chrome/browser/extensions/api/identity/test_scoped_should_animate_web_auth_flow_info_bar.h" #include "chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" @@ -21,11 +19,9 @@ #include "chrome/browser/sessions/session_restore.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" -#include "chrome/common/chrome_features.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/keep_alive_registry/keep_alive_types.h" #include "components/keep_alive_registry/scoped_keep_alive.h" -#include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/test/back_forward_cache_util.h" #include "content/public/test/browser_test.h" @@ -78,7 +74,6 @@ class WebAuthFlowBrowserTest : public InProcessBrowserTest { void StartWebAuthFlow( const GURL& url, - WebAuthFlow::Partition partition = WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, WebAuthFlow::Mode mode = WebAuthFlow::Mode::INTERACTIVE, Profile* profile = nullptr, WebAuthFlow::AbortOnLoad abort_on_load_for_non_interactive = @@ -89,7 +84,7 @@ class WebAuthFlowBrowserTest : public InProcessBrowserTest { profile = browser()->profile(); web_auth_flow_ = std::make_unique<WebAuthFlow>( - &mock_web_auth_flow_delegate_, profile, url, mode, partition, + &mock_web_auth_flow_delegate_, profile, url, mode, /*user_gesture=*/true, abort_on_load_for_non_interactive, timeout_for_non_interactive); @@ -120,17 +115,8 @@ class WebAuthFlowBrowserTest : public InProcessBrowserTest { scoped_refptr<base::TestMockTimeTaskRunner> timeout_task_runner_; }; -class WebAuthFlowInBrowserTabParamBrowserTest - : public WebAuthFlowBrowserTest, - public testing::WithParamInterface<bool> { +class WebAuthFlowInBrowserTabParamBrowserTest : public WebAuthFlowBrowserTest { public: - WebAuthFlowInBrowserTabParamBrowserTest() { - scoped_feature_list_.InitWithFeatureState( - features::kWebAuthFlowInBrowserTab, use_tab_feature_enabled()); - } - - bool use_tab_feature_enabled() { return GetParam(); } - bool JsRedirectToUrl(const GURL& url) { content::TestNavigationObserver redirect_observer(url); redirect_observer.WatchExistingWebContents(); @@ -142,12 +128,9 @@ class WebAuthFlowInBrowserTabParamBrowserTest } return result; } - - private: - base::test::ScopedFeatureList scoped_feature_list_; }; -IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest, OnAuthFlowURLChangeCalled) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); @@ -163,7 +146,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, navigation_observer.WaitForNavigationFinished(); } -IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest, OnAuthFlowFailureChangeCalled) { // Navigate to a url that doesn't exist. const GURL error_url = embedded_test_server()->GetURL("/error"); @@ -182,7 +165,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // Tests that the flow launched in silent mode with default parameters will // terminate immediately with the "interacation required" error if the page // loads and does not navigate to the redirect URL. -IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest, OnAuthFlowFailureCalledInteractionRequired) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); @@ -195,8 +178,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // In SILENT mode, DidStopLoading() will force the auth flow to fail if it has // not already redirected, because we did not specify a timeout. EXPECT_CALL(mock(), OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED)); - StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::SILENT); + StartWebAuthFlow(auth_url, WebAuthFlow::SILENT); navigation_observer.Wait(); } @@ -205,7 +187,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // `abortOnLoadForNonInteractive` set to `false` will terminate with the // "interaction required" after a specified timeout if the page loads and does // not navigate to the redirect URL. -IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest, OnAuthFlowInteractionRequiredWithTimeout) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); @@ -218,8 +200,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // In SILENT mode, DidStopLoading() will wait for our specified 50ms timeout // before calling OnAuthFlowFailure. EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0); - StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::SILENT, /*profile=*/nullptr, + StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr, WebAuthFlow::AbortOnLoad::kNo, /*timeout_for_non_interactive=*/base::Milliseconds(50)); navigation_observer.Wait(); @@ -239,7 +220,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // `abortOnLoadForNonInteractive` set to `false` will terminate with the // "interaction required" error after a default timeout if the page loads and // does not navigate to the redirect URL. -IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest, OnAuthFlowInteractionRequiredWithDefaultTimeout) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); @@ -252,8 +233,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // In SILENT mode, DidStopLoading() will wait for the default 1 minute timeout // before calling OnAuthFlowFailure. EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0); - StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::SILENT, /*profile=*/nullptr, + StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr, WebAuthFlow::AbortOnLoad::kNo); navigation_observer.Wait(); testing::Mock::VerifyAndClearExpectations(&mock()); @@ -274,7 +254,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // set will terminate with the "timed out" error after a timeout if the page // fails to load (distinct from the flow failing to navigate to the redirect URL // in time). -IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest, OnAuthFlowPageLoadTimeout) { const GURL auth_url = embedded_test_server()->GetURL("/hung-after-headers"); @@ -287,8 +267,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // In SILENT mode, DidStopLoading() will wait for our specified 50ms timeout // before calling OnAuthFlowFailure. EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0); - StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::SILENT, /*profile=*/nullptr, + StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr, WebAuthFlow::AbortOnLoad::kYes, /*timeout_for_non_interactive=*/base::Milliseconds(50)); // Wait for navigation to the failing page to start first. @@ -310,7 +289,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // `abortOnLoadForNonInteractive` set to `false` and // `timeoutMsForNonInteractive` set will succeed if it navigates to the redirect // URL before the timeout. -IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest, OnAuthFlowRedirectBeforeTimeout) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); @@ -323,8 +302,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // In SILENT mode, DidStopLoading() will wait for our specified 50ms timeout // before calling OnAuthFlowFailure. EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0); - StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::SILENT, /*profile=*/nullptr, + StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr, WebAuthFlow::AbortOnLoad::kNo, /*timeout_for_non_interactive=*/base::Milliseconds(50)); @@ -344,7 +322,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // Tests that the loaded auth page can redirect multiple times and fails only // after the timeout. -IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowInBrowserTabParamBrowserTest, OnAuthFlowMultipleRedirects) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); @@ -357,8 +335,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, // In SILENT mode, DidStopLoading() will wait for our specified 50ms timeout // before calling OnAuthFlowFailure. EXPECT_CALL(mock(), OnAuthFlowFailure).Times(0); - StartWebAuthFlow(auth_url, WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::SILENT, /*profile=*/nullptr, + StartWebAuthFlow(auth_url, WebAuthFlow::SILENT, /*profile=*/nullptr, WebAuthFlow::AbortOnLoad::kNo, /*timeout_for_non_interactive=*/base::Milliseconds(50)); @@ -396,126 +373,6 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowInBrowserTabParamBrowserTest, timeout_task_runner()->FastForwardBy(base::Milliseconds(30)); } -INSTANTIATE_TEST_SUITE_P( - , - WebAuthFlowInBrowserTabParamBrowserTest, - testing::Bool(), - [](const testing::TestParamInfo< - WebAuthFlowInBrowserTabParamBrowserTest::ParamType>& info) { - return base::StrCat( - {info.param ? "With" : "Without", "WebAuthFlowInBrowserTab"}); - }); - -class WebAuthFlowGuestPartitionParamTest - : public WebAuthFlowBrowserTest, - public testing::WithParamInterface< - std::tuple<bool, WebAuthFlow::Partition>> { - public: - WebAuthFlowGuestPartitionParamTest() { - std::vector<base::test::FeatureRef> enabled_features; - std::vector<base::test::FeatureRef> disabled_features; - - persist_storage_feature_enabled() - ? enabled_features.push_back(kPersistentStorageForWebAuthFlow) - : disabled_features.push_back(kPersistentStorageForWebAuthFlow); - - // Explicitly disable the `kWebAuthFlowInBrowserTab` feature as it is - // incompatible with the Guest Partition tests and - // `kPersistentStorageForWebAuthFlow`. - disabled_features.push_back(features::kWebAuthFlowInBrowserTab); - - scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); - } - - bool persist_storage_feature_enabled() { return std::get<0>(GetParam()); } - - WebAuthFlow::Partition partition() { return std::get<1>(GetParam()); } - - void LoadWebAuthFlow() { - const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); - - // Observer for waiting until a navigation to a url has finished. - content::TestNavigationObserver navigation_observer(auth_url); - navigation_observer.StartWatchingNewWebContents(); - - StartWebAuthFlow(auth_url, partition()); - EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - - navigation_observer.WaitForNavigationFinished(); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -// Tests that the partition returned by `WebAuthFlow::GetGuestPartition()` -// matches the one used by the webview. -IN_PROC_BROWSER_TEST_P(WebAuthFlowGuestPartitionParamTest, GetGuestPartition) { - LoadWebAuthFlow(); - - // Set a test cookie on the page. - ASSERT_TRUE( - content::ExecJs(web_contents(), "document.cookie = \"testCookie=1\"")); - - // Verify that the cookie was added to the guest partition. - base::test::TestFuture<const net::CookieList&> get_cookies_future; - web_auth_flow() - ->GetGuestPartition() - ->GetCookieManagerForBrowserProcess() - ->GetAllCookies(get_cookies_future.GetCallback()); - const net::CookieList cookies = get_cookies_future.Get(); - ASSERT_EQ(1u, cookies.size()); - EXPECT_EQ("testCookie", cookies[0].Name()); - EXPECT_EQ("1", cookies[0].Value()); -} - -IN_PROC_BROWSER_TEST_P(WebAuthFlowGuestPartitionParamTest, - PRE_PersistenceTest) { - LoadWebAuthFlow(); - // Set a test cookie on the page. - ASSERT_TRUE(content::ExecJs( - web_contents(), "document.cookie = \"testCookie=1; max-age=3600\"")); -} - -IN_PROC_BROWSER_TEST_P(WebAuthFlowGuestPartitionParamTest, PersistenceTest) { - LoadWebAuthFlow(); - - base::test::TestFuture<const net::CookieList&> get_cookies_future; - web_auth_flow() - ->GetGuestPartition() - ->GetCookieManagerForBrowserProcess() - ->GetAllCookies(get_cookies_future.GetCallback()); - const net::CookieList cookies = get_cookies_future.Get(); - - // Verify that the cookie set in the previous test is persisted for the - // webAuthFlow if the feature is enabled. - // Read from the cookie store directly rather than execute a script on the - // auth page because the page URL changes between test (test server doesn't - // have a fixed port). - if (persist_storage_feature_enabled() && - partition() == WebAuthFlow::LAUNCH_WEB_AUTH_FLOW) { - ASSERT_EQ(1u, cookies.size()); - EXPECT_EQ("testCookie", cookies[0].Name()); - EXPECT_EQ("1", cookies[0].Value()); - } else { - EXPECT_EQ(0u, cookies.size()); - } -} - -INSTANTIATE_TEST_SUITE_P( - , - WebAuthFlowGuestPartitionParamTest, - testing::Combine(testing::Bool(), - testing::Values(WebAuthFlow::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::GET_AUTH_TOKEN)), - [](const testing::TestParamInfo< - WebAuthFlowGuestPartitionParamTest::ParamType>& info) { - return base::StrCat( - {std::get<0>(info.param) ? "FeatureOn" : "FeatureOff", - std::get<1>(info.param) == WebAuthFlow::LAUNCH_WEB_AUTH_FLOW - ? "WebAuthFlow" - : "GetAuthToken"}); - }); class WebAuthFlowFencedFrameTest : public WebAuthFlowInBrowserTabParamBrowserTest { public: @@ -527,7 +384,7 @@ class WebAuthFlowFencedFrameTest content::test::FencedFrameTestHelper fenced_frame_helper_; }; -IN_PROC_BROWSER_TEST_P(WebAuthFlowFencedFrameTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowFencedFrameTest, FencedFrameNavigationSuccess) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); @@ -554,7 +411,7 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowFencedFrameTest, embedded_test_server()->GetURL("/fenced_frames/title1.html"))); } -IN_PROC_BROWSER_TEST_P(WebAuthFlowFencedFrameTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowFencedFrameTest, FencedFrameNavigationFailure) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); @@ -582,27 +439,6 @@ IN_PROC_BROWSER_TEST_P(WebAuthFlowFencedFrameTest, embedded_test_server()->GetURL("/error"), net::Error::ERR_FAILED)); } -INSTANTIATE_TEST_SUITE_P(, - WebAuthFlowFencedFrameTest, - testing::Bool(), - [](const testing::TestParamInfo< - WebAuthFlowFencedFrameTest::ParamType>& info) { - return base::StrCat({info.param ? "With" : "Without", - "WebAuthFlowInBrowserTab"}); - }); - -class WebAuthFlowWithBrowserTabBrowserTest : public WebAuthFlowBrowserTest { - public: - WebAuthFlowWithBrowserTabBrowserTest() { - // By default the feature param is {{"browser_tab_mode", "popup_window"}}. - scoped_feature_list_.InitAndEnableFeature( - features::kWebAuthFlowInBrowserTab); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - // This test is in two parts: // - First create a WebAuthFlow in interactive mode that will create a new tab // with the auth_url. @@ -611,15 +447,14 @@ class WebAuthFlowWithBrowserTabBrowserTest : public WebAuthFlowBrowserTest { // // These two tests are combined into one in order not to re-test the tab // creation twice. -IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest, InteractivePopupWindowCreatedWithAuthURL_ThenCloseTab) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); content::TestNavigationObserver navigation_observer(auth_url); navigation_observer.StartWatchingNewWebContents(); EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::Mode::INTERACTIVE); + StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE); const char extension_name[] = "extension_name"; web_auth_flow()->SetShouldShowInfoBar(extension_name); @@ -650,15 +485,14 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, } IN_PROC_BROWSER_TEST_F( - WebAuthFlowWithBrowserTabBrowserTest, + WebAuthFlowBrowserTest, InteractivePopupWindowCreatedWithAuthURL_NavigationInURLDoesNotBreakTheFlow) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); content::TestNavigationObserver navigation_observer(auth_url); navigation_observer.StartWatchingNewWebContents(); EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::Mode::INTERACTIVE); + StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE); web_auth_flow()->SetShouldShowInfoBar("extension name"); navigation_observer.Wait(); @@ -683,13 +517,6 @@ IN_PROC_BROWSER_TEST_F( // Simulate an internal navigation, such as an authentication that needs an // input of username and password on two different pages/urls. GURL new_url = embedded_test_server()->GetURL("/title2.html"); - // Below a first navigation will be done, then going back on the initial auth - // page, in the popup window mode the error should not trigger and the auth - // flow should stay alive. - EXPECT_CALL(mock(), - OnAuthFlowFailure(WebAuthFlow::Failure::USER_NAVIGATED_AWAY)) - .Times(0); - EXPECT_CALL(mock(), OnAuthFlowURLChange(new_url)); ASSERT_TRUE(content::NavigateToURL(web_contents(), new_url)); @@ -713,7 +540,7 @@ IN_PROC_BROWSER_TEST_F( } IN_PROC_BROWSER_TEST_F( - WebAuthFlowWithBrowserTabBrowserTest, + WebAuthFlowBrowserTest, InteractiveNoBrowser_WebAuthCreatesBrowserWithPopupWindow) { Profile* profile = browser()->profile(); // Simulates an extension being opened, in order for the profile not to be @@ -730,8 +557,7 @@ IN_PROC_BROWSER_TEST_F( navigation_observer.StartWatchingNewWebContents(); EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::Mode::INTERACTIVE, profile); + StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE, profile); navigation_observer.Wait(); @@ -746,7 +572,7 @@ IN_PROC_BROWSER_TEST_F( // This is a regression test for crbug/1445824, makes sure the opened popup // window does not trigger Session restore. -IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest, InteractiveNoBrowser_NotActivatingSessionRestore) { Profile* profile = browser()->profile(); @@ -767,8 +593,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, navigation_observer.StartWatchingNewWebContents(); EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::Mode::INTERACTIVE, profile); + StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE, profile); navigation_observer.Wait(); // Makes sure only one browser is created and profile is not trying to restore @@ -785,8 +610,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, auth_url); } -IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, - SilentNewTabNotCreated) { +IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest, SilentNewTabNotCreated) { TabStripModel* tabs = browser()->tab_strip_model(); int initial_tab_count = tabs->count(); @@ -797,8 +621,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, EXPECT_CALL(mock(), OnAuthFlowFailure(WebAuthFlow::Failure::INTERACTION_REQUIRED)); EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::Mode::SILENT); + StartWebAuthFlow(auth_url, WebAuthFlow::Mode::SILENT); navigation_observer.Wait(); @@ -806,15 +629,14 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, EXPECT_EQ(tabs->count(), initial_tab_count); } -IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest, InteractiveNewTabCreatedWithAuthURL_NoInfoBarByDefault) { const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); content::TestNavigationObserver navigation_observer(auth_url); navigation_observer.StartWatchingNewWebContents(); EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::GET_AUTH_TOKEN, - WebAuthFlow::Mode::INTERACTIVE); + StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE); navigation_observer.Wait(); @@ -830,7 +652,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, EXPECT_FALSE(infobar_delegate); } -IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, +IN_PROC_BROWSER_TEST_F(WebAuthFlowBrowserTest, PopupWindowOpened_ThenCloseWindow) { size_t initial_browser_count = chrome::GetTotalBrowserCount(); @@ -839,8 +661,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, navigation_observer.StartWatchingNewWebContents(); EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::Mode::INTERACTIVE); + StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE); navigation_observer.Wait(); @@ -865,7 +686,7 @@ IN_PROC_BROWSER_TEST_F(WebAuthFlowWithBrowserTabBrowserTest, } IN_PROC_BROWSER_TEST_F( - WebAuthFlowWithBrowserTabBrowserTest, + WebAuthFlowBrowserTest, Interactive_MarkedForDeletionProfileNotAllowedToCreatePopupWindow) { // Marking active profile for deletion. MarkProfileDirectoryForDeletion(browser()->profile()->GetPath()); @@ -880,78 +701,8 @@ IN_PROC_BROWSER_TEST_F( // should return an error. EXPECT_CALL(mock(), OnAuthFlowFailure(WebAuthFlow::Failure::CANNOT_CREATE_WINDOW)); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::GET_AUTH_TOKEN, - WebAuthFlow::Mode::INTERACTIVE); + StartWebAuthFlow(auth_url, WebAuthFlow::Mode::INTERACTIVE); navigation_observer.Wait(); } -class WebAuthFlowWithBrowserTabInNewTabBrowserTest - : public WebAuthFlowBrowserTest { - public: - WebAuthFlowWithBrowserTabInNewTabBrowserTest() { - // Enables feature with New tab mode. - scoped_feature_list_.InitAndEnableFeatureWithParameters( - features::kWebAuthFlowInBrowserTab, {{"browser_tab_mode", "new_tab"}}); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -IN_PROC_BROWSER_TEST_F( - WebAuthFlowWithBrowserTabInNewTabBrowserTest, - InteractiveNewTabCreatedWithAuthURL_ThenChangeURLBeforeAuthResult) { - const GURL auth_url = embedded_test_server()->GetURL("/title1.html"); - content::TestNavigationObserver navigation_observer(auth_url); - navigation_observer.StartWatchingNewWebContents(); - - EXPECT_CALL(mock(), OnAuthFlowURLChange(auth_url)); - // Remove the animation mainly for the deleting part as it could create - // flakiness when checking for the deletion of the info bar. - TestScopedShouldAnimateWebAuthFlowInfoBar should_animate(false); - StartWebAuthFlow(auth_url, WebAuthFlow::Partition::LAUNCH_WEB_AUTH_FLOW, - WebAuthFlow::Mode::INTERACTIVE); - web_auth_flow()->SetShouldShowInfoBar("extension name"); - - navigation_observer.Wait(); - - //--------------------------------------------------------------------- - // Browser-initiated URL change in the opened tab before completing the auth - // flow should trigger an auth flow failure. - //--------------------------------------------------------------------- - testing::Mock::VerifyAndClearExpectations(&mock()); - - // Keeping a reference to the info bar delegate to check later. - base::WeakPtr<WebAuthFlowInfoBarDelegate> auth_info_bar = - web_auth_flow()->GetInfoBarDelegateForTesting(); - ASSERT_TRUE(auth_info_bar); - - Browser* newtab_browser = chrome::FindBrowserWithWebContents(web_contents()); - EXPECT_EQ(browser(), newtab_browser); - TabStripModel* tabs = newtab_browser->tab_strip_model(); - - // Simulating a non user navigation, it shouldn't break the flow. - GURL internal_url = embedded_test_server()->GetURL("/title2.html"); - EXPECT_CALL(mock(), OnAuthFlowURLChange(internal_url)); - EXPECT_CALL(mock(), OnAuthFlowFailure(testing::_)).Times(0); - ASSERT_TRUE(content::NavigateToURLFromRenderer(web_contents(), internal_url)); - EXPECT_TRUE(web_auth_flow()); - EXPECT_TRUE(auth_info_bar); - testing::Mock::VerifyAndClearExpectations(&mock()); - - // Simulating user manually navigating to another URL. - GURL browsing_url = embedded_test_server()->GetURL("/simple.html"); - EXPECT_CALL(mock(), - OnAuthFlowFailure(WebAuthFlow::Failure::USER_NAVIGATED_AWAY)); - ASSERT_TRUE(content::NavigateToURL(web_contents(), browsing_url)); - - // New tab is not expected to be closed, it is now used for navigation and - // not part of the flow anymore. - EXPECT_FALSE(web_contents()); - EXPECT_FALSE(web_auth_flow()); - EXPECT_EQ(tabs->GetActiveWebContents()->GetLastCommittedURL(), browsing_url); - // Infobar should be closed on navigation. - EXPECT_FALSE(auth_info_bar); -} - } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc index c9ad487f7e1..a84f713fd5c 100644 --- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc +++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.cc @@ -16,9 +16,6 @@ namespace extensions { -absl::optional<bool> WebAuthFlowInfoBarDelegate::should_animate_for_testing_ = - absl::nullopt; - base::WeakPtr<WebAuthFlowInfoBarDelegate> WebAuthFlowInfoBarDelegate::Create( content::WebContents* web_contents, const std::string& extension_name) { @@ -67,12 +64,4 @@ void WebAuthFlowInfoBarDelegate::CloseInfoBar() { infobar()->RemoveSelf(); } -bool WebAuthFlowInfoBarDelegate::ShouldAnimate() const { - if (should_animate_for_testing_.has_value()) { - return should_animate_for_testing_.value(); - } - - return ConfirmInfoBarDelegate::ShouldAnimate(); -} - } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h index 15429e47fb3..88eee975166 100644 --- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h +++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow_info_bar_delegate.h @@ -8,7 +8,6 @@ #include "components/infobars/core/confirm_infobar_delegate.h" #include "base/memory/weak_ptr.h" -#include "third_party/abseil-cpp/absl/types/optional.h" namespace content { class WebContents; @@ -16,8 +15,6 @@ class WebContents; namespace extensions { -class TestScopedShouldAnimateWebAuthFlowInfoBar; - // Infobar used by extension auth flow `chrome.identity.launchWebAuthFlow()` // when authentication is done through a Browser Tab. A browser tab is opened // when needing action from the user in this flow. @@ -40,19 +37,13 @@ class WebAuthFlowInfoBarDelegate : public ConfirmInfoBarDelegate { // ConfirmInfoBarDelegate: std::u16string GetMessageText() const override; int GetButtons() const override; - bool ShouldAnimate() const override; // Closes the info bar this delegate is associated with. void CloseInfoBar(); private: - friend TestScopedShouldAnimateWebAuthFlowInfoBar; - explicit WebAuthFlowInfoBarDelegate(const std::string& extension_name); - // Only controlled by `TestScopedShouldAnimateWebAuthFlowInfoBar`. - static absl::optional<bool> should_animate_for_testing_; - const std::string extension_name_; base::WeakPtrFactory<WebAuthFlowInfoBarDelegate> weak_factory_{this}; diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc index ac96347fa34..80ae9c39a8f 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_controller_lacros.cc @@ -13,6 +13,7 @@ #include "chromeos/lacros/lacros_service.h" #include "content/public/browser/browser_context.h" #include "extensions/browser/event_router.h" +#include "extensions/common/extension_id.h" namespace image_writer_api = extensions::api::image_writer_private; @@ -113,7 +114,7 @@ class ImageWriterControllerLacros::ImageWriterClientLacros // Note: |this| is deleted at this point. } - const std::string extension_id_; + const ExtensionId extension_id_; // Both pointers of |browser_context_| and |controller_| are guaranteed // to be valid for the lifetime of this class, as destruction of either // BrowserContext or ImageWriterControllerLacros will result in synchronous diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc b/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc index 614302e4896..b0734b96152 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc @@ -25,7 +25,6 @@ #include "extensions/browser/api/extensions_api_client.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_host.h" -#include "extensions/browser/notification_types.h" #include "mojo/public/cpp/bindings/pending_remote.h" #if BUILDFLAG(IS_CHROMEOS_ASH) diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc index 870709971da..adc0b2b3d3a 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos_unittest.cc @@ -102,7 +102,7 @@ class RemovableStorageProviderChromeOsUnitTest : public testing::Test { } content::BrowserTaskEnvironment task_environment_; - raw_ptr<ash::disks::MockDiskMountManager, ExperimentalAsh> + raw_ptr<ash::disks::MockDiskMountManager, DanglingUntriaged | ExperimentalAsh> disk_mount_manager_mock_; scoped_refptr<StorageDeviceList> devices_; }; diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc index 3add088c13c..1ed76b28bcd 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc @@ -11,8 +11,8 @@ #include <IOKit/storage/IOStorageProtocolCharacteristics.h> #include <stdint.h> -#include "base/mac/foundation_util.h" -#include "base/mac/scoped_cftyperef.h" +#include "base/apple/foundation_util.h" +#include "base/apple/scoped_cftyperef.h" #include "base/mac/scoped_ioobject.h" #include "base/memory/scoped_refptr.h" #include "base/strings/sys_string_conversions.h" @@ -27,7 +27,7 @@ RemovableStorageProvider::PopulateDeviceList() { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); // Match only writable whole-disks. - base::ScopedCFTypeRef<CFMutableDictionaryRef> matching( + base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> matching( IOServiceMatching(kIOMediaClass)); CFDictionaryAddValue(matching, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); CFDictionaryAddValue(matching, CFSTR(kIOMediaWritableKey), kCFBooleanTrue); @@ -54,7 +54,7 @@ RemovableStorageProvider::PopulateDeviceList() { if (!is_suitable) continue; - base::ScopedCFTypeRef<CFMutableDictionaryRef> dict; + base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> dict; if (IORegistryEntryCreateCFProperties(disk_obj, dict.InitializeInto(), kCFAllocatorDefault, 0) != KERN_SUCCESS) { @@ -62,12 +62,10 @@ RemovableStorageProvider::PopulateDeviceList() { continue; } - base::ScopedCFTypeRef<CFDictionaryRef> characteristics( + base::apple::ScopedCFTypeRef<CFDictionaryRef> characteristics( static_cast<CFDictionaryRef>(IORegistryEntrySearchCFProperty( - disk_obj, - kIOServicePlane, - CFSTR(kIOPropertyDeviceCharacteristicsKey), - kCFAllocatorDefault, + disk_obj, kIOServicePlane, + CFSTR(kIOPropertyDeviceCharacteristicsKey), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively))); if (!characteristics) { @@ -75,11 +73,11 @@ RemovableStorageProvider::PopulateDeviceList() { continue; } - CFStringRef cf_vendor = base::mac::GetValueFromDictionary<CFStringRef>( + CFStringRef cf_vendor = base::apple::GetValueFromDictionary<CFStringRef>( characteristics, CFSTR(kIOPropertyVendorNameKey)); std::string vendor = base::SysCFStringRefToUTF8(cf_vendor); - CFStringRef cf_model = base::mac::GetValueFromDictionary<CFStringRef>( + CFStringRef cf_model = base::apple::GetValueFromDictionary<CFStringRef>( characteristics, CFSTR(kIOPropertyProductNameKey)); std::string model = base::SysCFStringRefToUTF8(cf_model); diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc index 6bd29e79444..e36c0a59364 100644 --- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc @@ -26,6 +26,7 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/process_manager.h" #include "extensions/common/manifest_handlers/background_info.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/ime/ash/component_extension_ime_manager.h" #include "ui/base/ime/ash/extension_ime_util.h" #include "ui/base/ime/ash/ime_keymap.h" @@ -854,7 +855,7 @@ class ImeObserverChromeOS } } - std::string extension_id_; + extensions::ExtensionId extension_id_; raw_ptr<Profile, DanglingUntriaged> profile_; }; @@ -919,7 +920,9 @@ bool InputImeEventRouter::RegisterImeExtension( std::string(), // TODO(uekawa): Set short name. layout, languages, false, // 3rd party IMEs are always not for login. - component.options_page_url, component.input_view_url)); + component.options_page_url, component.input_view_url, + // Not applicable to 3rd-party IMEs. + /*handwriting_language=*/absl::nullopt)); } } diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc b/chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc index 5b09ea8f979..ab6c7b226b8 100644 --- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_apitest_chromeos.cc @@ -39,7 +39,7 @@ IN_PROC_BROWSER_TEST_F(InputImeApiTest, Basic) { "_ext_ime_ilanclmaeigfpnmdlgelmhkpkegdioiptest"}; ash::input_method::InputMethodManager::Get() ->GetActiveIMEState() - ->SetEnabledExtensionImes(&extension_ime_ids); + ->SetEnabledExtensionImes(extension_ime_ids); ASSERT_TRUE(RunExtensionTest("input_ime")) << message_; } diff --git a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc index d4c0e987adc..f2731f32ddf 100644 --- a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc +++ b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc @@ -341,11 +341,6 @@ LanguageSettingsPrivateEnableLanguageFunction::Run() { std::string chrome_language = language_code; language::ToChromeLanguageSynonym(&chrome_language); - if (base::Contains(languages, chrome_language)) { - LOG(ERROR) << "Language " << chrome_language << " already enabled"; - return RespondNow(NoArguments()); - } - translate_prefs->AddToLanguageList(language_code, /*force_blocked=*/false); return RespondNow(NoArguments()); @@ -372,15 +367,7 @@ LanguageSettingsPrivateDisableLanguageFunction::Run() { std::string chrome_language = language_code; language::ToChromeLanguageSynonym(&chrome_language); - if (!base::Contains(languages, chrome_language)) { - LOG(ERROR) << "Language " << chrome_language << " not enabled"; - return RespondNow(NoArguments()); - } - translate_prefs->RemoveFromLanguageList(language_code); - if (language_code == translate_prefs->GetRecentTargetLanguage()) { - translate_prefs->ResetRecentTargetLanguage(); - } return RespondNow(NoArguments()); } diff --git a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc index bf355ff4813..a35e53d2a8e 100644 --- a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc @@ -31,6 +31,7 @@ #include "extensions/browser/api_test_utils.h" #include "extensions/browser/event_router_factory.h" #include "extensions/browser/extension_prefs.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #if BUILDFLAG(IS_CHROMEOS_ASH) #include "ash/constants/ash_features.h" @@ -463,14 +464,17 @@ class TestInputMethodManager : public input_method::MockInputMethodManager { std::string layout("us"); InputMethodDescriptor extension_ime( GetExtensionImeId(), "ExtensionIme", "", layout, {"vi"}, - false /* is_login_keyboard */, GURL(), GURL()); + false /* is_login_keyboard */, GURL(), GURL(), + /*handwriting_language=*/absl::nullopt); InputMethodDescriptor component_extension_ime( GetComponentExtensionImeId(), "ComponentExtensionIme", "", layout, - {"en-US", "en"}, false /* is_login_keyboard */, GURL(), GURL()); + {"en-US", "en"}, false /* is_login_keyboard */, GURL(), GURL(), + /*handwriting_language=*/absl::nullopt); InputMethodDescriptor arc_ime(GetArcImeId(), "ArcIme", "", layout, {ash::extension_ime_util::kArcImeLanguage}, false /* is_login_keyboard */, GURL(), - GURL()); + GURL(), + /*handwriting_language=*/absl::nullopt); input_methods_ = {extension_ime, component_extension_ime, arc_ime}; } diff --git a/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc index f5ea98249a1..98847287ab3 100644 --- a/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc +++ b/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc @@ -9,7 +9,6 @@ #include "base/functional/bind.h" #include "base/memory/raw_ptr.h" -#include "base/strings/strcat.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -63,65 +62,20 @@ #include "extensions/common/api/management.h" #include "extensions/common/extension.h" #include "extensions/common/extension_urls.h" -#include "services/data_decoder/public/cpp/data_decoder.h" #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h" #include "third_party/blink/public/mojom/window_features/window_features.mojom.h" #if BUILDFLAG(IS_CHROMEOS_ASH) -#include "ash/components/arc/arc_util.h" -#include "ash/components/arc/mojom/intent_helper.mojom.h" -#include "ash/components/arc/session/arc_bridge_service.h" -#include "ash/components/arc/session/arc_service_manager.h" -#include "chrome/browser/ash/app_list/arc/arc_app_utils.h" -#include "chrome/browser/ash/arc/arc_util.h" #include "chrome/browser/ash/login/demo_mode/demo_session.h" #endif // BUILDFLAG(IS_CHROMEOS_ASH) namespace { - -#if BUILDFLAG(IS_CHROMEOS_ASH) -const char kPlayIntentPrefix[] = - "https://play.google.com/store/apps/details?id="; -const char kChromeWebStoreReferrer[] = "&referrer=chrome_web_store"; -#endif // BUILDFLAG(IS_CHROMEOS_ASH) - -using InstallAndroidAppCallback = - extensions::ManagementAPIDelegate::InstallAndroidAppCallback; -using AndroidAppInstallStatusCallback = - extensions::ManagementAPIDelegate::AndroidAppInstallStatusCallback; using InstallOrLaunchWebAppCallback = extensions::ManagementAPIDelegate::InstallOrLaunchWebAppCallback; using InstallOrLaunchWebAppResult = extensions::ManagementAPIDelegate::InstallOrLaunchWebAppResult; using InstallableCheckResult = web_app::InstallableCheckResult; -#if BUILDFLAG(IS_CHROMEOS_ASH) -void OnDidCheckForIntentToPlayStore(const std::string& intent, - InstallAndroidAppCallback callback, - bool installable) { - if (!installable) { - std::move(callback).Run(false); - return; - } - - auto* arc_service_manager = arc::ArcServiceManager::Get(); - if (!arc_service_manager) { - std::move(callback).Run(false); - return; - } - - auto* instance = ARC_GET_INSTANCE_FOR_METHOD( - arc_service_manager->arc_bridge_service()->intent_helper(), HandleUrl); - if (!instance) { - std::move(callback).Run(false); - return; - } - - instance->HandleUrl(intent, arc::kPlayStorePackage); - std::move(callback).Run(true); -} -#endif // BUILDFLAG(IS_CHROMEOS_ASH) - class ManagementSetEnabledFunctionInstallPromptDelegate : public extensions::InstallPromptDelegate { public: @@ -266,7 +220,7 @@ class ChromeAppForLinkDelegate : public extensions::AppForLinkDelegate { } #endif // BUILDFLAG(IS_CHROMEOS_ASH) - auto web_app_info = std::make_unique<WebAppInstallInfo>(); + auto web_app_info = std::make_unique<web_app::WebAppInstallInfo>(); web_app_info->title = base::UTF8ToUTF16(title); web_app_info->start_url = launch_url; web_app_info->display_mode = web_app::DisplayMode::kBrowser; @@ -491,18 +445,6 @@ extensions::LaunchType ChromeManagementAPIDelegate::GetLaunchType( return extensions::GetLaunchType(prefs, extension); } -void ChromeManagementAPIDelegate:: - GetPermissionWarningsByManifestFunctionDelegate( - extensions::ManagementGetPermissionWarningsByManifestFunction* function, - const std::string& manifest_str) const { - data_decoder::DataDecoder::ParseJsonIsolated( - manifest_str, - base::BindOnce( - &extensions::ManagementGetPermissionWarningsByManifestFunction:: - OnParse, - function)); -} - std::unique_ptr<extensions::InstallPromptDelegate> ChromeManagementAPIDelegate::SetEnabledFunctionDelegate( content::WebContents* web_contents, @@ -612,66 +554,6 @@ void ChromeManagementAPIDelegate::InstallOrLaunchReplacementWebApp( std::move(callback), std::move(web_contents))); } -bool ChromeManagementAPIDelegate::CanContextInstallAndroidApps( - content::BrowserContext* context) const { -#if BUILDFLAG(IS_CHROMEOS_ASH) - return arc::IsArcAllowedForProfile(Profile::FromBrowserContext(context)); -#else - return false; -#endif // BUILDFLAG(IS_CHROMEOS_ASH) -} - -void ChromeManagementAPIDelegate::CheckAndroidAppInstallStatus( - const std::string& package_name, - AndroidAppInstallStatusCallback callback) const { -#if BUILDFLAG(IS_CHROMEOS_ASH) - auto* arc_service_manager = arc::ArcServiceManager::Get(); - if (!arc_service_manager) { - std::move(callback).Run(false); - return; - } - - auto* instance = ARC_GET_INSTANCE_FOR_METHOD( - arc_service_manager->arc_bridge_service()->app(), IsInstallable); - if (!instance) { - std::move(callback).Run(false); - return; - } - - instance->IsInstallable(package_name, std::move(callback)); -#else - std::move(callback).Run(false); -#endif // BUILDFLAG(IS_CHROMEOS_ASH) -} - -void ChromeManagementAPIDelegate::InstallReplacementAndroidApp( - const std::string& package_name, - InstallAndroidAppCallback callback) const { -#if BUILDFLAG(IS_CHROMEOS_ASH) - std::string intent = - base::StrCat({kPlayIntentPrefix, package_name, kChromeWebStoreReferrer}); - - auto* arc_service_manager = arc::ArcServiceManager::Get(); - if (!arc_service_manager) { - std::move(callback).Run(false); - return; - } - - auto* instance = ARC_GET_INSTANCE_FOR_METHOD( - arc_service_manager->arc_bridge_service()->app(), IsInstallable); - if (!instance) { - std::move(callback).Run(false); - return; - } - - instance->IsInstallable( - package_name, base::BindOnce(&OnDidCheckForIntentToPlayStore, intent, - std::move(callback))); -#else - std::move(callback).Run(false); -#endif // BUILDFLAG(IS_CHROMEOS_ASH) -} - void ChromeManagementAPIDelegate::EnableExtension( content::BrowserContext* context, const std::string& extension_id) const { diff --git a/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h b/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h index d6f29588886..91da4eb8e88 100644 --- a/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h +++ b/chromium/chrome/browser/extensions/api/management/chrome_management_api_delegate.h @@ -23,9 +23,6 @@ class ChromeManagementAPIDelegate : public extensions::ManagementAPIDelegate { extensions::LaunchType GetLaunchType( const extensions::ExtensionPrefs* prefs, const extensions::Extension* extension) const override; - void GetPermissionWarningsByManifestFunctionDelegate( - extensions::ManagementGetPermissionWarningsByManifestFunction* function, - const std::string& manifest_str) const override; std::unique_ptr<extensions::InstallPromptDelegate> SetEnabledFunctionDelegate( content::WebContents* web_contents, content::BrowserContext* browser_context, @@ -53,15 +50,6 @@ class ChromeManagementAPIDelegate : public extensions::ManagementAPIDelegate { const GURL& web_app_url, ManagementAPIDelegate::InstallOrLaunchWebAppCallback callback) const override; - bool CanContextInstallAndroidApps( - content::BrowserContext* context) const override; - void CheckAndroidAppInstallStatus( - const std::string& package_name, - ManagementAPIDelegate::AndroidAppInstallStatusCallback callback) - const override; - void InstallReplacementAndroidApp( - const std::string& package_name, - ManagementAPIDelegate::InstallAndroidAppCallback callback) const override; void EnableExtension(content::BrowserContext* context, const std::string& extension_id) const override; void DisableExtension( diff --git a/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc index 7bbe060e189..e7877539aa5 100644 --- a/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/management/management_api_browsertest.cc @@ -32,7 +32,6 @@ #include "extensions/browser/extension_host_test_helper.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" -#include "extensions/browser/notification_types.h" #include "extensions/common/extension_builder.h" #include "extensions/common/extension_id.h" #include "extensions/test/extension_test_message_listener.h" diff --git a/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc b/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc index 57cf8c624f9..1ac7fbf7ae7 100644 --- a/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc @@ -865,9 +865,6 @@ class TestManagementAPIDelegate : public ManagementAPIDelegate { const Extension* extension) const override { return LaunchType::LAUNCH_TYPE_DEFAULT; } - void GetPermissionWarningsByManifestFunctionDelegate( - ManagementGetPermissionWarningsByManifestFunction* function, - const std::string& manifest_str) const override {} std::unique_ptr<InstallPromptDelegate> SetEnabledFunctionDelegate( content::WebContents* web_contents, content::BrowserContext* browser_context, @@ -905,6 +902,7 @@ class TestManagementAPIDelegate : public ManagementAPIDelegate { void SetLaunchType(content::BrowserContext* context, const std::string& extension_id, LaunchType launch_type) const override {} + std::unique_ptr<AppForLinkDelegate> GenerateAppForLinkFunctionDelegate( ManagementGenerateAppForLinkFunction* function, content::BrowserContext* context, @@ -920,16 +918,6 @@ class TestManagementAPIDelegate : public ManagementAPIDelegate { content::BrowserContext* context, const GURL& web_app_url, InstallOrLaunchWebAppCallback callback) const override {} - bool CanContextInstallAndroidApps( - content::BrowserContext* context) const override { - return true; - } - void CheckAndroidAppInstallStatus( - const std::string& package_name, - AndroidAppInstallStatusCallback callback) const override {} - void InstallReplacementAndroidApp( - const std::string& package_name, - InstallAndroidAppCallback callback) const override {} GURL GetIconURL(const Extension* extension, int icon_size, ExtensionIconSet::MatchType match, diff --git a/chromium/chrome/browser/extensions/api/management/management_apitest.cc b/chromium/chrome/browser/extensions/api/management/management_apitest.cc index f620dc0e9dc..5f6e396ca92 100644 --- a/chromium/chrome/browser/extensions/api/management/management_apitest.cc +++ b/chromium/chrome/browser/extensions/api/management/management_apitest.cc @@ -7,7 +7,6 @@ #include "base/auto_reset.h" #include "base/strings/stringprintf.h" #include "base/test/gtest_tags.h" -#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/extensions/extension_apitest.h" @@ -27,7 +26,6 @@ #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_registrar.h" -#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_constants.h" #include "content/public/test/browser_test.h" @@ -198,32 +196,6 @@ IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, GenerateAppForLink) { ASSERT_TRUE(RunExtensionTest("management/generate_app_for_link")); } -#if BUILDFLAG(IS_CHROMEOS_ASH) -class GenerateAppForLinkWithLacrosWebAppsApiTest - : public ExtensionManagementApiTest { - public: - GenerateAppForLinkWithLacrosWebAppsApiTest() { - features_.InitAndEnableFeature(features::kWebAppsCrosapi); - } - - private: - base::test::ScopedFeatureList features_; -}; - -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - GenerateAppForLinkWithLacrosWebAppsApiTest, - ::testing::Values(ContextType::kPersistentBackground)); -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - GenerateAppForLinkWithLacrosWebAppsApiTest, - ::testing::Values(ContextType::kServiceWorker)); - -IN_PROC_BROWSER_TEST_P(GenerateAppForLinkWithLacrosWebAppsApiTest, - GenerateAppForLink) { - web_app::test::WaitUntilReady(web_app::WebAppProvider::GetForTest(profile())); - ASSERT_TRUE(RunExtensionTest("management/generate_app_for_link_lacros")); -} -#endif // BUILDFLAG(IS_CHROMEOS_ASH) - class InstallReplacementWebAppApiTest : public ExtensionManagementApiTest { public: InstallReplacementWebAppApiTest() @@ -388,42 +360,6 @@ IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, InstallableWebApp) { } #endif -#if BUILDFLAG(IS_CHROMEOS_ASH) -class InstallReplacementWebAppWithLacrosWebAppsApiTest - : public InstallReplacementWebAppApiTest { - public: - InstallReplacementWebAppWithLacrosWebAppsApiTest() { - features_.InitAndEnableFeature(features::kWebAppsCrosapi); - } - - private: - base::test::ScopedFeatureList features_; -}; - -INSTANTIATE_TEST_SUITE_P(PersistentBackground, - InstallReplacementWebAppWithLacrosWebAppsApiTest, - ::testing::Values(ContextType::kPersistentBackground)); -INSTANTIATE_TEST_SUITE_P(ServiceWorker, - InstallReplacementWebAppWithLacrosWebAppsApiTest, - ::testing::Values(ContextType::kServiceWorker)); - -IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppWithLacrosWebAppsApiTest, - InstallableWebApp) { - static constexpr char kGoodWebAppURL[] = - "/management/install_replacement_web_app/acceptable_web_app/index.html"; - static constexpr char kBackground[] = - R"(chrome.test.runWithUserGesture(function() { - chrome.management.installReplacementWebApp(function() { - chrome.test.assertLastError( - 'Web apps can\'t be installed in the current user profile.'); - chrome.test.notifyPass(); - }); - });)"; - - RunTest(kManifest, kGoodWebAppURL, kBackground, true /* from_webstore */); -} -#endif // BUILDFLAG(IS_CHROMEOS_ASH) - #if !BUILDFLAG(IS_CHROMEOS_LACROS) // TODO(crbug.com/1288199): Run these tests on Chrome OS with both Ash and // Lacros processes active. diff --git a/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc b/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc index 85c074cf1a5..49f16877a8e 100644 --- a/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc +++ b/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc @@ -241,10 +241,10 @@ void MDnsAPI::WriteToConsole(const std::string& service_type, extensions::ExtensionHost* host = extensions::ProcessManager::Get(browser_context_) ->GetBackgroundHostForExtension(extension_id); - content::RenderFrameHost* rfh = + content::RenderFrameHost* render_frame_host = host ? host->host_contents()->GetPrimaryMainFrame() : nullptr; - if (rfh) { - rfh->AddMessageToConsole(level, logged_message); + if (render_frame_host) { + render_frame_host->AddMessageToConsole(level, logged_message); } } } diff --git a/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc b/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc index d062790716a..b4f1776306d 100644 --- a/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc +++ b/chromium/chrome/browser/extensions/api/messaging/chrome_messaging_delegate.cc @@ -119,10 +119,10 @@ std::unique_ptr<MessagePort> ChromeMessagingDelegate::CreateReceiverForTab( bool include_child_frames = receiver_frame_id == -1 && receiver_document_id.empty(); - content::RenderFrameHost* receiver_rfh = nullptr; + content::RenderFrameHost* receiver_render_frame_host = nullptr; if (include_child_frames) { // The target is the active outermost main frame of the WebContents. - receiver_rfh = receiver_contents->GetPrimaryMainFrame(); + receiver_render_frame_host = receiver_contents->GetPrimaryMainFrame(); } else if (!receiver_document_id.empty()) { ExtensionApiFrameIdMap::DocumentId document_id = ExtensionApiFrameIdMap::DocumentIdFromString(receiver_document_id); @@ -131,28 +131,30 @@ std::unique_ptr<MessagePort> ChromeMessagingDelegate::CreateReceiverForTab( if (!document_id) return nullptr; - receiver_rfh = + receiver_render_frame_host = ExtensionApiFrameIdMap::Get()->GetRenderFrameHostByDocumentId( document_id); // If both |document_id| and |receiver_frame_id| are provided they // should find the same RenderFrameHost, if not return early. if (receiver_frame_id != -1 && - ExtensionApiFrameIdMap::GetRenderFrameHostById( - receiver_contents, receiver_frame_id) != receiver_rfh) { + ExtensionApiFrameIdMap::GetRenderFrameHostById(receiver_contents, + receiver_frame_id) != + receiver_render_frame_host) { return nullptr; } } else { DCHECK_GT(receiver_frame_id, -1); - receiver_rfh = ExtensionApiFrameIdMap::GetRenderFrameHostById( + receiver_render_frame_host = ExtensionApiFrameIdMap::GetRenderFrameHostById( receiver_contents, receiver_frame_id); } - if (!receiver_rfh) + if (!receiver_render_frame_host) { return nullptr; + } return std::make_unique<ExtensionMessagePort>( - channel_delegate, receiver_port_id, extension_id, receiver_rfh, - include_child_frames); + channel_delegate, receiver_port_id, extension_id, + receiver_render_frame_host, include_child_frames); } std::unique_ptr<MessagePort> diff --git a/chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc b/chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc index 139a2e1facf..0a04d003a92 100644 --- a/chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc +++ b/chromium/chrome/browser/extensions/api/messaging/messaging_apitest.cc @@ -39,6 +39,7 @@ #include "components/infobars/content/content_infobar_manager.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/service_worker_context.h" #include "content/public/browser/storage_partition.h" @@ -335,6 +336,18 @@ class ExternallyConnectableMessagingTest : public MessagingApiTest { return static_cast<Result>(result); } + Result CanUseSendMessagePromise(const Extension* extension) { + content::RenderFrameHost* frame = browser() + ->tab_strip_model() + ->GetActiveWebContents() + ->GetPrimaryMainFrame(); + std::string command = + content::JsReplace("assertions.canUseSendMessagePromise($1, $2)", + extension->id(), extension->is_platform_app()); + int result = content::EvalJs(frame, command).ExtractInt(); + return static_cast<Result>(result); + } + testing::AssertionResult AreAnyNonWebApisDefinedForMainFrame() { return AreAnyNonWebApisDefinedForFrame(browser() ->tab_strip_model() @@ -655,6 +668,18 @@ IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, EXPECT_FALSE(AreAnyNonWebApisDefinedForMainFrame()); } +// Tests that an externally connectable web page context can use the promise +// based form of sendMessage. +IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, + SendMessagePromiseSignatureExposed) { + // Install the web connectable extension. + scoped_refptr<const Extension> chromium_connectable = + LoadChromiumConnectableExtension(); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), chromium_org_url())); + EXPECT_EQ(OK, CanUseSendMessagePromise(chromium_connectable.get())); +} + // See http://crbug.com/297866 IN_PROC_BROWSER_TEST_F(ExternallyConnectableMessagingTest, DISABLED_BackgroundPageClosesOnMessageReceipt) { diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc b/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc index c64f72ca057..2e1268e6972 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc @@ -69,8 +69,7 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingApiTestBase, UserLevelSendNativeMessage) { #if BUILDFLAG(IS_WIN) // On Windows, a new codepath is used to directly launch .EXE-based Native // Hosts. This codepath allows launching of Native Hosts even when cmd.exe is -// disabled, or if the path to the host contains a character that prevents -// cmd.exe from successfully launching it (e.g. "&" in this test). +// disabled or misconfigured. class NativeMessagingLaunchExeTest : public NativeMessagingApiTestBase, public testing::WithParamInterface<bool> { public: @@ -90,23 +89,33 @@ INSTANTIATE_TEST_SUITE_P(NativeMessagingLaunchExe, NativeMessagingLaunchExeTest, testing::Bool()); -IN_PROC_BROWSER_TEST_P(NativeMessagingLaunchExeTest, SendNativeMessageWinExe) { - ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost(/*user_level=*/false)); +IN_PROC_BROWSER_TEST_P(NativeMessagingLaunchExeTest, + UserLevelSendNativeMessageWinExe) { + ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost( + "native_messaging_test_echo_host.exe", /*user_level=*/true)); - // The extension works properly only if the host launches successfully, which - // requires the kLaunchWindowsNativeHostsDirectly feature to be enabled. - ASSERT_EQ(IsDirectLaunchEnabled(), - RunExtensionTest("native_messaging_send_native_message_exe")); + ASSERT(RunExtensionTest("native_messaging_send_native_message_exe")); } +// The Host's filename deliberately contains the character '&' which causes the +// Host to fail to launch if cmd.exe is used as an intermediary between the +// extension and the host executable, unless extra quotes are used. +// crbug.com/335558 IN_PROC_BROWSER_TEST_P(NativeMessagingLaunchExeTest, - UserLevelSendNativeMessageWinExe) { - ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost(/*user_level=*/true)); + SendNativeMessageWinExeAmpersand) { + ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost( + "native_messaging_test_echo_&_host.exe", /*user_level=*/false)); + + ASSERT(RunExtensionTest("native_messaging_send_native_message_exe")); +} + +// Make sure that a filename with a space is supported. +IN_PROC_BROWSER_TEST_P(NativeMessagingLaunchExeTest, + SendNativeMessageWinExeSpace) { + ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestExeHost( + "native_messaging_test_echo_ _host.exe", /*user_level=*/false)); - // The extension works properly only if the host launches successfully, which - // requires the kLaunchWindowsNativeHostsDirectly feature to be enabled. - ASSERT_EQ(IsDirectLaunchEnabled(), - RunExtensionTest("native_messaging_send_native_message_exe")); + ASSERT(RunExtensionTest("native_messaging_send_native_message_exe")); } #endif diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc b/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc index 719f64b9f2d..7d1bfe22b01 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc @@ -116,7 +116,7 @@ class ExtensionSupportsConnectionFromNativeAppTest : public ::testing::Test { content::BrowserTaskEnvironment task_environment_; bool has_listener_result_ = true; TestingProfile profile_; - std::string extension_id_; + ExtensionId extension_id_; }; TEST_F(ExtensionSupportsConnectionFromNativeAppTest, Success) { diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc b/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc index 3861f5e1c6e..6ff032ba83c 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.cc @@ -130,7 +130,9 @@ void ScopedTestNativeMessagingHost::RegisterTestHost(bool user_level) { #if BUILDFLAG(IS_WIN) // On Windows, a new codepath is used to directly launch .EXE-based Native // Hosts. -void ScopedTestNativeMessagingHost::RegisterTestExeHost(bool user_level) { +void ScopedTestNativeMessagingHost::RegisterTestExeHost( + std::string_view filename, + bool user_level) { base::ScopedAllowBlockingForTesting allow_blocking; ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); @@ -142,12 +144,7 @@ void ScopedTestNativeMessagingHost::RegisterTestExeHost(bool user_level) { // Unlike in the |RegisterTestHost| case above, we must leave the Host // .exe where it was built, because the Host will fail to run from the // temp_dir_ if is_component_build is set for the build. - // - // The Host's filename deliberately contains the character '&' which causes - // the Host to fail to launch if cmd.exe is used as an intermediary between - // the extension and the host executable. crbug.com/335558 - base::FilePath host_path = - binary_dir.AppendASCII("native_messaging_test_echo_&_host.exe"); + base::FilePath host_path = binary_dir.AppendASCII(filename); ASSERT_NO_FATAL_FAILURE(WriteTestNativeHostManifest( temp_dir_.GetPath(), kHostExeName, host_path, user_level, false)); } diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h b/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h index 525fc3e1b4f..007cf5d05a0 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h +++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_test_util.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGING_TEST_UTIL_H_ #include <memory> +#include <string_view> #include "base/files/scoped_temp_dir.h" #include "build/build_config.h" @@ -47,9 +48,8 @@ class ScopedTestNativeMessagingHost { void RegisterTestHost(bool user_level); #if BUILDFLAG(IS_WIN) - // Register the Windows-only |native_messaging_test_echo_host.exe| Native - // Host. - void RegisterTestExeHost(bool user_level); + // Register the Windows-only Native Host exe. + void RegisterTestExeHost(std::string_view filename, bool user_level); #endif const base::FilePath& temp_dir() { return temp_dir_.GetPath(); } diff --git a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc index 20680fbf140..5de3521d4d3 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher_win.cc @@ -145,7 +145,7 @@ base::Process LaunchNativeHostViaCmd(const std::wstring& command, L"COMSPEC", base::WriteInto(&comspec, comspec_length), comspec_length); std::wstring wrapped_command = base::StringPrintf( - L"%ls /d /c %ls < %ls > %ls", comspec.c_str(), command.c_str(), + L"%ls /d /s /c \"%ls\" < %ls > %ls", comspec.c_str(), command.c_str(), in_pipe_name.c_str(), out_pipe_name.c_str()); return base::LaunchProcess(wrapped_command, options); diff --git a/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc index 42dba0f0ca1..cf27fcf9bbd 100644 --- a/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc +++ b/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc @@ -11,6 +11,7 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/test/bind.h" +#include "base/test/test_future.h" #include "base/values.h" #include "chrome/browser/extensions/extension_apitest.h" #include "components/onc/onc_constants.h" @@ -53,11 +54,8 @@ #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h" #include "chromeos/crosapi/mojom/test_controller.mojom.h" #include "chromeos/lacros/lacros_service.h" - -using crosapi::mojom::ShillClientTestInterfaceAsyncWaiter; #endif // This tests the Chrome OS implementation of the networkingPrivate API @@ -510,11 +508,12 @@ class NetworkingPrivateChromeOSApiTestLacros LOG(ERROR) << "Unsupported ash version."; return false; } - crosapi::mojom::TestControllerAsyncWaiter test_controller_waiter{ - service->GetRemote<crosapi::mojom::TestController>().get()}; - test_controller_waiter.BindShillClientTestInterface( - shill_test_.BindNewPipeAndPassReceiver()); + base::test::TestFuture<void> future; + service->GetRemote<crosapi::mojom::TestController>() + ->BindShillClientTestInterface(shill_test_.BindNewPipeAndPassReceiver(), + future.GetCallback()); + EXPECT_TRUE(future.Wait()); ConfigFakeNetwork(); @@ -533,74 +532,89 @@ class NetworkingPrivateChromeOSApiTestLacros return ""; } - crosapi::mojom::TestControllerAsyncWaiter test_controller_waiter{ - service->GetRemote<crosapi::mojom::TestController>().get()}; - - std::string userhash; - test_controller_waiter.GetSanitizedActiveUsername(&userhash); - return userhash; + base::test::TestFuture<const std::string&> future; + service->GetRemote<crosapi::mojom::TestController>() + ->GetSanitizedActiveUsername(future.GetCallback()); + return future.Take(); } void AddDevice(const std::string& device_path, const std::string& type, const std::string& name) override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()) - .AddDevice(device_path, type, name); + base::test::TestFuture<void> future; + shill_test_->AddDevice(device_path, type, name, future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void SetDeviceProperty(const std::string& device_path, const std::string& name, const base::Value& value) override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()) - .SetDeviceProperty(device_path, name, value.Clone(), - /*notify_changed=*/true); + base::test::TestFuture<void> future; + shill_test_->SetDeviceProperty(device_path, name, value.Clone(), + /*notify_changed=*/true, + future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void SetSimLocked(const std::string& device_path, bool enabled) override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()) - .SetSimLocked(device_path, enabled); + base::test::TestFuture<void> future; + shill_test_->SetSimLocked(device_path, enabled, future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void ClearDevices() override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()).ClearDevices(); + base::test::TestFuture<void> future; + shill_test_->ClearDevices(future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void AddService(const std::string& service_path, const std::string& name, const std::string& type, const std::string& state) override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()) - .AddService(service_path, service_path + "_guid", name, type, state, - true /* add_to_visible */); + base::test::TestFuture<void> future; + shill_test_->AddService(service_path, service_path + "_guid", name, type, + state, true /* add_to_visible */, + future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void ClearServices() override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()).ClearServices(); + base::test::TestFuture<void> future; + shill_test_->ClearServices(future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void SetServiceProperty(const std::string& service_path, const std::string& property, const base::Value& value) override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()) - .SetServiceProperty(service_path, property, value.Clone()); + base::test::TestFuture<void> future; + shill_test_->SetServiceProperty(service_path, property, value.Clone(), + future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void AddIPConfig(const std::string& ip_config_path, base::Value::Dict properties) override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()) - .AddIPConfig(ip_config_path, base::Value(std::move(properties))); + base::test::TestFuture<void> future; + shill_test_->AddIPConfig(ip_config_path, base::Value(std::move(properties)), + future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void AddProfile(const std::string& profile_path, const std::string& userhash) override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()) - .AddProfile(profile_path, userhash); + base::test::TestFuture<void> future; + shill_test_->AddProfile(profile_path, userhash, future.GetCallback()); + ASSERT_TRUE(future.Wait()); } void AddServiceToProfile(const std::string& profile_path, const std::string& service_path) override { - ShillClientTestInterfaceAsyncWaiter(shill_test_.get()) - .AddServiceToProfile(profile_path, service_path); + base::test::TestFuture<void> future; + shill_test_->AddServiceToProfile(profile_path, service_path, + future.GetCallback()); + ASSERT_TRUE(future.Wait()); } std::string GetSharedProfilePath() override { diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc index ab06550c58b..173bb0edf07 100644 --- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc +++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.cc @@ -39,11 +39,11 @@ ExtensionNotificationDisplayHelperFactory:: ExtensionNotificationDisplayHelperFactory:: ~ExtensionNotificationDisplayHelperFactory() = default; -KeyedService* -ExtensionNotificationDisplayHelperFactory::BuildServiceInstanceFor( - content::BrowserContext* context) const { +std::unique_ptr<KeyedService> ExtensionNotificationDisplayHelperFactory:: + BuildServiceInstanceForBrowserContext( + content::BrowserContext* context) const { Profile* profile = Profile::FromBrowserContext(context); - return new ExtensionNotificationDisplayHelper(profile); + return std::make_unique<ExtensionNotificationDisplayHelper>(profile); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h index 1f726c1262f..d31b3040d97 100644 --- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h +++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h @@ -30,7 +30,7 @@ class ExtensionNotificationDisplayHelperFactory protected: // Overridden from BrowserContextKeyedServiceFactory. - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const override; private: diff --git a/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc b/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc index ea75249bd7f..d9b0f852288 100644 --- a/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc +++ b/chromium/chrome/browser/extensions/api/notifications/extension_notification_handler_unittest.cc @@ -48,7 +48,7 @@ class TestExtensionNotificationHandler : public ExtensionNotificationHandler { } private: - std::string extension_id_; + ExtensionId extension_id_; std::string event_name_; size_t param_count_; }; diff --git a/chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc index 682ba13ad4d..58be93e34f2 100644 --- a/chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc +++ b/chromium/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc @@ -26,6 +26,7 @@ #include "components/omnibox/browser/autocomplete_input.h" #include "components/omnibox/browser/autocomplete_match.h" #include "components/omnibox/browser/autocomplete_result.h" +#include "components/omnibox/browser/omnibox_controller.h" #include "components/omnibox/browser/omnibox_edit_model.h" #include "components/omnibox/browser/omnibox_view.h" #include "content/public/test/browser_test.h" @@ -130,7 +131,7 @@ class OmniboxApiTest : public ExtensionApiTest, Browser* browser) { return GetLocationBar(browser) ->GetOmniboxView() - ->model() + ->controller() ->autocomplete_controller(); } }; diff --git a/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc b/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc index 0dbcb52167b..d532c4a1aff 100644 --- a/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc +++ b/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.cc @@ -20,7 +20,6 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/mhtml_generation_params.h" #include "extensions/browser/extension_util.h" -#include "extensions/common/extension_messages.h" #include "extensions/common/permissions/permissions_data.h" using content::BrowserThread; @@ -116,30 +115,7 @@ bool PageCaptureSaveAsMHTMLFunction::CanCaptureCurrentPage(std::string* error) { return can_capture_page; } -bool PageCaptureSaveAsMHTMLFunction::OnMessageReceived( - const IPC::Message& message) { - if (message.type() != ExtensionHostMsg_ResponseAck::ID) - return false; - - int message_request_id; - base::PickleIterator iter(message); - if (!iter.ReadInt(&message_request_id)) { - NOTREACHED() << "malformed extension message"; - return true; - } - - if (message_request_id != request_id()) - return false; - - // The extension process has processed the response and has created a - // reference to the blob, it is safe for us to go away. - Release(); // Balanced in Run() - - return true; -} - -void PageCaptureSaveAsMHTMLFunction::OnServiceWorkerAck() { - DCHECK(is_from_service_worker()); +void PageCaptureSaveAsMHTMLFunction::OnResponseAck() { // The extension process has processed the response and has created a // reference to the blob, it is safe for us to go away. // This instance may be deleted after this call, so no code goes after @@ -238,7 +214,7 @@ void PageCaptureSaveAsMHTMLFunction::ReturnSuccess(int file_size) { base::Value::Dict response; response.Set("mhtmlFilePath", mhtml_path_.AsUTF8Unsafe()); response.Set("mhtmlFileLength", file_size); - response.Set("requestId", request_id()); + response.Set("requestId", request_uuid().AsLowercaseString()); // Add a reference, extending the lifespan of this extension function until // the response has been received by the renderer. This function generates a @@ -248,8 +224,7 @@ void PageCaptureSaveAsMHTMLFunction::ReturnSuccess(int file_size) { // renderer has it's reference, so we can release ours. // TODO(crbug.com/1050887): Potential memory leak here. AddRef(); // Balanced in either OnMessageReceived() - if (is_from_service_worker()) - AddWorkerResponseTarget(); + AddResponseTarget(); Respond(WithArguments(std::move(response))); } diff --git a/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h b/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h index ac5d18d2b81..cf8ef0643d5 100644 --- a/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h +++ b/chromium/chrome/browser/extensions/api/page_capture/page_capture_api.h @@ -39,13 +39,11 @@ class PageCaptureSaveAsMHTMLFunction : public ExtensionFunction { }; static void SetTestDelegate(TestDelegate* delegate); - // ExtensionFunction: - void OnServiceWorkerAck() override; - private: + // ExtensionFunction: ~PageCaptureSaveAsMHTMLFunction() override; ResponseAction Run() override; - bool OnMessageReceived(const IPC::Message& message) override; + void OnResponseAck() override; // Returns whether or not the extension has permission to capture the current // page. Sets |*error| to an error value on failure. diff --git a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc index 3f0d9413528..29a779743fe 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc @@ -172,7 +172,7 @@ api::passwords_private::PasswordCheckState ConvertPasswordCheckState( std::string FormatElapsedTime(base::Time time) { const base::TimeDelta elapsed_time = base::Time::Now() - time; if (elapsed_time < base::Minutes(1)) - return l10n_util::GetStringUTF8(IDS_SETTINGS_PASSWORDS_JUST_NOW); + return l10n_util::GetStringUTF8(IDS_PASSWORD_MANAGER_UI_JUST_NOW); return base::UTF16ToUTF8(TimeFormat::SimpleWithMonthAndYear( TimeFormat::FORMAT_ELAPSED, TimeFormat::LENGTH_LONG, elapsed_time, true)); @@ -293,7 +293,7 @@ PasswordCheckDelegate::GetCredentialsWithReusedPassword() { bool PasswordCheckDelegate::MuteInsecureCredential( const api::passwords_private::PasswordUiEntry& credential) { // Try to obtain the original CredentialUIEntry. Return false if fails. - const CredentialUIEntry* entry = FindMatchingEntry(credential); + const CredentialUIEntry* entry = id_generator_->TryGetKey(credential.id); if (!entry) return false; @@ -303,25 +303,13 @@ bool PasswordCheckDelegate::MuteInsecureCredential( bool PasswordCheckDelegate::UnmuteInsecureCredential( const api::passwords_private::PasswordUiEntry& credential) { // Try to obtain the original CredentialUIEntry. Return false if fails. - const CredentialUIEntry* entry = FindMatchingEntry(credential); + const CredentialUIEntry* entry = id_generator_->TryGetKey(credential.id); if (!entry) return false; return insecure_credentials_manager_.UnmuteCredential(*entry); } -// Records that a change password flow was started for |credential|. -void PasswordCheckDelegate::RecordChangePasswordFlowStarted( - const api::passwords_private::PasswordUiEntry& credential) { - // If the |credential| does not have a |change_password_url|, skip it. - if (!credential.change_password_url) - return; - - GetPasswordChangeSuccessTracker()->OnManualChangePasswordFlowStarted( - GURL(*credential.change_password_url), credential.username, - PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings); -} - void PasswordCheckDelegate::StartPasswordCheck( StartPasswordCheckCallback callback) { // If the delegate isn't initialized yet, enqueue the callback and return @@ -347,12 +335,9 @@ void PasswordCheckDelegate::StartPasswordAnalyses( insecure_credentials_manager_.StartWeakCheck(base::BindOnce( &PasswordCheckDelegate::RecordAndNotifyAboutCompletedWeakPasswordCheck, weak_ptr_factory_.GetWeakPtr())); - if (base::FeatureList::IsEnabled( - password_manager::features::kPasswordManagerRedesign)) { - insecure_credentials_manager_.StartReuseCheck( - base::BindOnce(&PasswordCheckDelegate::NotifyPasswordCheckStatusChanged, - weak_ptr_factory_.GetWeakPtr())); - } + insecure_credentials_manager_.StartReuseCheck( + base::BindOnce(&PasswordCheckDelegate::NotifyPasswordCheckStatusChanged, + weak_ptr_factory_.GetWeakPtr())); auto progress = base::MakeRefCounted<PasswordCheckProgress>(); for (const auto& password : saved_passwords_presenter_->GetSavedPasswords()) progress->IncrementCounts(password); @@ -366,16 +351,6 @@ void PasswordCheckDelegate::StartPasswordAnalyses( bulk_leak_check_service_adapter_.GetBulkLeakCheckState()); } -void PasswordCheckDelegate::StopPasswordCheck() { - if (!is_initialized_) { - for (auto&& callback : std::exchange(start_check_callbacks_, {})) - std::move(callback).Run(State::kIdle); - return; - } - - bulk_leak_check_service_adapter_.StopBulkLeakCheck(); -} - api::passwords_private::PasswordCheckStatus PasswordCheckDelegate::GetPasswordCheckStatus() const { api::passwords_private::PasswordCheckStatus result; @@ -481,22 +456,6 @@ void PasswordCheckDelegate::OnCredentialDone( } } -const CredentialUIEntry* PasswordCheckDelegate::FindMatchingEntry( - const api::passwords_private::PasswordUiEntry& credential) const { - const CredentialUIEntry* entry = id_generator_->TryGetKey(credential.id); - if (!entry) - return nullptr; - - if (credential.urls.signon_realm != entry->GetFirstSignonRealm() || - credential.username != base::UTF16ToUTF8(entry->username) || - (credential.password && - *credential.password != base::UTF16ToUTF8(entry->password))) { - return nullptr; - } - - return entry; -} - void PasswordCheckDelegate:: RecordAndNotifyAboutCompletedCompromisedPasswordCheck() { profile_->GetPrefs()->SetDouble( @@ -534,10 +493,7 @@ api::passwords_private::PasswordUiEntry PasswordCheckDelegate::ConstructInsecureCredentialUiEntry( CredentialUIEntry entry) { api::passwords_private::PasswordUiEntry api_credential; - api_credential.is_android_credential = - password_manager::IsValidAndroidFacetURI(entry.GetFirstSignonRealm()); api_credential.username = base::UTF16ToUTF8(entry.username); - api_credential.urls = CreateUrlCollectionFromCredential(entry); api_credential.stored_in = StoreSetFromCredential(entry); api_credential.compromised_info = CreateCompromiseInfo(entry); absl::optional<GURL> change_password_url = entry.GetChangePasswordURL(); diff --git a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h index 630d8861872..8c144fddc8d 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h +++ b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate.h @@ -76,18 +76,12 @@ class PasswordCheckDelegate bool UnmuteInsecureCredential( const api::passwords_private::PasswordUiEntry& credential); - // Records that a change password flow was started for `credential`. - void RecordChangePasswordFlowStarted( - const api::passwords_private::PasswordUiEntry& credential); - // Checks that all preconditions for running a password check are fulfilled // and, once that is the case, launches the password check. Invokes `callback` // once a check is running or the request was stopped via // `StopPasswordCheck()`. void StartPasswordCheck( StartPasswordCheckCallback callback = base::DoNothing()); - // Stops checking for insecure passwords. - void StopPasswordCheck(); // Returns the current status of the password check. api::passwords_private::PasswordCheckStatus GetPasswordCheckStatus() const; @@ -113,14 +107,6 @@ class PasswordCheckDelegate void OnCredentialDone(const password_manager::LeakCheckCredential& credential, password_manager::IsLeaked is_leaked) override; - // Tries to find the matching CredentialUIEntry for |credential|. It - // performs a look-up in |id_generator_| using |credential.id|. If a matching - // value exists it also verifies that signon realm, username and when possible - // password match. Returns a pointer to the matching CredentialUIEntry on - // success or nullptr otherwise. - const password_manager::CredentialUIEntry* FindMatchingEntry( - const api::passwords_private::PasswordUiEntry& credential) const; - // Starts the analyses of whether credentials are compromised and/or weak. // Assumes that `StartPasswordCheck()` was called prior. void StartPasswordAnalyses(StartPasswordCheckCallback callback); @@ -168,7 +154,7 @@ class PasswordCheckDelegate // List of callbacks that were passed to `StartPasswordCheck()` prior to the // delegate being initialized. These will be run when either initialization - // finishes, or `StopPasswordCheck()` gets invoked before hand. + // finishes. std::vector<StartPasswordCheckCallback> start_check_callbacks_; // Remembers the progress of the ongoing check. Null if no check is currently diff --git a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc index aa9f3a7fa25..2c1b03607c7 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc @@ -33,7 +33,7 @@ #include "chrome/test/base/testing_profile.h" #include "components/keyed_service/core/keyed_service.h" #include "components/password_manager/content/browser/password_change_success_tracker_factory.h" -#include "components/password_manager/core/browser/affiliation/mock_affiliation_service.h" +#include "components/password_manager/core/browser/affiliation/fake_affiliation_service.h" #include "components/password_manager/core/browser/bulk_leak_check_service.h" #include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h" #include "components/password_manager/core/browser/leak_detection/leak_detection_delegate_interface.h" @@ -89,9 +89,9 @@ constexpr char16_t kWeakPassword2[] = u"111111"; constexpr char kGoogleAccounts[] = "https://accounts.google.com"; using api::passwords_private::CompromisedInfo; +using api::passwords_private::DomainInfo; using api::passwords_private::PasswordCheckStatus; using api::passwords_private::PasswordUiEntry; -using api::passwords_private::UrlCollection; using password_manager::BulkLeakCheckDelegateInterface; using password_manager::BulkLeakCheckService; using password_manager::InsecureType; @@ -241,12 +241,6 @@ PasswordForm MakeSavedAndroidPassword( return form; } -auto ExpectUrls(const std::string& formatted_origin, - const std::string& detailed_origin) { - return AllOf(Field(&UrlCollection::shown, formatted_origin), - Field(&UrlCollection::link, detailed_origin)); -} - // Creates matcher for a given compromised info. auto ExpectCompromisedInfo( base::TimeDelta elapsed_time_since_compromise, @@ -262,21 +256,15 @@ auto ExpectCompromisedInfo( } // Creates matcher for a given compromised credential -auto ExpectCredential(const std::string& formatted_origin, - const std::string& detailed_origin, - const absl::optional<std::string>& change_password_url, +auto ExpectCredential(const absl::optional<std::string>& change_password_url, const std::u16string& username) { return AllOf( Field(&PasswordUiEntry::username, base::UTF16ToASCII(username)), - Field(&PasswordUiEntry::urls, - ExpectUrls(formatted_origin, detailed_origin)), Field(&PasswordUiEntry::change_password_url, change_password_url)); } // Creates matcher for a given compromised credential auto ExpectCompromisedCredential( - const std::string& formatted_origin, - const std::string& detailed_origin, const absl::optional<std::string>& change_password_url, const std::u16string& username, base::TimeDelta elapsed_time_since_compromise, @@ -291,12 +279,10 @@ auto ExpectCompromisedCredential( return AllOf( Field(&PasswordUiEntry::username, base::UTF16ToASCII(username)), change_password_url_field_matcher, - Field(&PasswordUiEntry::urls, - ExpectUrls(formatted_origin, detailed_origin)), Field(&PasswordUiEntry::compromised_info, Optional(ExpectCompromisedInfo(elapsed_time_since_compromise, - elapsed_time_since_compromise_str, - compromise_types)))); + elapsed_time_since_compromise_str, + compromise_types)))); } class PasswordCheckDelegateTest : public ::testing::Test { @@ -349,7 +335,7 @@ class PasswordCheckDelegateTest : public ::testing::Test { raw_ptr<syncer::TestSyncService> sync_service_ = CreateAndUseSyncService(&profile_); IdGenerator credential_id_generator_; - password_manager::MockAffiliationService affiliation_service_; + password_manager::FakeAffiliationService affiliation_service_; SavedPasswordsPresenter presenter_{&affiliation_service_, store_, account_store_}; PasswordCheckDelegate delegate_{&profile_, &presenter_, @@ -370,13 +356,10 @@ TEST_F(PasswordCheckDelegateTest, GetInsecureCredentialsFillsFieldsCorrectly) { EXPECT_THAT( delegate().GetInsecureCredentials(), UnorderedElementsAre( - ExpectCredential("example.com", "https://example.com/", - "https://example.com/.well-known/change-password", + ExpectCredential("https://example.com/.well-known/change-password", kUsername1), - ExpectCredential( - "Example App", - "https://play.google.com/store/apps/details?id=com.example.app", - "https://example.com/.well-known/change-password", kUsername2))); + ExpectCredential("https://example.com/.well-known/change-password", + kUsername2))); } // Verify that computation of weak credentials notifies observers. @@ -404,7 +387,6 @@ TEST_F(PasswordCheckDelegateTest, WeakCheckWhenUserSignedOut) { EXPECT_THAT( delegate().GetInsecureCredentials(), ElementsAre(ExpectCredential( - "example.com", "https://example.com/", "https://example.com/.well-known/change-password", kUsername1))); EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_SIGNED_OUT, delegate().GetPasswordCheckStatus().state); @@ -435,25 +417,25 @@ TEST_F(PasswordCheckDelegateTest, GetInsecureCredentialsHandlesTimes) { EXPECT_THAT( delegate().GetInsecureCredentials(), ElementsAre(ExpectCompromisedCredential( - "example.com", "https://example.com/", "https://example.com/.well-known/change-password", kUsername1, base::Seconds(59), "Just now", - {api::passwords_private::COMPROMISE_TYPE_LEAKED}), + {api::passwords_private::COMPROMISE_TYPE_LEAKED, + api::passwords_private::COMPROMISE_TYPE_REUSED}), ExpectCompromisedCredential( - "example.com", "https://example.com/", "https://example.com/.well-known/change-password", kUsername2, base::Seconds(60), "1 minute ago", - {api::passwords_private::COMPROMISE_TYPE_LEAKED}), + {api::passwords_private::COMPROMISE_TYPE_LEAKED, + api::passwords_private::COMPROMISE_TYPE_REUSED}), ExpectCompromisedCredential( - "example.org", "http://www.example.org/", "http://www.example.org/.well-known/change-password", kUsername1, base::Days(100), "3 months ago", - {api::passwords_private::COMPROMISE_TYPE_LEAKED}), + {api::passwords_private::COMPROMISE_TYPE_LEAKED, + api::passwords_private::COMPROMISE_TYPE_REUSED}), ExpectCompromisedCredential( - "example.org", "http://www.example.org/", "http://www.example.org/.well-known/change-password", kUsername2, base::Days(800), "2 years ago", - {api::passwords_private::COMPROMISE_TYPE_LEAKED}))); + {api::passwords_private::COMPROMISE_TYPE_LEAKED, + api::passwords_private::COMPROMISE_TYPE_REUSED}))); } // Verifies that both leaked and phished credentials are ordered correctly @@ -485,27 +467,27 @@ TEST_F(PasswordCheckDelegateTest, EXPECT_THAT(delegate().GetInsecureCredentials(), UnorderedElementsAre( ExpectCompromisedCredential( - "example.com", "https://example.com/", "https://example.com/.well-known/change-password", kUsername1, base::Minutes(1), "1 minute ago", {api::passwords_private::COMPROMISE_TYPE_LEAKED, - api::passwords_private::COMPROMISE_TYPE_PHISHED}), + api::passwords_private::COMPROMISE_TYPE_PHISHED, + api::passwords_private::COMPROMISE_TYPE_REUSED}), ExpectCompromisedCredential( - "example.org", "http://www.example.org/", "http://www.example.org/.well-known/change-password", kUsername1, base::Minutes(3), "3 minutes ago", - {api::passwords_private::COMPROMISE_TYPE_PHISHED}), + {api::passwords_private::COMPROMISE_TYPE_PHISHED, + api::passwords_private::COMPROMISE_TYPE_REUSED}), ExpectCompromisedCredential( - "example.org", "http://www.example.org/", "http://www.example.org/.well-known/change-password", kUsername2, base::Minutes(4), "4 minutes ago", {api::passwords_private::COMPROMISE_TYPE_LEAKED, - api::passwords_private::COMPROMISE_TYPE_PHISHED}), + api::passwords_private::COMPROMISE_TYPE_PHISHED, + api::passwords_private::COMPROMISE_TYPE_REUSED}), ExpectCompromisedCredential( - "example.com", "https://example.com/", "https://example.com/.well-known/change-password", kUsername2, base::Minutes(2), "2 minutes ago", - {api::passwords_private::COMPROMISE_TYPE_LEAKED}))); + {api::passwords_private::COMPROMISE_TYPE_LEAKED, + api::passwords_private::COMPROMISE_TYPE_REUSED}))); } TEST_F(PasswordCheckDelegateTest, GetInsecureCredentialsInjectsAndroid) { @@ -527,25 +509,22 @@ TEST_F(PasswordCheckDelegateTest, GetInsecureCredentialsInjectsAndroid) { // Verify that the compromised credentials match what is stored in the // password store. - EXPECT_THAT( - delegate().GetInsecureCredentials(), - UnorderedElementsAre( - ExpectCompromisedCredential( - "Example App", - "https://play.google.com/store/apps/details?id=com.example.app", - "https://example.com/.well-known/change-password", kUsername2, - base::Days(3), "3 days ago", - {api::passwords_private::COMPROMISE_TYPE_PHISHED}), - ExpectCompromisedCredential( - "app.example.com", - "https://play.google.com/store/apps/details?id=com.example.app", - absl::nullopt, kUsername1, base::Days(4), "4 days ago", - {api::passwords_private::COMPROMISE_TYPE_PHISHED}), - ExpectCompromisedCredential( - "example.com", "https://example.com/", - "https://example.com/.well-known/change-password", kUsername1, - base::Minutes(5), "5 minutes ago", - {api::passwords_private::COMPROMISE_TYPE_LEAKED}))); + EXPECT_THAT(delegate().GetInsecureCredentials(), + UnorderedElementsAre( + ExpectCompromisedCredential( + "https://example.com/.well-known/change-password", + kUsername2, base::Days(3), "3 days ago", + {api::passwords_private::COMPROMISE_TYPE_PHISHED, + api::passwords_private::COMPROMISE_TYPE_REUSED}), + ExpectCompromisedCredential( + absl::nullopt, kUsername1, base::Days(4), "4 days ago", + {api::passwords_private::COMPROMISE_TYPE_PHISHED, + api::passwords_private::COMPROMISE_TYPE_REUSED}), + ExpectCompromisedCredential( + "https://example.com/.well-known/change-password", + kUsername1, base::Minutes(5), "5 minutes ago", + {api::passwords_private::COMPROMISE_TYPE_LEAKED, + api::passwords_private::COMPROMISE_TYPE_REUSED}))); } // Test that a change to compromised credential notifies observers. @@ -681,68 +660,6 @@ TEST_F(PasswordCheckDelegateTest, UnmuteInsecureCredentialIdMismatch) { EXPECT_FALSE(delegate().UnmuteInsecureCredential(credential)); } -TEST_F(PasswordCheckDelegateTest, RecordChangePasswordFlowStarted) { - // Create an insecure credential. - PasswordForm form = MakeSavedPassword(kExampleCom, kUsername1); - AddIssueToForm(&form, InsecureType::kLeaked); - store().AddLogin(form); - RunUntilIdle(); - - PasswordUiEntry credential = - std::move(delegate().GetInsecureCredentials().at(0)); - ASSERT_EQ(base::UTF16ToASCII(kUsername1), credential.username); - - EXPECT_CALL( - password_change_success_tracker(), - OnManualChangePasswordFlowStarted( - GURL(*credential.change_password_url), credential.username, - PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings)); - - delegate().RecordChangePasswordFlowStarted(credential); -} - -TEST_F(PasswordCheckDelegateTest, - RecordChangePasswordFlowStartedForAppWithWebRealm) { - // Create an insecure credential. - PasswordForm form = MakeSavedAndroidPassword(kExampleApp, kUsername2, - "Example App", kExampleCom); - AddIssueToForm(&form, InsecureType::kLeaked); - store().AddLogin(form); - RunUntilIdle(); - - PasswordUiEntry credential = - std::move(delegate().GetInsecureCredentials().at(0)); - ASSERT_EQ(base::UTF16ToASCII(kUsername2), credential.username); - - EXPECT_CALL( - password_change_success_tracker(), - OnManualChangePasswordFlowStarted( - GURL(*credential.change_password_url), credential.username, - PasswordChangeSuccessTracker::EntryPoint::kLeakCheckInSettings)); - - delegate().RecordChangePasswordFlowStarted(credential); -} - -TEST_F(PasswordCheckDelegateTest, - RecordChangePasswordFlowStartedForAppWithoutWebRealm) { - // Create an insecure credential. - PasswordForm form = MakeSavedAndroidPassword(kExampleApp, kUsername1, "", ""); - AddIssueToForm(&form, InsecureType::kLeaked); - store().AddLogin(form); - RunUntilIdle(); - - PasswordUiEntry credential = - std::move(delegate().GetInsecureCredentials().at(0)); - ASSERT_EQ(base::UTF16ToASCII(kUsername1), credential.username); - - // Since no password change link exists, we expect no call to the tracker. - EXPECT_CALL(password_change_success_tracker(), - OnManualChangePasswordFlowStarted) - .Times(0); - - delegate().RecordChangePasswordFlowStarted(credential); -} - // Tests that we don't create an entry in the database if there is no matching // saved password. TEST_F(PasswordCheckDelegateTest, OnLeakFoundDoesNotCreateCredential) { @@ -916,21 +833,6 @@ TEST_F(PasswordCheckDelegateTest, GetPasswordCheckStatusCount) { EXPECT_EQ(*status.total_number_of_passwords, 2); } -// Verifies that the case where the check is canceled is reported correctly. -TEST_F(PasswordCheckDelegateTest, GetPasswordCheckStatusCanceled) { - identity_test_env().MakeAccountAvailable(kTestEmail); - store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1)); - RunUntilIdle(); - - delegate().StartPasswordCheck(); - EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_RUNNING, - delegate().GetPasswordCheckStatus().state); - - delegate().StopPasswordCheck(); - EXPECT_EQ(api::passwords_private::PASSWORD_CHECK_STATE_CANCELED, - delegate().GetPasswordCheckStatus().state); -} - // Verifies that the case where the user is offline is reported correctly. TEST_F(PasswordCheckDelegateTest, GetPasswordCheckStatusOffline) { identity_test_env().MakeAccountAvailable(kTestEmail); @@ -1085,24 +987,6 @@ TEST_F(PasswordCheckDelegateTest, OnCredentialDoneUpdatesProgress) { EXPECT_EQ(0, *status->remaining_in_queue); } -// Tests that StopPasswordCheck() invokes pending callbacks before -// initialization finishes. -TEST_F(PasswordCheckDelegateTest, - StopPasswordCheckRespondsCancelsBeforeInitialization) { - MockStartPasswordCheckCallback callback1; - MockStartPasswordCheckCallback callback2; - delegate().StartPasswordCheck(callback1.Get()); - delegate().StartPasswordCheck(callback2.Get()); - - EXPECT_CALL(callback1, Run(BulkLeakCheckService::State::kIdle)); - EXPECT_CALL(callback2, Run(BulkLeakCheckService::State::kIdle)); - delegate().StopPasswordCheck(); - - Mock::VerifyAndClearExpectations(&callback1); - Mock::VerifyAndClearExpectations(&callback2); - RunUntilIdle(); -} - // Tests that pending callbacks get invoked once initialization finishes. TEST_F(PasswordCheckDelegateTest, StartPasswordCheckRunsCallbacksAfterInitialization) { @@ -1117,7 +1001,7 @@ TEST_F(PasswordCheckDelegateTest, // Use a local delegate instead of |delegate()| so that the Password Store can // be set-up prior to constructing the object. - password_manager::MockAffiliationService affiliation_service; + password_manager::FakeAffiliationService affiliation_service; SavedPasswordsPresenter new_presenter(&affiliation_service, &store(), /*account_store=*/nullptr); PasswordCheckDelegate delegate = CreateDelegate(&new_presenter); @@ -1162,10 +1046,6 @@ TEST_F(PasswordCheckDelegateTest, WellKnownChangePasswordUrl_androidrealm) { // credentials. TEST_F(PasswordCheckDelegateTest, GetCredentialsWithReusedPasswordFillsFieldsCorrectly) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - password_manager::features::kPasswordManagerRedesign); - store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1, kWeakPassword1)); store().AddLogin(MakeSavedPassword(kExampleCom, kUsername2, kWeakPassword2)); store().AddLogin(MakeSavedAndroidPassword( @@ -1186,34 +1066,23 @@ TEST_F(PasswordCheckDelegateTest, Field(&api::passwords_private::PasswordUiEntryList::entries, UnorderedElementsAre( ExpectCredential( - "example.com", "https://example.com/", "https://example.com/.well-known/change-password", kUsername2), ExpectCredential( - "Example App", - "https://play.google.com/store/apps/" - "details?id=com.example.app", "https://example.com/.well-known/change-password", kUsername1))), Field(&api::passwords_private::PasswordUiEntryList::entries, UnorderedElementsAre( ExpectCredential( - "example.com", "https://example.com/", "https://example.com/.well-known/change-password", kUsername1), ExpectCredential( - "Example App", - "https://play.google.com/store/apps/" - "details?id=com.example.app", "https://example.com/.well-known/change-password", kUsername2))))); } TEST_F(PasswordCheckDelegateTest, GetCredentialsWithReusedPasswordAvoidsSingleReuse) { - base::test::ScopedFeatureList scoped_feature_list( - password_manager::features::kPasswordManagerRedesign); - store().AddLogin(MakeSavedPassword(kExampleCom, kUsername1, kWeakPassword1)); store().AddLogin(MakeSavedPassword(kExampleApp, kUsername2, kWeakPassword1)); RunUntilIdle(); @@ -1230,3 +1099,4 @@ TEST_F(PasswordCheckDelegateTest, } } // namespace extensions +
\ No newline at end of file diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc index 181e9e81ac8..7ee1928a92f 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc @@ -49,29 +49,6 @@ PasswordsPrivateRecordPasswordsPageAccessInSettingsFunction::Run() { return RespondNow(NoArguments()); } -// PasswordsPrivateChangeSavedPasswordFunction -ResponseAction PasswordsPrivateChangeSavedPasswordFunction::Run() { - if (!GetDelegate(browser_context())) { - return RespondNow(Error(kNoDelegateError)); - } - - auto parameters = - api::passwords_private::ChangeSavedPassword::Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(parameters); - - auto new_id = GetDelegate(browser_context()) - ->ChangeSavedPassword(parameters->id, parameters->params); - if (new_id.has_value()) { - return RespondNow(ArgumentList( - api::passwords_private::ChangeSavedPassword::Results::Create( - new_id.value()))); - } - return RespondNow(Error( - "Could not change the password. Either the password is empty, the user " - "is not authenticated or no matching password could be found for the " - "id.")); -} - // PasswordsPrivateChangeCredentialFunction ResponseAction PasswordsPrivateChangeCredentialFunction::Run() { if (!GetDelegate(browser_context())) { @@ -208,19 +185,11 @@ ResponseAction PasswordsPrivateGetSavedPasswordListFunction::Run() { return RespondNow(Error(kNoDelegateError)); } - // GetList() can immediately call GotList() (which would Respond() before - // RespondLater()). So we post a task to preserve order. - base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, - base::BindOnce(&PasswordsPrivateGetSavedPasswordListFunction::GetList, - this)); - return RespondLater(); -} - -void PasswordsPrivateGetSavedPasswordListFunction::GetList() { GetDelegate(browser_context()) ->GetSavedPasswordsList(base::BindOnce( &PasswordsPrivateGetSavedPasswordListFunction::GotList, this)); + + return did_respond() ? AlreadyResponded() : RespondLater(); } void PasswordsPrivateGetSavedPasswordListFunction::GotList( @@ -246,19 +215,11 @@ ResponseAction PasswordsPrivateGetPasswordExceptionListFunction::Run() { return RespondNow(Error(kNoDelegateError)); } - // GetList() can immediately call GotList() (which would Respond() before - // RespondLater()). So we post a task to preserve order. - base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, - base::BindOnce(&PasswordsPrivateGetPasswordExceptionListFunction::GetList, - this)); - return RespondLater(); -} - -void PasswordsPrivateGetPasswordExceptionListFunction::GetList() { GetDelegate(browser_context()) ->GetPasswordExceptionsList(base::BindOnce( &PasswordsPrivateGetPasswordExceptionListFunction::GotList, this)); + + return did_respond() ? AlreadyResponded() : RespondLater(); } void PasswordsPrivateGetPasswordExceptionListFunction::GotList( @@ -282,6 +243,45 @@ ResponseAction PasswordsPrivateMovePasswordsToAccountFunction::Run() { return RespondNow(NoArguments()); } +// PasswordsPrivateFetchFamilyMembersFunction +ResponseAction PasswordsPrivateFetchFamilyMembersFunction::Run() { + if (!GetDelegate(browser_context())) { + return RespondNow(Error(kNoDelegateError)); + } + + GetDelegate(browser_context()) + ->FetchFamilyMembers(base::BindOnce( + &PasswordsPrivateFetchFamilyMembersFunction::FamilyFetchCompleted, + this)); + + // `FamilyFetchCompleted()` might respond before we reach this point. + return did_respond() ? AlreadyResponded() : RespondLater(); +} + +// PasswordsPrivateSharePasswordFunction +ResponseAction PasswordsPrivateSharePasswordFunction::Run() { + if (!GetDelegate(browser_context())) { + return RespondNow(Error(kNoDelegateError)); + } + + // TODO(crbug/1445526): Respond with an error if arguments are not valid + // (password doesn't exist, auth validity expired, recipient doesn't have + // public key or user_id). + + auto parameters = + api::passwords_private::SharePassword::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(parameters); + GetDelegate(browser_context()) + ->SharePassword(parameters->id, parameters->recipients); + return RespondNow(NoArguments()); +} + +void PasswordsPrivateFetchFamilyMembersFunction::FamilyFetchCompleted( + const api::passwords_private::FamilyFetchResults& result) { + Respond(ArgumentList( + api::passwords_private::FetchFamilyMembers::Results::Create(result))); +} + // PasswordsPrivateImportPasswordsFunction ResponseAction PasswordsPrivateImportPasswordsFunction::Run() { if (!GetDelegate(browser_context())) { @@ -371,16 +371,6 @@ void PasswordsPrivateExportPasswordsFunction::ExportRequestCompleted( Respond(Error(error)); } -// PasswordsPrivateCancelExportPasswordsFunction -ResponseAction PasswordsPrivateCancelExportPasswordsFunction::Run() { - if (!GetDelegate(browser_context())) { - return RespondNow(Error(kNoDelegateError)); - } - - GetDelegate(browser_context())->CancelExportPasswords(); - return RespondNow(NoArguments()); -} - // PasswordsPrivateRequestExportProgressStatusFunction ResponseAction PasswordsPrivateRequestExportProgressStatusFunction::Run() { if (!GetDelegate(browser_context())) { @@ -491,25 +481,6 @@ ResponseAction PasswordsPrivateUnmuteInsecureCredentialFunction::Run() { return RespondNow(NoArguments()); } -// PasswordsPrivateRecordChangePasswordFlowStartedFunction: -PasswordsPrivateRecordChangePasswordFlowStartedFunction:: - ~PasswordsPrivateRecordChangePasswordFlowStartedFunction() = default; - -ResponseAction PasswordsPrivateRecordChangePasswordFlowStartedFunction::Run() { - if (!GetDelegate(browser_context())) { - return RespondNow(Error(kNoDelegateError)); - } - - auto parameters = - api::passwords_private::RecordChangePasswordFlowStarted::Params::Create( - args()); - EXTENSION_FUNCTION_VALIDATE(parameters); - - GetDelegate(browser_context()) - ->RecordChangePasswordFlowStarted(parameters->credential); - return RespondNow(NoArguments()); -} - // PasswordsPrivateStartPasswordCheckFunction: PasswordsPrivateStartPasswordCheckFunction:: ~PasswordsPrivateStartPasswordCheckFunction() = default; @@ -535,19 +506,6 @@ void PasswordsPrivateStartPasswordCheckFunction::OnStarted( : Error("Starting password check failed.")); } -// PasswordsPrivateStopPasswordCheckFunction: -PasswordsPrivateStopPasswordCheckFunction:: - ~PasswordsPrivateStopPasswordCheckFunction() = default; - -ResponseAction PasswordsPrivateStopPasswordCheckFunction::Run() { - if (!GetDelegate(browser_context())) { - return RespondNow(Error(kNoDelegateError)); - } - - GetDelegate(browser_context())->StopPasswordCheck(); - return RespondNow(NoArguments()); -} - // PasswordsPrivateGetPasswordCheckStatusFunction: PasswordsPrivateGetPasswordCheckStatusFunction:: ~PasswordsPrivateGetPasswordCheckStatusFunction() = default; diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h index 97f445d33f8..66ff906714b 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_api.h @@ -29,18 +29,6 @@ class PasswordsPrivateRecordPasswordsPageAccessInSettingsFunction ResponseAction Run() override; }; -class PasswordsPrivateChangeSavedPasswordFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("passwordsPrivate.changeSavedPassword", - PASSWORDSPRIVATE_CHANGESAVEDPASSWORD) - - protected: - ~PasswordsPrivateChangeSavedPasswordFunction() override = default; - - // ExtensionFunction overrides. - ResponseAction Run() override; -}; - class PasswordsPrivateChangeCredentialFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("passwordsPrivate.changeCredential", @@ -136,7 +124,6 @@ class PasswordsPrivateGetSavedPasswordListFunction : public ExtensionFunction { ResponseAction Run() override; private: - void GetList(); void GotList(const PasswordsPrivateDelegate::UiEntries& entries); }; @@ -165,7 +152,6 @@ class PasswordsPrivateGetPasswordExceptionListFunction ResponseAction Run() override; private: - void GetList(); void GotList(const PasswordsPrivateDelegate::ExceptionEntries& entries); }; @@ -182,6 +168,34 @@ class PasswordsPrivateMovePasswordsToAccountFunction ResponseAction Run() override; }; +class PasswordsPrivateFetchFamilyMembersFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("passwordsPrivate.fetchFamilyMembers", + PASSWORDSPRIVATE_FETCHFAMILYMEMBERS) + + protected: + ~PasswordsPrivateFetchFamilyMembersFunction() override = default; + + // ExtensionFunction overrides. + ResponseAction Run() override; + + private: + void FamilyFetchCompleted( + const api::passwords_private::FamilyFetchResults& results); +}; + +class PasswordsPrivateSharePasswordFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("passwordsPrivate.sharePassword", + PASSWORDSPRIVATE_SHAREPASSWORD) + + protected: + ~PasswordsPrivateSharePasswordFunction() override = default; + + // ExtensionFunction overrides. + ResponseAction Run() override; +}; + class PasswordsPrivateImportPasswordsFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("passwordsPrivate.importPasswords", @@ -240,18 +254,6 @@ class PasswordsPrivateExportPasswordsFunction : public ExtensionFunction { void ExportRequestCompleted(const std::string& error); }; -class PasswordsPrivateCancelExportPasswordsFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("passwordsPrivate.cancelExportPasswords", - PASSWORDSPRIVATE_CANCELEXPORTPASSWORDS) - - protected: - ~PasswordsPrivateCancelExportPasswordsFunction() override = default; - - // ExtensionFunction overrides. - ResponseAction Run() override; -}; - class PasswordsPrivateRequestExportProgressStatusFunction : public ExtensionFunction { public: @@ -344,19 +346,6 @@ class PasswordsPrivateUnmuteInsecureCredentialFunction ResponseAction Run() override; }; -class PasswordsPrivateRecordChangePasswordFlowStartedFunction - : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("passwordsPrivate.recordChangePasswordFlowStarted", - PASSWORDSPRIVATE_RECORDCHANGEPASSWORDFLOWSTARTED) - - protected: - ~PasswordsPrivateRecordChangePasswordFlowStartedFunction() override; - - // ExtensionFunction overrides. - ResponseAction Run() override; -}; - class PasswordsPrivateStartPasswordCheckFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("passwordsPrivate.startPasswordCheck", @@ -372,18 +361,6 @@ class PasswordsPrivateStartPasswordCheckFunction : public ExtensionFunction { void OnStarted(password_manager::BulkLeakCheckService::State state); }; -class PasswordsPrivateStopPasswordCheckFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("passwordsPrivate.stopPasswordCheck", - PASSWORDSPRIVATE_STOPPASSWORDCHECK) - - protected: - ~PasswordsPrivateStopPasswordCheckFunction() override; - - // ExtensionFunction overrides. - ResponseAction Run() override; -}; - class PasswordsPrivateGetPasswordCheckStatusFunction : public ExtensionFunction { public: diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc index 2eb6374b579..adfe50b7d8a 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc @@ -41,12 +41,7 @@ namespace { class PasswordsPrivateApiTest : public ExtensionApiTest { public: - PasswordsPrivateApiTest() { - scoped_feature_list_.InitWithFeatures( - {password_manager::features::kPasswordManagerRedesign, - password_manager::features::kPasswordsGrouping}, - {}); - } + PasswordsPrivateApiTest() = default; PasswordsPrivateApiTest(const PasswordsPrivateApiTest&) = delete; PasswordsPrivateApiTest& operator=(const PasswordsPrivateApiTest&) = delete; @@ -80,6 +75,14 @@ class PasswordsPrivateApiTest : public ExtensionApiTest { return test_delegate_->ImportPasswordsTriggered(); } + bool fetch_family_members_was_triggered() { + return test_delegate_->FetchFamilyMembersTriggered(); + } + + bool share_password_was_triggered() { + return test_delegate_->SharePasswordTriggered(); + } + bool continue_import_was_triggered() { return test_delegate_->ContinueImportTriggered(); } @@ -92,18 +95,10 @@ class PasswordsPrivateApiTest : public ExtensionApiTest { return test_delegate_->ExportPasswordsTriggered(); } - bool cancelExportPasswordsWasTriggered() { - return test_delegate_->CancelExportPasswordsTriggered(); - } - bool start_password_check_triggered() { return test_delegate_->StartPasswordCheckTriggered(); } - bool stop_password_check_triggered() { - return test_delegate_->StopPasswordCheckTriggered(); - } - void set_start_password_check_state( password_manager::BulkLeakCheckService::State state) { test_delegate_->SetStartPasswordCheckState(state); @@ -127,10 +122,6 @@ class PasswordsPrivateApiTest : public ExtensionApiTest { test_delegate_->SetIsAccountStoreDefault(is_default); } - const std::string& last_change_flow_url() { - return test_delegate_->last_change_flow_url(); - } - const std::vector<int>& last_moved_passwords() const { return test_delegate_->last_moved_passwords(); } @@ -148,7 +139,6 @@ class PasswordsPrivateApiTest : public ExtensionApiTest { } private: - base::test::ScopedFeatureList scoped_feature_list_; scoped_refptr<TestPasswordsPrivateDelegate> test_delegate_; }; @@ -187,28 +177,6 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, AddPasswordWhenOperationFails) { EXPECT_TRUE(RunPasswordsSubtest("addPasswordWhenOperationFails")) << message_; } -IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ChangeSavedPasswordSucceeds) { - EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordSucceeds")) << message_; -} - -IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, - ChangeSavedPasswordWithIncorrectIdFails) { - EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordWithIncorrectIdFails")) - << message_; -} - -IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, - ChangeSavedPasswordWithEmptyPasswordFails) { - EXPECT_TRUE(RunPasswordsSubtest("changeSavedPasswordWithEmptyPasswordFails")) - << message_; -} - -IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, - ChangeSavedPasswordWithNoteSucceeds) { - EXPECT_TRUE(RunPasswordsSubtest("ChangeSavedPasswordWithNoteSucceeds")) - << message_; -} - IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ChangeCredentialChangePassword) { EXPECT_TRUE(RunPasswordsSubtest("changeCredentialChangePassword")) @@ -267,6 +235,18 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, GetPasswordExceptionList) { EXPECT_TRUE(RunPasswordsSubtest("getPasswordExceptionList")) << message_; } +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, FetchFamilyMembers) { + EXPECT_FALSE(fetch_family_members_was_triggered()); + EXPECT_TRUE(RunPasswordsSubtest("fetchFamilyMembers")) << message_; + EXPECT_TRUE(fetch_family_members_was_triggered()); +} + +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, SharePassword) { + EXPECT_FALSE(share_password_was_triggered()); + EXPECT_TRUE(RunPasswordsSubtest("sharePassword")) << message_; + EXPECT_TRUE(share_password_was_triggered()); +} + IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ImportPasswords) { EXPECT_FALSE(importPasswordsWasTriggered()); EXPECT_TRUE(RunPasswordsSubtest("importPasswords")) << message_; @@ -291,12 +271,6 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ExportPasswords) { EXPECT_TRUE(exportPasswordsWasTriggered()); } -IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, CancelExportPasswords) { - EXPECT_FALSE(cancelExportPasswordsWasTriggered()); - EXPECT_TRUE(RunPasswordsSubtest("cancelExportPasswords")) << message_; - EXPECT_TRUE(cancelExportPasswordsWasTriggered()); -} - IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, RequestExportProgressStatus) { EXPECT_TRUE(RunPasswordsSubtest("requestExportProgressStatus")) << message_; } @@ -339,21 +313,6 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, UnmuteInsecureCredentialFails) { EXPECT_TRUE(RunPasswordsSubtest("unmuteInsecureCredentialFails")) << message_; } -IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, - RecordChangePasswordFlowStarted) { - EXPECT_TRUE(RunPasswordsSubtest("recordChangePasswordFlowStarted")) - << message_; - EXPECT_EQ(last_change_flow_url(), - "https://example.com/.well-known/change-password"); -} - -IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, - RecordChangePasswordFlowStartedAppNoUrl) { - EXPECT_TRUE(RunPasswordsSubtest("recordChangePasswordFlowStartedAppNoUrl")) - << message_; - EXPECT_EQ(last_change_flow_url(), ""); -} - IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, StartPasswordCheck) { set_start_password_check_state( password_manager::BulkLeakCheckService::State::kRunning); @@ -370,12 +329,6 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, StartPasswordCheckFailed) { EXPECT_TRUE(start_password_check_triggered()); } -IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, StopPasswordCheck) { - EXPECT_FALSE(stop_password_check_triggered()); - EXPECT_TRUE(RunPasswordsSubtest("stopPasswordCheck")) << message_; - EXPECT_TRUE(stop_password_check_triggered()); -} - IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, GetPasswordCheckStatus) { EXPECT_TRUE(RunPasswordsSubtest("getPasswordCheckStatus")) << message_; } diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h index 99c902c270c..5eced39ce00 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h @@ -38,6 +38,11 @@ class PasswordsPrivateDelegate using ImportResultsCallback = base::OnceCallback<void(const api::passwords_private::ImportResults&)>; + using FetchFamilyResultsCallback = base::OnceCallback<void( + const api::passwords_private::FamilyFetchResults&)>; + + using ShareRecipients = std::vector<api::passwords_private::RecipientInfo>; + using PlaintextPasswordCallback = base::OnceCallback<void(absl::optional<std::u16string>)>; @@ -87,15 +92,6 @@ class PasswordsPrivateDelegate bool use_account_store, content::WebContents* web_contents) = 0; - // Changes the username and password corresponding to |ids|. - // |ids|: The ids for the password entries being updated. - // |params|: The struct which holds the new username, password and note. - // Returns the ids if the change was successful (can be the same ids if the - // username and the password didn't change), nullopt otherwise. - virtual absl::optional<int> ChangeSavedPassword( - int id, - const api::passwords_private::ChangeSavedPasswordParams& params) = 0; - // Updates a credential. Not all attributes can be updated. // |credential|: The credential to be updated. Matched to an existing // credential by id. @@ -154,6 +150,15 @@ class PasswordsPrivateDelegate virtual void MovePasswordsToAccount(const std::vector<int>& ids, content::WebContents* web_contents) = 0; + // Fetches family members of the current user for the password sharing flow. + // |callback|: Used to communicate the status of a request to fetch family + // members, as well as the data returned in the response. + virtual void FetchFamilyMembers(FetchFamilyResultsCallback callback) = 0; + + // Sends sharing invitations for a credential with given |id| to the + // |recipients|. + virtual void SharePassword(int id, const ShareRecipients& recipients) = 0; + // Trigger the password import procedure, allowing the user to select a file // containing passwords to import. // |to_store|: destination store (Device or Account) for imported passwords. @@ -186,9 +191,6 @@ class PasswordsPrivateDelegate base::OnceCallback<void(const std::string&)> callback, content::WebContents* web_contents) = 0; - // Cancel any ongoing export. - virtual void CancelExportPasswords() = 0; - // Get the most recent progress status. virtual api::passwords_private::ExportProgressStatus GetExportProgressStatus() = 0; @@ -225,15 +227,9 @@ class PasswordsPrivateDelegate virtual bool UnmuteInsecureCredential( const api::passwords_private::PasswordUiEntry& credential) = 0; - // Records that a change password flow was started for |credential|. - virtual void RecordChangePasswordFlowStarted( - const api::passwords_private::PasswordUiEntry& credential) = 0; - // Requests to start a check for insecure passwords. Invokes |callback| // once a check is running or the request was stopped via StopPasswordCheck(). virtual void StartPasswordCheck(StartPasswordCheckCallback callback) = 0; - // Stops a check for insecure passwords. - virtual void StopPasswordCheck() = 0; // Returns the current status of the password check. virtual api::passwords_private::PasswordCheckStatus diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc index 8543dddf70a..399c04f1457 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc @@ -23,36 +23,23 @@ using content::BrowserContext; PasswordsPrivateDelegateProxy::PasswordsPrivateDelegateProxy( BrowserContext* browser_context) - : browser_context_(browser_context) { - if (base::FeatureList::IsEnabled( - password_manager::features::kPasswordManagerRedesign)) { - return; - } - scoped_instance_ = base::MakeRefCounted<PasswordsPrivateDelegateImpl>( - static_cast<Profile*>(browser_context_)); -} + : browser_context_(browser_context) {} PasswordsPrivateDelegateProxy::PasswordsPrivateDelegateProxy( BrowserContext* browser_context, scoped_refptr<PasswordsPrivateDelegate> delegate) - : browser_context_(browser_context), scoped_instance_(std::move(delegate)) { - weak_instance_ = scoped_instance_->AsWeakPtr(); + : browser_context_(browser_context) { + weak_instance_ = delegate->AsWeakPtr(); } PasswordsPrivateDelegateProxy::~PasswordsPrivateDelegateProxy() = default; void PasswordsPrivateDelegateProxy::Shutdown() { browser_context_ = nullptr; weak_instance_ = nullptr; - scoped_instance_ = nullptr; } scoped_refptr<PasswordsPrivateDelegate> PasswordsPrivateDelegateProxy::GetOrCreateDelegate() { - if (!base::FeatureList::IsEnabled( - password_manager::features::kPasswordManagerRedesign)) { - return scoped_instance_; - } - if (weak_instance_) { return scoped_refptr<PasswordsPrivateDelegate>(weak_instance_.get()); } @@ -66,11 +53,7 @@ PasswordsPrivateDelegateProxy::GetOrCreateDelegate() { scoped_refptr<PasswordsPrivateDelegate> PasswordsPrivateDelegateProxy::GetDelegate() { - if (base::FeatureList::IsEnabled( - password_manager::features::kPasswordManagerRedesign)) { - return scoped_refptr<PasswordsPrivateDelegate>(weak_instance_.get()); - } - return scoped_instance_; + return scoped_refptr<PasswordsPrivateDelegate>(weak_instance_.get()); } // static @@ -109,9 +92,10 @@ PasswordsPrivateDelegateFactory::PasswordsPrivateDelegateFactory() PasswordsPrivateDelegateFactory::~PasswordsPrivateDelegateFactory() = default; -KeyedService* PasswordsPrivateDelegateFactory::BuildServiceInstanceFor( +std::unique_ptr<KeyedService> +PasswordsPrivateDelegateFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const { - return new PasswordsPrivateDelegateProxy(profile); + return std::make_unique<PasswordsPrivateDelegateProxy>(profile); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h index 2303b8ea9db..49e0e0f9056 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h @@ -44,9 +44,6 @@ class PasswordsPrivateDelegateProxy : public KeyedService { raw_ptr<content::BrowserContext> browser_context_ = nullptr; base::WeakPtr<PasswordsPrivateDelegate> weak_instance_; - // TODO(crbug.com/1412348): Remove this after the feature is enabled by - // default. - scoped_refptr<PasswordsPrivateDelegate> scoped_instance_; }; // Factory for creating PasswordPrivateDelegates. @@ -65,7 +62,7 @@ class PasswordsPrivateDelegateFactory : public ProfileKeyedServiceFactory { ~PasswordsPrivateDelegateFactory() override; // BrowserContextKeyedServiceFactory implementation. - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const override; }; diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc index 5c87121acf7..b1a28a5a678 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc @@ -23,6 +23,7 @@ #include "chrome/browser/password_manager/account_password_store_factory.h" #include "chrome/browser/password_manager/affiliation_service_factory.h" #include "chrome/browser/password_manager/chrome_password_manager_client.h" +#include "chrome/browser/password_manager/password_sender_service_factory.h" #include "chrome/browser/password_manager/password_store_factory.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/profiles/profile.h" @@ -40,16 +41,20 @@ #include "chrome/browser/web_applications/web_app_install_params.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/webauthn/passkey_model_factory.h" +#include "chrome/common/channel_info.h" #include "chrome/common/extensions/api/passwords_private.h" #include "chrome/grit/generated_resources.h" #include "chromeos/constants/chromeos_features.h" #include "components/keyed_service/core/service_access_type.h" #include "components/password_manager/core/browser/affiliation/affiliation_utils.h" +#include "components/password_manager/core/browser/features/password_features.h" #include "components/password_manager/core/browser/password_access_authenticator.h" #include "components/password_manager/core/browser/password_form.h" #include "components/password_manager/core/browser/password_manager_features_util.h" #include "components/password_manager/core/browser/password_manager_util.h" #include "components/password_manager/core/browser/password_sync_util.h" +#include "components/password_manager/core/browser/sharing/password_sender_service.h" +#include "components/password_manager/core/browser/sharing/recipients_fetcher_impl.h" #include "components/password_manager/core/browser/ui/credential_ui_entry.h" #include "components/password_manager/core/common/password_manager_features.h" #include "components/prefs/pref_service.h" @@ -58,6 +63,7 @@ #include "components/sync/service/sync_service.h" #include "components/url_formatter/elide_url.h" #include "content/public/browser/navigation_handle.h" +#include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" @@ -86,6 +92,7 @@ namespace { using password_manager::CredentialFacet; using password_manager::CredentialUIEntry; +using password_manager::FetchFamilyMembersRequestStatus; // The error message returned to the UI when Chrome refuses to start multiple // exports. @@ -417,35 +424,6 @@ bool PasswordsPrivateDelegateImpl::AddPassword( return success; } -absl::optional<int> PasswordsPrivateDelegateImpl::ChangeSavedPassword( - int id, - const api::passwords_private::ChangeSavedPasswordParams& params) { - const CredentialUIEntry* original_credential = - credential_id_generator_.TryGetKey(id); - if (!original_credential) { - return absl::nullopt; - } - - CredentialUIEntry updated_credential = *original_credential; - updated_credential.username = base::UTF8ToUTF16(params.username); - updated_credential.password = base::UTF8ToUTF16(params.password); - if (params.note) { - updated_credential.note = base::UTF8ToUTF16(*params.note); - } - switch (saved_passwords_presenter_.EditSavedCredentials(*original_credential, - updated_credential)) { - case password_manager::SavedPasswordsPresenter::EditResult::kSuccess: - case password_manager::SavedPasswordsPresenter::EditResult::kNothingChanged: - break; - case password_manager::SavedPasswordsPresenter::EditResult::kNotFound: - case password_manager::SavedPasswordsPresenter::EditResult::kAlreadyExisits: - case password_manager::SavedPasswordsPresenter::EditResult::kEmptyPassword: - return absl::nullopt; - } - - return credential_id_generator_.GenerateId(std::move(updated_credential)); -} - bool PasswordsPrivateDelegateImpl::ChangeCredential( const api::passwords_private::PasswordUiEntry& credential) { const CredentialUIEntry* original_credential = @@ -581,6 +559,49 @@ void PasswordsPrivateDelegateImpl::OsReauthCall( #endif } +void PasswordsPrivateDelegateImpl::OnFetchingFamilyMembersCompleted( + FetchFamilyResultsCallback callback, + std::vector<password_manager::RecipientInfo> family_members, + FetchFamilyMembersRequestStatus request_status) { + api::passwords_private::FamilyFetchResults results; + switch (request_status) { + case FetchFamilyMembersRequestStatus::kUnknown: + case FetchFamilyMembersRequestStatus::kNetworkError: + case FetchFamilyMembersRequestStatus::kPendingRequest: + results.status = api::passwords_private::FamilyFetchStatus:: + FAMILY_FETCH_STATUS_UNKNOWN_ERROR; + break; + case FetchFamilyMembersRequestStatus::kSuccess: + results.status = api::passwords_private::FamilyFetchStatus:: + FAMILY_FETCH_STATUS_SUCCESS; + break; + case FetchFamilyMembersRequestStatus::kNoFamily: + results.status = api::passwords_private::FamilyFetchStatus:: + FAMILY_FETCH_STATUS_NO_MEMBERS; + } + if (request_status == FetchFamilyMembersRequestStatus::kSuccess) { + for (const password_manager::RecipientInfo& family_member : + family_members) { + api::passwords_private::RecipientInfo recipient_info; + recipient_info.user_id = family_member.user_id; + recipient_info.email = family_member.email; + recipient_info.display_name = family_member.user_name; + recipient_info.profile_image_url = family_member.profile_image_url; + + if (!family_member.public_key.key.empty()) { + recipient_info.is_eligible = true; + api::passwords_private::PublicKey public_key; + public_key.value = family_member.public_key.key; + public_key.version = family_member.public_key.key_version; + recipient_info.public_key = std::move(public_key); + } + + results.family_members.push_back(std::move(recipient_info)); + } + } + std::move(callback).Run(results); +} + void PasswordsPrivateDelegateImpl::OsReauthTimeoutCall() { #if !BUILDFLAG(IS_LINUX) PasswordsPrivateEventRouter* router = @@ -611,17 +632,14 @@ void PasswordsPrivateDelegateImpl::SetCredentials( CreatePasswordUiEntryFromCredentialUiEntry(std::move(credential))); } } - if (base::FeatureList::IsEnabled( - password_manager::features::kPasswordsGrouping)) { - for (CredentialUIEntry& credential : - saved_passwords_presenter_.GetBlockedSites()) { - api::passwords_private::ExceptionEntry current_exception_entry; - current_exception_entry.urls = - CreateUrlCollectionFromCredential(credential); - current_exception_entry.id = - credential_id_generator_.GenerateId(std::move(credential)); - current_exceptions_.push_back(std::move(current_exception_entry)); - } + for (CredentialUIEntry& credential : + saved_passwords_presenter_.GetBlockedSites()) { + api::passwords_private::ExceptionEntry current_exception_entry; + current_exception_entry.urls = + CreateUrlCollectionFromCredential(credential); + current_exception_entry.id = + credential_id_generator_.GenerateId(std::move(credential)); + current_exceptions_.push_back(std::move(current_exception_entry)); } if (current_entries_initialized_) { @@ -677,6 +695,49 @@ void PasswordsPrivateDelegateImpl::MovePasswordsToAccount( kExplicitlyTriggeredForMultiplePasswordsInSettings); } +void PasswordsPrivateDelegateImpl::FetchFamilyMembers( + FetchFamilyResultsCallback callback) { + if (!sharing_password_recipients_fetcher_) { + sharing_password_recipients_fetcher_ = + std::make_unique<password_manager::RecipientsFetcherImpl>( + chrome::GetChannel(), + profile_->GetDefaultStoragePartition() + ->GetURLLoaderFactoryForBrowserProcess(), + IdentityManagerFactory::GetForProfile(profile_)); + } + sharing_password_recipients_fetcher_->FetchFamilyMembers(base::BindOnce( + &PasswordsPrivateDelegateImpl::OnFetchingFamilyMembersCompleted, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + +void PasswordsPrivateDelegateImpl::SharePassword( + int id, + const ShareRecipients& recipients) { + const CredentialUIEntry* entry = credential_id_generator_.TryGetKey(id); + if (!entry) { + return; + } + + std::vector<password_manager::PasswordForm> corresponding_credentials = + saved_passwords_presenter_.GetCorrespondingPasswordForms(*entry); + if (corresponding_credentials.empty()) { + return; + } + + password_manager::PasswordSenderService* password_sender_service = + PasswordSenderServiceFactory::GetForProfile(profile_); + for (const api::passwords_private::RecipientInfo& recipient_info : + recipients) { + CHECK(recipient_info.public_key.has_value()); + password_manager::PublicKey public_key; + public_key.key = recipient_info.public_key.value().value; + public_key.key_version = recipient_info.public_key.value().version; + password_sender_service->SendPasswords( + corresponding_credentials, {.user_id = recipient_info.user_id, + .public_key = std::move(public_key)}); + } +} + void PasswordsPrivateDelegateImpl::ImportPasswords( api::passwords_private::PasswordStoreSet to_store, ImportResultsCallback results_callback, @@ -742,10 +803,6 @@ void PasswordsPrivateDelegateImpl::ExportPasswords( std::move(accepted_callback), web_contents)); } -void PasswordsPrivateDelegateImpl::CancelExportPasswords() { - password_manager_porter_->CancelExport(); -} - api::passwords_private::ExportProgressStatus PasswordsPrivateDelegateImpl::GetExportProgressStatus() { return ConvertStatus(password_manager_porter_->GetExportProgressStatus()); @@ -795,20 +852,11 @@ bool PasswordsPrivateDelegateImpl::UnmuteInsecureCredential( return password_check_delegate_.UnmuteInsecureCredential(credential); } -void PasswordsPrivateDelegateImpl::RecordChangePasswordFlowStarted( - const api::passwords_private::PasswordUiEntry& credential) { - password_check_delegate_.RecordChangePasswordFlowStarted(credential); -} - void PasswordsPrivateDelegateImpl::StartPasswordCheck( StartPasswordCheckCallback callback) { password_check_delegate_.StartPasswordCheck(std::move(callback)); } -void PasswordsPrivateDelegateImpl::StopPasswordCheck() { - password_check_delegate_.StopPasswordCheck(); -} - api::passwords_private::PasswordCheckStatus PasswordsPrivateDelegateImpl::GetPasswordCheckStatus() { return password_check_delegate_.GetPasswordCheckStatus(); @@ -1082,43 +1130,32 @@ api::passwords_private::PasswordUiEntry PasswordsPrivateDelegateImpl::CreatePasswordUiEntryFromCredentialUiEntry( CredentialUIEntry credential) { api::passwords_private::PasswordUiEntry entry; - if (base::FeatureList::IsEnabled( - password_manager::features::kPasswordsGrouping)) { - entry.affiliated_domains = - std::vector<api::passwords_private::DomainInfo>(); - base::ranges::transform( - credential.GetAffiliatedDomains(), - std::back_inserter(entry.affiliated_domains.value()), - [](const CredentialUIEntry::DomainInfo& domain) { - api::passwords_private::DomainInfo domainInfo; - domainInfo.name = domain.name; - domainInfo.url = domain.url.spec(); - domainInfo.signon_realm = domain.signon_realm; - return domainInfo; - }); - } + base::ranges::transform(credential.GetAffiliatedDomains(), + std::back_inserter(entry.affiliated_domains), + [](const CredentialUIEntry::DomainInfo& domain) { + api::passwords_private::DomainInfo domain_info; + domain_info.name = domain.name; + domain_info.url = domain.url.spec(); + domain_info.signon_realm = domain.signon_realm; + return domain_info; + }); entry.is_passkey = !credential.passkey_credential_id.empty(); - entry.urls = extensions::CreateUrlCollectionFromCredential(credential); entry.username = base::UTF16ToUTF8(credential.username); if (entry.is_passkey) { entry.display_name = base::UTF16ToUTF8(credential.user_display_name); } entry.stored_in = extensions::StoreSetFromCredential(credential); - entry.is_android_credential = password_manager::IsValidAndroidFacetURI( - credential.GetFirstSignonRealm()); if (!credential.federation_origin.opaque()) { std::u16string formatted_origin = url_formatter::FormatOriginForSecurityDisplay( credential.federation_origin, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC); - if (base::FeatureList::IsEnabled( - password_manager::features::kPasswordsGrouping)) { - entry.federation_text = base::UTF16ToUTF8(formatted_origin); - } else { - entry.federation_text = l10n_util::GetStringFUTF8( - IDS_PASSWORDS_VIA_FEDERATION, formatted_origin); - } + entry.federation_text = base::UTF16ToUTF8(formatted_origin); + } + absl::optional<GURL> change_password_url = credential.GetChangePasswordURL(); + if (change_password_url.has_value()) { + entry.change_password_url = change_password_url->spec(); } entry.id = credential_id_generator_.GenerateId(std::move(credential)); return entry; diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h index c58c40cc112..fa365a225a6 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h @@ -28,6 +28,7 @@ #include "components/password_manager/core/browser/password_access_authenticator.h" #include "components/password_manager/core/browser/password_account_storage_settings_watcher.h" #include "components/password_manager/core/browser/reauth_purpose.h" +#include "components/password_manager/core/browser/sharing/recipients_fetcher.h" #include "components/password_manager/core/browser/ui/credential_ui_entry.h" #include "components/password_manager/core/browser/ui/export_progress_status.h" #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h" @@ -44,6 +45,10 @@ namespace web_app { class WebAppInstallManager; } +namespace password_manager { +class RecipientsFetcher; +} + namespace extensions { // Concrete PasswordsPrivateDelegate implementation. @@ -71,9 +76,6 @@ class PasswordsPrivateDelegateImpl const std::u16string& note, bool use_account_store, content::WebContents* web_contents) override; - absl::optional<int> ChangeSavedPassword( - int id, - const api::passwords_private::ChangeSavedPasswordParams& params) override; bool ChangeCredential( const api::passwords_private::PasswordUiEntry& credential) override; void RemoveCredential( @@ -90,6 +92,8 @@ class PasswordsPrivateDelegateImpl content::WebContents* web_contents) override; void MovePasswordsToAccount(const std::vector<int>& ids, content::WebContents* web_contents) override; + void FetchFamilyMembers(FetchFamilyResultsCallback callback) override; + void SharePassword(int id, const ShareRecipients& recipients) override; void ImportPasswords(api::passwords_private::PasswordStoreSet to_store, ImportResultsCallback results_callback, content::WebContents* web_contents) override; @@ -100,7 +104,6 @@ class PasswordsPrivateDelegateImpl void ExportPasswords( base::OnceCallback<void(const std::string&)> accepted_callback, content::WebContents* web_contents) override; - void CancelExportPasswords() override; api::passwords_private::ExportProgressStatus GetExportProgressStatus() override; bool IsOptedInForAccountStorage() override; @@ -115,10 +118,7 @@ class PasswordsPrivateDelegateImpl const api::passwords_private::PasswordUiEntry& credential) override; bool UnmuteInsecureCredential( const api::passwords_private::PasswordUiEntry& credential) override; - void RecordChangePasswordFlowStarted( - const api::passwords_private::PasswordUiEntry& credential) override; void StartPasswordCheck(StartPasswordCheckCallback callback) override; - void StopPasswordCheck() override; api::passwords_private::PasswordCheckStatus GetPasswordCheckStatus() override; password_manager::InsecureCredentialsManager* GetInsecureCredentialsManager() override; @@ -147,6 +147,14 @@ class PasswordsPrivateDelegateImpl std::unique_ptr<PasswordManagerPorterInterface> porter) { password_manager_porter_ = std::move(porter); } + + void SetRecipientsFetcherForTesting( + std::unique_ptr<password_manager::RecipientsFetcher> + sharing_password_recipients_fetcher) { + sharing_password_recipients_fetcher_ = + std::move(sharing_password_recipients_fetcher); + } + #endif // defined(UNIT_TEST) private: @@ -215,6 +223,11 @@ class PasswordsPrivateDelegateImpl password_manager::PasswordAccessAuthenticator::AuthResultCallback callback); + void OnFetchingFamilyMembersCompleted( + FetchFamilyResultsCallback callback, + std::vector<password_manager::RecipientInfo> recipients_info, + password_manager::FetchFamilyMembersRequestStatus request_status); + // Records user action and emits histogram values for retrieving |entry|. void EmitHistogramsForCredentialAccess( const password_manager::CredentialUIEntry& entry, @@ -284,6 +297,9 @@ class PasswordsPrivateDelegateImpl web_app::WebAppInstallManagerObserver> install_manager_observation_{this}; + std::unique_ptr<password_manager::RecipientsFetcher> + sharing_password_recipients_fetcher_; + base::WeakPtrFactory<PasswordsPrivateDelegateImpl> weak_ptr_factory_{this}; }; diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc index 50bb05d5bd1..424682ae852 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc @@ -10,7 +10,9 @@ #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" +#include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" +#include "base/rand_util.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "base/test/bind.h" @@ -28,7 +30,9 @@ #include "chrome/browser/password_manager/affiliation_service_factory.h" #include "chrome/browser/password_manager/chrome_password_manager_client.h" #include "chrome/browser/password_manager/password_manager_test_util.h" +#include "chrome/browser/password_manager/password_sender_service_factory.h" #include "chrome/browser/sync/sync_service_factory.h" +#include "chrome/browser/ui/autofill/chrome_autofill_client.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" @@ -48,6 +52,7 @@ #include "components/keyed_service/core/keyed_service.h" #include "components/password_manager/content/browser/password_manager_log_router_factory.h" #include "components/password_manager/core/browser/affiliation/fake_affiliation_service.h" +#include "components/password_manager/core/browser/features/password_features.h" #include "components/password_manager/core/browser/insecure_credentials_table.h" #include "components/password_manager/core/browser/mock_password_feature_manager.h" #include "components/password_manager/core/browser/password_form.h" @@ -55,11 +60,16 @@ #include "components/password_manager/core/browser/password_manager_metrics_util.h" #include "components/password_manager/core/browser/password_manager_test_utils.h" #include "components/password_manager/core/browser/reauth_purpose.h" +#include "components/password_manager/core/browser/sharing/mock_password_sender_service.h" +#include "components/password_manager/core/browser/sharing/password_sharing_recipients_downloader.h" +#include "components/password_manager/core/browser/sharing/recipients_fetcher_impl.h" #include "components/password_manager/core/browser/test_password_store.h" #include "components/password_manager/core/browser/ui/import_results.h" #include "components/password_manager/core/common/password_manager_features.h" #include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" #include "components/sync/base/features.h" +#include "components/sync/protocol/password_sharing_recipients.pb.h" #include "components/sync/test/test_sync_service.h" #include "components/webauthn/core/browser/test_passkey_model.h" #include "content/public/browser/browser_context.h" @@ -80,12 +90,21 @@ using MockReauthCallback = base::MockCallback< password_manager::PasswordAccessAuthenticator::ReauthCallback>; +using extensions::api::passwords_private::FamilyFetchResults; +using extensions::api::passwords_private::RecipientInfo; +using password_manager::PasswordForm; +using password_manager::PasswordRecipient; using password_manager::ReauthPurpose; using password_manager::TestPasswordStore; using ::testing::_; +using ::testing::AllOf; +using ::testing::ElementsAre; using ::testing::Eq; +using ::testing::Field; +using ::testing::IsEmpty; using ::testing::IsNull; using ::testing::Ne; +using ::testing::Optional; using ::testing::Return; using ::testing::SizeIs; using ::testing::StrictMock; @@ -94,6 +113,16 @@ namespace extensions { namespace { constexpr char kHistogramName[] = "PasswordManager.AccessPasswordInSettings"; +constexpr char kSharingRecipientId1[] = "user id 1"; +constexpr char kSharingRecipientKeyValue1[] = "key 1"; +constexpr char kSharingRecipientKeyValue2[] = "key 2"; +constexpr char kSharingRecipientId2[] = "user id 2"; +constexpr char kSharingRecipientDisplayName1[] = "User One"; +constexpr char kSharingRecipientDisplayName2[] = "User Two"; +constexpr char kSharingRecipientEmail1[] = "user1@example.com"; +constexpr char kSharingRecipientEmail2[] = "user2@example.com"; +constexpr char kSharingRecipientProfileImageUrl1[] = "image1.example.com"; +constexpr char kSharingRecipientProfileImageUrl2[] = "image2.example.com"; using MockPlaintextPasswordCallback = base::MockCallback<PasswordsPrivateDelegate::PlaintextPasswordCallback>; @@ -111,7 +140,7 @@ class MockPasswordManagerPorter : public PasswordManagerPorterInterface { MOCK_METHOD(void, Import, (content::WebContents * web_contents, - password_manager::PasswordForm::Store to_store, + PasswordForm::Store to_store, ImportResultsCallback results_callback), (override)); MOCK_METHOD(void, @@ -133,7 +162,7 @@ class FakePasswordManagerPorter : public PasswordManagerPorterInterface { } void Import(content::WebContents* web_contents, - password_manager::PasswordForm::Store to_store, + PasswordForm::Store to_store, ImportResultsCallback results_callback) override { password_manager::ImportResults results; results.status = import_results_status_; @@ -199,7 +228,7 @@ class MockPasswordManagerClient : public ChromePasswordManagerClient { private: explicit MockPasswordManagerClient(content::WebContents* web_contents) - : ChromePasswordManagerClient(web_contents, nullptr) {} + : ChromePasswordManagerClient(web_contents) {} password_manager::MockPasswordFeatureManager mock_password_feature_manager_; scoped_refptr<device_reauth::MockDeviceAuthenticator> @@ -289,11 +318,10 @@ std::unique_ptr<KeyedService> BuildPasswordsPrivateEventRouter( PasswordsPrivateEventRouter::Create(context)); } -password_manager::PasswordForm CreateSampleForm( - password_manager::PasswordForm::Store store = - password_manager::PasswordForm::Store::kProfileStore, +PasswordForm CreateSampleForm( + PasswordForm::Store store = PasswordForm::Store::kProfileStore, const std::u16string& username = u"test@gmail.com") { - password_manager::PasswordForm form; + PasswordForm form; form.signon_realm = "https://abc1.com"; form.url = GURL("https://abc1.com"); form.username_value = username; @@ -315,19 +343,19 @@ sync_pb::WebauthnCredentialSpecifics CreatePasskey() { MATCHER_P(PasswordUiEntryDataEquals, expected, "") { return testing::Value(expected.get().is_passkey, arg.is_passkey) && - testing::Value(expected.get().urls.link, arg.urls.link) && + testing::Value(expected.get().affiliated_domains[0].signon_realm, + arg.affiliated_domains[0].signon_realm) && testing::Value(expected.get().username, arg.username) && testing::Value(expected.get().display_name, arg.display_name) && - testing::Value(expected.get().stored_in, arg.stored_in) && - testing::Value(expected.get().is_android_credential, - arg.is_android_credential); + testing::Value(expected.get().stored_in, arg.stored_in); } } // namespace class PasswordsPrivateDelegateImplTest : public WebAppTest { public: - PasswordsPrivateDelegateImplTest() = default; + PasswordsPrivateDelegateImplTest() + : WebAppTest(WebAppTest::WithTestUrlLoaderFactory()) {} PasswordsPrivateDelegateImplTest(const PasswordsPrivateDelegateImplTest&) = delete; @@ -339,7 +367,7 @@ class PasswordsPrivateDelegateImplTest : public WebAppTest { void SetUp() override; // Sets up a testing password store and fills it with |forms|. - void SetUpPasswordStores(std::vector<password_manager::PasswordForm> forms); + void SetUpPasswordStores(std::vector<PasswordForm> forms); // Sets up a testing EventRouter with a production // PasswordsPrivateEventRouter. @@ -355,6 +383,16 @@ class PasswordsPrivateDelegateImplTest : public WebAppTest { PasswordsPrivateDelegate::UiEntries GetCredentials( PasswordsPrivateDelegate& delegate); + // Returns a test `WebContents` with an initialized Autofill client, which is + // needed for PasswordManager client to work properly. + std::unique_ptr<content::WebContents> CreateWebContents() { + std::unique_ptr<content::WebContents> web_contents = + content::WebContentsTester::CreateTestWebContents(profile(), + /*instance=*/nullptr); + autofill::ChromeAutofillClient::CreateForWebContents(web_contents.get()); + return web_contents; + } + protected: raw_ptr<extensions::TestEventRouter, DanglingUntriaged> event_router_ = nullptr; @@ -391,11 +429,17 @@ void PasswordsPrivateDelegateImplTest::SetUp() { [](content::BrowserContext*) -> std::unique_ptr<KeyedService> { return std::make_unique<webauthn::TestPasskeyModel>(); })); + + PasswordSenderServiceFactory::GetInstance()->SetTestingFactoryAndUse( + profile(), base::BindRepeating([](content::BrowserContext*) + -> std::unique_ptr<KeyedService> { + return std::make_unique<password_manager::MockPasswordSenderService>(); + })); } void PasswordsPrivateDelegateImplTest::SetUpPasswordStores( - std::vector<password_manager::PasswordForm> forms) { - for (const password_manager::PasswordForm& form : forms) { + std::vector<PasswordForm> forms) { + for (const PasswordForm& form : forms) { if (form.IsUsingAccountStore()) account_store_->AddLogin(form); else if (form.IsUsingProfileStore()) @@ -450,10 +494,10 @@ TEST_F(PasswordsPrivateDelegateImplTest, PasswordsDuplicatedInStoresAreRepresentedAsSingleEntity) { auto delegate = CreateDelegate(); - password_manager::PasswordForm account_password = - CreateSampleForm(password_manager::PasswordForm::Store::kAccountStore); - password_manager::PasswordForm profile_password = - CreateSampleForm(password_manager::PasswordForm::Store::kProfileStore); + PasswordForm account_password = + CreateSampleForm(PasswordForm::Store::kAccountStore); + PasswordForm profile_password = + CreateSampleForm(PasswordForm::Store::kProfileStore); SetUpPasswordStores({account_password, profile_password}); @@ -485,16 +529,14 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetPasswordExceptionsList) { TEST_F(PasswordsPrivateDelegateImplTest, ExceptionsDuplicatedInStoresAreRepresentedAsSingleEntity) { auto delegate = CreateDelegate(); - password_manager::PasswordForm account_exception; + PasswordForm account_exception; account_exception.blocked_by_user = true; account_exception.url = GURL("https://test.com"); - account_exception.in_store = - password_manager::PasswordForm::Store::kAccountStore; - password_manager::PasswordForm profile_exception; + account_exception.in_store = PasswordForm::Store::kAccountStore; + PasswordForm profile_exception; profile_exception.url = GURL("https://test.com"); profile_exception.blocked_by_user = true; - profile_exception.in_store = - password_manager::PasswordForm::Store::kProfileStore; + profile_exception.in_store = PasswordForm::Store::kProfileStore; SetUpPasswordStores({account_exception, profile_exception}); @@ -506,8 +548,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, } TEST_F(PasswordsPrivateDelegateImplTest, AddPassword) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage) @@ -537,13 +578,17 @@ TEST_F(PasswordsPrivateDelegateImplTest, AddPassword) { // Check that adding passwords got reflected in the passwords list. api::passwords_private::PasswordUiEntry expected_entry1; - expected_entry1.urls.link = "https://example1.com/"; + expected_entry1.affiliated_domains.emplace_back(); + expected_entry1.affiliated_domains.back().signon_realm = + "https://example1.com/"; expected_entry1.username = "username1"; expected_entry1.note.emplace(); expected_entry1.stored_in = api::passwords_private::PASSWORD_STORE_SET_ACCOUNT; api::passwords_private::PasswordUiEntry expected_entry2; - expected_entry2.urls.link = "http://example2.com/login"; + expected_entry2.affiliated_domains.emplace_back(); + expected_entry2.affiliated_domains.back().signon_realm = + "http://example2.com/"; expected_entry2.username = ""; expected_entry2.note = "note"; expected_entry2.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; @@ -555,8 +600,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, AddPassword) { } TEST_F(PasswordsPrivateDelegateImplTest, AddPasswordUpdatesDefaultStore) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); auto delegate = CreateDelegate(); @@ -574,8 +618,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, AddPasswordUpdatesDefaultStore) { ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage) .WillByDefault(Return(true)); EXPECT_CALL(*(client->GetPasswordFeatureManager()), - SetDefaultPasswordStore( - password_manager::PasswordForm::Store::kAccountStore)); + SetDefaultPasswordStore(PasswordForm::Store::kAccountStore)); EXPECT_TRUE( delegate->AddPassword("example2.com", u"username2", u"password2", u"", /*use_account_store=*/true, web_contents.get())); @@ -590,8 +633,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, AddPasswordUpdatesDefaultStore) { TEST_F(PasswordsPrivateDelegateImplTest, ImportPasswordsDoesNotUpdateDefaultStore) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); auto delegate = CreateDelegate(); @@ -613,8 +655,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, } TEST_F(PasswordsPrivateDelegateImplTest, ImportPasswordsUpdatesDefaultStore) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); auto delegate = CreateDelegate(); @@ -628,8 +669,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, ImportPasswordsUpdatesDefaultStore) { ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage) .WillByDefault(Return(true)); EXPECT_CALL(*(client->GetPasswordFeatureManager()), - SetDefaultPasswordStore( - password_manager::PasswordForm::Store::kAccountStore)); + SetDefaultPasswordStore(PasswordForm::Store::kAccountStore)); EXPECT_CALL(*mock_porter_ptr, Import).Times(1); delegate->ImportPasswords( api::passwords_private::PasswordStoreSet::PASSWORD_STORE_SET_ACCOUNT, @@ -638,9 +678,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, ImportPasswordsUpdatesDefaultStore) { TEST_F(PasswordsPrivateDelegateImplTest, ImportPasswordsLogsImportResultsStatus) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), - /*instance=*/nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); auto delegate = CreateDelegate(); @@ -672,9 +710,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, } TEST_F(PasswordsPrivateDelegateImplTest, TestReauthFailedOnImport) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), - /*instance=*/nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); @@ -717,9 +753,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestReauthFailedOnImport) { TEST_F(PasswordsPrivateDelegateImplTest, ContinueImportLogsImportResultsStatus) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), - /*instance=*/nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); scoped_refptr<PasswordsPrivateDelegateImpl> delegate = CreateDelegate(); @@ -760,120 +794,8 @@ TEST_F(PasswordsPrivateDelegateImplTest, ResetImporter) { delegate->ResetImporter(/*delete_file=*/false); } -TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPassword) { - password_manager::PasswordForm sample_form = CreateSampleForm(); - SetUpPasswordStores({sample_form}); - auto delegate = CreateDelegate(); - // Spin the loop to allow PasswordStore tasks posted on the creation of - // |delegate| to be completed. - base::RunLoop().RunUntilIdle(); - - // Double check that the contents of the passwords list matches our - // expectation. - base::MockCallback<PasswordsPrivateDelegate::UiEntriesCallback> callback; - EXPECT_CALL(callback, Run(SizeIs(1))) - .WillOnce([&](const PasswordsPrivateDelegate::UiEntries& passwords) { - EXPECT_EQ(sample_form.username_value, - base::UTF8ToUTF16(passwords[0].username)); - }); - delegate->GetSavedPasswordsList(callback.Get()); - int sample_form_id = delegate->GetIdForCredential( - password_manager::CredentialUIEntry(sample_form)); - - api::passwords_private::ChangeSavedPasswordParams params; - params.password = "new_pass"; - params.username = "new_user"; - params.note = "new note"; - - sample_form.username_value = u"new_user"; - sample_form.password_value = u"new_pass"; - int new_form_id = delegate->GetIdForCredential( - password_manager::CredentialUIEntry(sample_form)); - - auto result = delegate->ChangeSavedPassword(sample_form_id, params); - EXPECT_EQ(result, new_form_id); - - // Spin the loop to allow PasswordStore tasks posted when changing the - // password to be completed. - base::RunLoop().RunUntilIdle(); - - // Check that the changing the password got reflected in the passwords list. - // `note` field should not be filled when `GetSavedPasswordsList` is called. - EXPECT_CALL(callback, Run(SizeIs(1))) - .WillOnce([](const PasswordsPrivateDelegate::UiEntries& passwords) { - EXPECT_THAT(passwords[0].username, Eq("new_user")); - EXPECT_THAT(passwords[0].note, Eq(absl::nullopt)); - }); - delegate->GetSavedPasswordsList(callback.Get()); -} - -TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPasswordInBothStores) { - password_manager::PasswordForm profile_form = CreateSampleForm(); - password_manager::PasswordForm account_form = profile_form; - account_form.in_store = password_manager::PasswordForm::Store::kAccountStore; - SetUpPasswordStores({profile_form, account_form}); - - auto delegate = CreateDelegate(); - // Spin the loop to allow PasswordStore tasks posted on the creation of - // |delegate| to be completed. - base::RunLoop().RunUntilIdle(); - - int profile_form_id = delegate->GetIdForCredential( - password_manager::CredentialUIEntry(profile_form)); - int account_form_id = delegate->GetIdForCredential( - password_manager::CredentialUIEntry(account_form)); - - ASSERT_EQ(profile_form_id, account_form_id); - - api::passwords_private::ChangeSavedPasswordParams params; - params.password = "new_pass"; - params.username = "new_user"; - - profile_form.username_value = u"new_user"; - profile_form.password_value = u"new_pass"; - int new_profile_form_id = delegate->GetIdForCredential( - password_manager::CredentialUIEntry(profile_form)); - account_form.username_value = u"new_user"; - account_form.password_value = u"new_pass"; - int new_account_form_id = delegate->GetIdForCredential( - password_manager::CredentialUIEntry(account_form)); - - ASSERT_EQ(new_profile_form_id, new_account_form_id); - - EXPECT_EQ(new_profile_form_id, - delegate->ChangeSavedPassword(profile_form_id, params)); -} - -TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPasswordInAccountStore) { - password_manager::PasswordForm profile_form = CreateSampleForm(); - profile_form.password_value = u"different_pass"; - password_manager::PasswordForm account_form = CreateSampleForm(); - account_form.in_store = password_manager::PasswordForm::Store::kAccountStore; - SetUpPasswordStores({profile_form, account_form}); - - auto delegate = CreateDelegate(); - // Spin the loop to allow PasswordStore tasks posted on the creation of - // |delegate| to be completed. - base::RunLoop().RunUntilIdle(); - - int account_form_id = delegate->GetIdForCredential( - password_manager::CredentialUIEntry(account_form)); - - api::passwords_private::ChangeSavedPasswordParams params; - params.password = "new_pass"; - params.username = "new_user"; - - account_form.username_value = u"new_user"; - account_form.password_value = u"new_pass"; - int new_account_form_id = delegate->GetIdForCredential( - password_manager::CredentialUIEntry(account_form)); - - auto result = delegate->ChangeSavedPassword(account_form_id, params); - EXPECT_THAT(result, new_account_form_id); -} - TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_Password) { - password_manager::PasswordForm sample_form = CreateSampleForm(); + PasswordForm sample_form = CreateSampleForm(); SetUpPasswordStores({sample_form}); auto delegate = CreateDelegate(); // Spin the loop to allow PasswordStore tasks posted on the creation of @@ -904,9 +826,9 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_Password) { TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_PasswordInBothStores) { - password_manager::PasswordForm profile_form = CreateSampleForm(); - password_manager::PasswordForm account_form = profile_form; - account_form.in_store = password_manager::PasswordForm::Store::kAccountStore; + PasswordForm profile_form = CreateSampleForm(); + PasswordForm account_form = profile_form; + account_form.in_store = PasswordForm::Store::kAccountStore; SetUpPasswordStores({profile_form, account_form}); auto delegate = CreateDelegate(); @@ -938,10 +860,10 @@ TEST_F(PasswordsPrivateDelegateImplTest, TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_PasswordInAccountStore) { - password_manager::PasswordForm profile_form = CreateSampleForm(); + PasswordForm profile_form = CreateSampleForm(); profile_form.password_value = u"different_pass"; - password_manager::PasswordForm account_form = CreateSampleForm(); - account_form.in_store = password_manager::PasswordForm::Store::kAccountStore; + PasswordForm account_form = CreateSampleForm(); + account_form.in_store = PasswordForm::Store::kAccountStore; SetUpPasswordStores({profile_form, account_form}); auto delegate = CreateDelegate(); @@ -988,8 +910,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_Passkey) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitWithFeatures( - {password_manager::features::kPasswordsGrouping, - password_manager::features::kPasswordManagerPasskeys, + {password_manager::features::kPasswordManagerPasskeys, syncer::kSyncWebauthnCredentials}, /*disabled_features=*/{}); @@ -1042,7 +963,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_NotFound) { } TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_EmptyPassword) { - password_manager::PasswordForm sample_form = CreateSampleForm(); + PasswordForm sample_form = CreateSampleForm(); SetUpPasswordStores({sample_form}); auto delegate = CreateDelegate(); // Spin the loop to allow PasswordStore tasks posted on the creation of @@ -1060,7 +981,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, ChangeCredential_EmptyPassword) { // Checking callback result of RequestPlaintextPassword with reason Copy. // By implementation for Copy, callback will receive empty string. TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResult) { - password_manager::PasswordForm form = CreateSampleForm(); + PasswordForm form = CreateSampleForm(); SetUpPasswordStores({form}); auto delegate = CreateDelegate(); @@ -1091,8 +1012,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestCopyPasswordCallbackResult) { } TEST_F(PasswordsPrivateDelegateImplTest, TestShouldReauthForOptIn) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage) @@ -1108,8 +1028,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestShouldReauthForOptIn) { TEST_F(PasswordsPrivateDelegateImplTest, TestShouldNotReauthForOptOutAndShouldSetPref) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); password_manager::MockPasswordFeatureManager* feature_manager = @@ -1188,7 +1107,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestPassedReauthOnView) { TEST_F(PasswordsPrivateDelegateImplTest, TestPassedReauthOnRequestCredentialsDetails) { - password_manager::PasswordForm sample_form = CreateSampleForm(); + PasswordForm sample_form = CreateSampleForm(); sample_form.notes.emplace_back(u"best note ever", /*date_created=*/base::Time::Now()); SetUpPasswordStores({sample_form}); @@ -1347,8 +1266,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, } TEST_F(PasswordsPrivateDelegateImplTest, IsAccountStoreDefault) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage) @@ -1357,26 +1275,24 @@ TEST_F(PasswordsPrivateDelegateImplTest, IsAccountStoreDefault) { auto delegate = CreateDelegate(); EXPECT_CALL(*(client->GetPasswordFeatureManager()), GetDefaultPasswordStore) - .WillOnce(Return(password_manager::PasswordForm::Store::kAccountStore)); + .WillOnce(Return(PasswordForm::Store::kAccountStore)); EXPECT_TRUE(delegate->IsAccountStoreDefault(web_contents.get())); EXPECT_CALL(*(client->GetPasswordFeatureManager()), GetDefaultPasswordStore) - .WillOnce(Return(password_manager::PasswordForm::Store::kProfileStore)); + .WillOnce(Return(PasswordForm::Store::kProfileStore)); EXPECT_FALSE(delegate->IsAccountStoreDefault(web_contents.get())); } TEST_F(PasswordsPrivateDelegateImplTest, TestMovePasswordsToAccountStore) { - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage) .WillByDefault(Return(true)); auto delegate = CreateDelegate(); - password_manager::PasswordForm form1 = - CreateSampleForm(password_manager::PasswordForm::Store::kProfileStore); - password_manager::PasswordForm form2 = form1; + PasswordForm form1 = CreateSampleForm(PasswordForm::Store::kProfileStore); + PasswordForm form2 = form1; form2.username_value = u"different_username"; SetUpPasswordStores({form1, form2}); @@ -1396,28 +1312,6 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestMovePasswordsToAccountStore) { 1); } -TEST_F(PasswordsPrivateDelegateImplTest, AndroidCredential) { - auto delegate = CreateDelegate(); - - password_manager::PasswordForm android_form; - android_form.signon_realm = "android://hash@example.com"; - android_form.username_value = u"test@gmail.com"; - android_form.in_store = password_manager::PasswordForm::Store::kProfileStore; - SetUpPasswordStores({android_form}); - - base::MockCallback<PasswordsPrivateDelegate::UiEntriesCallback> callback; - - api::passwords_private::PasswordUiEntry expected_entry; - expected_entry.urls.link = - "https://play.google.com/store/apps/details?id=example.com"; - expected_entry.username = "test@gmail.com"; - expected_entry.is_android_credential = true; - expected_entry.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; - EXPECT_CALL(callback, Run(testing::ElementsAre(PasswordUiEntryDataEquals( - testing::ByRef(expected_entry))))); - delegate->GetSavedPasswordsList(callback.Get()); -} - TEST_F(PasswordsPrivateDelegateImplTest, VerifyCastingOfImportEntryStatus) { static_assert( static_cast<int>(api::passwords_private::ImportEntryStatus:: @@ -1552,8 +1446,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature( password_manager::features::kBiometricAuthenticationForFilling); - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); client->SetDeviceAuthenticator(biometric_authenticator_); @@ -1574,8 +1467,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature( password_manager::features::kBiometricAuthenticationForFilling); - std::unique_ptr<content::WebContents> web_contents = - content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + std::unique_ptr<content::WebContents> web_contents = CreateWebContents(); auto* client = MockPasswordManagerClient::CreateForWebContentsAndGet(web_contents.get()); client->SetDeviceAuthenticator(biometric_authenticator_); @@ -1641,16 +1533,12 @@ TEST_F(PasswordsPrivateDelegateImplTest, DISABLED_ShowAddShortcutDialog) { } TEST_F(PasswordsPrivateDelegateImplTest, GetCredentialGroups) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - password_manager::features::kPasswordsGrouping); - auto delegate = CreateDelegate(); - password_manager::PasswordForm password1 = CreateSampleForm( - password_manager::PasswordForm::Store::kProfileStore, u"username1"); - password_manager::PasswordForm password2 = CreateSampleForm( - password_manager::PasswordForm::Store::kProfileStore, u"username2"); + PasswordForm password1 = + CreateSampleForm(PasswordForm::Store::kProfileStore, u"username1"); + PasswordForm password2 = + CreateSampleForm(PasswordForm::Store::kProfileStore, u"username2"); SetUpPasswordStores({password1, password2}); @@ -1661,11 +1549,13 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetCredentialGroups) { EXPECT_EQ("https://abc1.com/favicon.ico", groups[0].icon_url); api::passwords_private::PasswordUiEntry expected_entry1; - expected_entry1.urls.link = "https://abc1.com/"; + expected_entry1.affiliated_domains.emplace_back(); + expected_entry1.affiliated_domains.back().signon_realm = "https://abc1.com"; expected_entry1.username = "username1"; expected_entry1.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; api::passwords_private::PasswordUiEntry expected_entry2; - expected_entry2.urls.link = "https://abc1.com/"; + expected_entry2.affiliated_domains.emplace_back(); + expected_entry2.affiliated_domains.back().signon_realm = "https://abc1.com"; expected_entry2.username = "username2"; expected_entry2.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; EXPECT_THAT(groups[0].entries, @@ -1693,8 +1583,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, PasswordManagerAppInstalled) { TEST_F(PasswordsPrivateDelegateImplTest, GetPasskeyInGroups) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitWithFeatures( - {password_manager::features::kPasswordsGrouping, - password_manager::features::kPasswordManagerPasskeys, + {password_manager::features::kPasswordManagerPasskeys, syncer::kSyncWebauthnCredentials}, /*disabled_features=*/{}); @@ -1707,8 +1596,8 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetPasskeyInGroups) { sync_pb::WebauthnCredentialSpecifics passkey = CreatePasskey(); passkey_model->AddNewPasskeyForTesting(passkey); - password_manager::PasswordForm password = CreateSampleForm( - password_manager::PasswordForm::Store::kProfileStore, u"username1"); + PasswordForm password = + CreateSampleForm(PasswordForm::Store::kProfileStore, u"username1"); SetUpPasswordStores({password}); auto groups = delegate->GetCredentialGroups(); @@ -1718,12 +1607,14 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetPasskeyInGroups) { EXPECT_EQ("https://abc1.com/favicon.ico", groups[0].icon_url); api::passwords_private::PasswordUiEntry expected_entry1; - expected_entry1.urls.link = "https://abc1.com/"; + expected_entry1.affiliated_domains.emplace_back(); + expected_entry1.affiliated_domains.back().signon_realm = "https://abc1.com"; expected_entry1.username = "username1"; expected_entry1.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; api::passwords_private::PasswordUiEntry expected_entry2; expected_entry2.is_passkey = true; - expected_entry2.urls.link = "https://abc1.com/"; + expected_entry2.affiliated_domains.emplace_back(); + expected_entry2.affiliated_domains.back().signon_realm = "https://abc1.com"; expected_entry2.username = passkey.user_name(); expected_entry2.display_name = passkey.user_display_name(); expected_entry2.stored_in = @@ -1738,8 +1629,7 @@ TEST_F(PasswordsPrivateDelegateImplTest, RemovePasskey) { base::UserActionTester user_action_tester; base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitWithFeatures( - {password_manager::features::kPasswordsGrouping, - password_manager::features::kPasswordManagerPasskeys, + {password_manager::features::kPasswordManagerPasskeys, syncer::kSyncWebauthnCredentials}, /*disabled_features=*/{}); @@ -1774,4 +1664,352 @@ TEST_F(PasswordsPrivateDelegateImplTest, RemovePasskey) { 1); } +TEST_F(PasswordsPrivateDelegateImplTest, SharePasswordWithTwoRecipients) { + auto delegate = CreateDelegate(); + PasswordForm password = CreateSampleForm(); + SetUpPasswordStores({password}); + + PasswordsPrivateDelegate::ShareRecipients recipients; + api::passwords_private::RecipientInfo recipient1; + api::passwords_private::PublicKey public_key1; + public_key1.value = kSharingRecipientKeyValue1; + recipient1.public_key = std::move(public_key1); + recipient1.user_id = kSharingRecipientId1; + recipient1.display_name = kSharingRecipientDisplayName1; + recipient1.email = kSharingRecipientEmail1; + recipient1.profile_image_url = kSharingRecipientProfileImageUrl1; + recipients.push_back(std::move(recipient1)); + + api::passwords_private::RecipientInfo recipient2; + api::passwords_private::PublicKey public_key2; + public_key2.value = kSharingRecipientKeyValue2; + recipient2.public_key = std::move(public_key2); + recipient2.user_id = kSharingRecipientId2; + recipient2.display_name = kSharingRecipientDisplayName2; + recipient2.email = kSharingRecipientEmail2; + recipient2.profile_image_url = kSharingRecipientProfileImageUrl2; + recipients.push_back(std::move(recipient2)); + + password_manager::MockPasswordSenderService* password_sender_service = + static_cast<password_manager::MockPasswordSenderService*>( + PasswordSenderServiceFactory::GetForProfile(profile())); + + password_manager::PublicKey expected_public_key1, expected_public_key2; + expected_public_key1.key = kSharingRecipientKeyValue1; + expected_public_key2.key = kSharingRecipientKeyValue2; + // There are two recipients and hence, SendPasswords() should be called twice + // with the same credentials for each recipient. + EXPECT_CALL( + *password_sender_service, + SendPasswords( + ElementsAre(AllOf( + Field(&PasswordForm::username_value, password.username_value), + Field(&PasswordForm::password_value, password.password_value), + Field(&PasswordForm::signon_realm, password.signon_realm))), + AllOf(Field("user id", &PasswordRecipient::user_id, + kSharingRecipientId1), + Field("public key", &PasswordRecipient::public_key, + expected_public_key1)))); + EXPECT_CALL( + *password_sender_service, + SendPasswords( + ElementsAre(AllOf( + Field(&PasswordForm::username_value, password.username_value), + Field(&PasswordForm::password_value, password.password_value), + Field(&PasswordForm::signon_realm, password.signon_realm))), + AllOf(Field("user id", &PasswordRecipient::user_id, + kSharingRecipientId2), + Field("public key", &PasswordRecipient::public_key, + expected_public_key2))) + + ); + + delegate->SharePassword(/*id=*/0, recipients); +} + +TEST_F(PasswordsPrivateDelegateImplTest, + ShareAllPasswordsRepresentedByUiEntry) { + auto delegate = CreateDelegate(); + // `password1` and `password2` share the same username and password and their + // origins are PSL matches. They should be represented by the same ui entry. + // `password3` has a different username and hence is represented by a + // different ui entry. + PasswordForm password1 = + CreateSampleForm(PasswordForm::Store::kProfileStore, u"username1"); + password1.signon_realm = "https://facebook.com"; + password1.url = GURL("https://facebook.com"); + + PasswordForm password2 = password1; + password2.signon_realm = "https://m.facebook.com"; + password2.url = GURL("https://m.facebook.com"); + + PasswordForm password3 = + CreateSampleForm(PasswordForm::Store::kProfileStore, u"username3"); + + SetUpPasswordStores({password1, password2, password3}); + + // Credentials should have been grouped in two groups. + PasswordsPrivateDelegate::CredentialsGroups groups = + delegate->GetCredentialGroups(); + ASSERT_EQ(groups.size(), 2U); + // Find the id of the ui entry that represents both facebook.com and + // m.facebook.com + int id_with_two_affiliated_domains = -1; + for (const api::passwords_private::CredentialGroup& group : groups) { + for (const api::passwords_private::PasswordUiEntry& entry : group.entries) { + if (entry.affiliated_domains.size() == 2) { + id_with_two_affiliated_domains = entry.id; + break; + } + } + } + ASSERT_NE(-1, id_with_two_affiliated_domains); + + PasswordsPrivateDelegate::ShareRecipients recipients; + api::passwords_private::RecipientInfo recipient; + api::passwords_private::PublicKey public_key; + public_key.value = kSharingRecipientKeyValue1; + recipient.public_key = std::move(public_key); + recipient.user_id = kSharingRecipientId1; + recipient.display_name = kSharingRecipientDisplayName1; + recipient.email = kSharingRecipientEmail1; + recipient.profile_image_url = kSharingRecipientProfileImageUrl1; + recipients.push_back(std::move(recipient)); + + password_manager::MockPasswordSenderService* password_sender_service = + static_cast<password_manager::MockPasswordSenderService*>( + PasswordSenderServiceFactory::GetForProfile(profile())); + + password_manager::PublicKey expected_public_key; + expected_public_key.key = kSharingRecipientKeyValue1; + // There is one recipient and hence, SendPasswords() should be called only + // once with the two credentials represented by this ui entry. + EXPECT_CALL( + *password_sender_service, + SendPasswords( + UnorderedElementsAre( + Field(&PasswordForm::signon_realm, "https://facebook.com"), + Field(&PasswordForm::signon_realm, "https://m.facebook.com")), + AllOf(Field("user id", &PasswordRecipient::user_id, + kSharingRecipientId1), + Field("public key", &PasswordRecipient::public_key, + expected_public_key)))) + .Times(1); + + delegate->SharePassword(/*id=*/id_with_two_affiliated_domains, recipients); +} + +TEST_F(PasswordsPrivateDelegateImplTest, ShareNonExistentPassword) { + auto delegate = CreateDelegate(); + + PasswordsPrivateDelegate::ShareRecipients recipients; + api::passwords_private::RecipientInfo recipient; + recipient.user_id = kSharingRecipientId1; + recipients.push_back(std::move(recipient)); + + password_manager::MockPasswordSenderService* password_sender_service = + static_cast<password_manager::MockPasswordSenderService*>( + PasswordSenderServiceFactory::GetForProfile(profile())); + EXPECT_CALL(*password_sender_service, SendPasswords).Times(0); + + delegate->SharePassword(/*id=*/100, recipients); +} + +class PasswordsPrivateDelegateImplFetchFamilyMembersTest + : public PasswordsPrivateDelegateImplTest { + public: + PasswordsPrivateDelegateImplFetchFamilyMembersTest() = default; + + void SetUp() override { + PasswordsPrivateDelegateImplTest::SetUp(); + delegate_ = CreateDelegate(); + delegate_->SetRecipientsFetcherForTesting( + std::make_unique<password_manager::RecipientsFetcherImpl>( + version_info::Channel::DEFAULT, + profile_url_loader_factory().GetSafeWeakWrapper(), + identity_test_env_.identity_manager())); + identity_test_env_.MakePrimaryAccountAvailable("test@email.com", + signin::ConsentLevel::kSync); + identity_test_env_.SetAutomaticIssueOfAccessTokens(true); + } + + void TearDown() override { + delegate_ = nullptr; + PasswordsPrivateDelegateImplTest::TearDown(); + } + + protected: + const std::string kTestUserId = "12345"; + const std::string kTestUserName = "Theo Tester"; + const std::string kTestEmail = "theo@example.com"; + const std::string kTestProfileImageUrl = + "https://3837fjsdjaka.image.example.com"; + const std::string kTestPublicKeyBase64 = + "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MTI="; + const uint32_t kTestPublicKeyVersion = 42; + + void SetServerResponse(sync_pb::PasswordSharingRecipientsResponse:: + PasswordSharingRecipientsResult result, + net::HttpStatusCode status = net::HTTP_OK, + bool recipient_has_public_key = false) { + sync_pb::PasswordSharingRecipientsResponse response; + response.set_result(result); + if (result == sync_pb::PasswordSharingRecipientsResponse::SUCCESS) { + sync_pb::UserInfo* user_info = response.add_recipients(); + user_info->set_user_id(kTestUserId); + user_info->mutable_user_display_info()->set_display_name(kTestUserName); + user_info->mutable_user_display_info()->set_email(kTestEmail); + user_info->mutable_user_display_info()->set_profile_image_url( + kTestProfileImageUrl); + if (recipient_has_public_key) { + const password_manager::PublicKey kTestPublicKey = { + kTestPublicKeyBase64, kTestPublicKeyVersion}; + user_info->mutable_cross_user_sharing_public_key()->CopyFrom( + kTestPublicKey.ToProto()); + } + } + profile_url_loader_factory().AddResponse( + password_manager::PasswordSharingRecipientsDownloader:: + GetPasswordSharingRecipientsURL(version_info::Channel::DEFAULT) + .spec(), + response.SerializeAsString(), status); + } + + PasswordsPrivateDelegateImpl* delegate() { return delegate_.get(); } + + private: + signin::IdentityTestEnvironment identity_test_env_; + scoped_refptr<PasswordsPrivateDelegateImpl> delegate_; +}; + +TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest, + FetchFamilyMembersSucceedsWithoutPublicKey) { + SetServerResponse(sync_pb::PasswordSharingRecipientsResponse::SUCCESS); + + base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback> + callback; + EXPECT_CALL( + callback, + Run(AllOf(Field(&FamilyFetchResults::status, + api::passwords_private::FAMILY_FETCH_STATUS_SUCCESS), + Field(&FamilyFetchResults::family_members, + ElementsAre(AllOf( + Field(&RecipientInfo::user_id, kTestUserId), + Field(&RecipientInfo::display_name, kTestUserName), + Field(&RecipientInfo::email, kTestEmail), + Field(&RecipientInfo::is_eligible, false), + Field(&RecipientInfo::public_key, Eq(absl::nullopt)), + Field(&RecipientInfo::profile_image_url, + kTestProfileImageUrl))))))); + + delegate()->FetchFamilyMembers(callback.Get()); + task_environment()->RunUntilIdle(); +} + +TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest, + FetchFamilyMembersSucceedsWithPublicKey) { + SetServerResponse(sync_pb::PasswordSharingRecipientsResponse::SUCCESS, + net::HTTP_OK, /*recipient_has_public_key=*/true); + + base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback> + callback; + EXPECT_CALL( + callback, + Run(AllOf( + Field(&FamilyFetchResults::status, + api::passwords_private::FAMILY_FETCH_STATUS_SUCCESS), + Field(&FamilyFetchResults::family_members, + ElementsAre(AllOf( + Field(&RecipientInfo::user_id, kTestUserId), + Field(&RecipientInfo::display_name, kTestUserName), + Field(&RecipientInfo::email, kTestEmail), + Field(&RecipientInfo::is_eligible, true), + Field(&RecipientInfo::public_key, + Optional(AllOf( + Field(&api::passwords_private::PublicKey::value, + kTestPublicKeyBase64), + Field(&api::passwords_private::PublicKey::version, + kTestPublicKeyVersion)))), + Field(&RecipientInfo::profile_image_url, + kTestProfileImageUrl))))))); + + delegate()->FetchFamilyMembers(callback.Get()); + task_environment()->RunUntilIdle(); +} + +TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest, + FetchFamilyMembersFailsWithUnknownError) { + SetServerResponse(sync_pb::PasswordSharingRecipientsResponse::UNKNOWN); + + base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback> + callback; + EXPECT_CALL( + callback, + Run(AllOf( + Field(&FamilyFetchResults::status, + api::passwords_private::FAMILY_FETCH_STATUS_UNKNOWN_ERROR), + Field(&FamilyFetchResults::family_members, IsEmpty())))); + + delegate()->FetchFamilyMembers(callback.Get()); + task_environment()->RunUntilIdle(); +} + +TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest, + FetchFamilyMembersFailsWithNoFamilyMembersError) { + SetServerResponse( + sync_pb::PasswordSharingRecipientsResponse::NOT_FAMILY_MEMBER); + + base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback> + callback; + EXPECT_CALL( + callback, + Run(AllOf(Field(&FamilyFetchResults::status, + api::passwords_private::FAMILY_FETCH_STATUS_NO_MEMBERS), + Field(&FamilyFetchResults::family_members, IsEmpty())))); + + delegate()->FetchFamilyMembers(callback.Get()); + task_environment()->RunUntilIdle(); +} + +TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest, + FetchFamilyMembersFailsWithAnotherRequestInFlight) { + base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback> + callback1; + delegate()->FetchFamilyMembers(callback1.Get()); + + base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback> + callback2; + EXPECT_CALL( + callback2, + Run(AllOf( + Field(&FamilyFetchResults::status, + api::passwords_private::FAMILY_FETCH_STATUS_UNKNOWN_ERROR), + Field(&FamilyFetchResults::family_members, IsEmpty())))); + delegate()->FetchFamilyMembers(callback2.Get()); + + task_environment()->RunUntilIdle(); +} + +TEST_F(PasswordsPrivateDelegateImplFetchFamilyMembersTest, + FetchFamilyMembersFailsWithNetworkError) { + profile_url_loader_factory().AddResponse( + password_manager::PasswordSharingRecipientsDownloader:: + GetPasswordSharingRecipientsURL(version_info::Channel::DEFAULT) + .spec(), + /*content=*/std::string(), net::HTTP_INTERNAL_SERVER_ERROR); + + base::MockCallback<PasswordsPrivateDelegate::FetchFamilyResultsCallback> + callback; + FamilyFetchResults family_fetch_results; + EXPECT_CALL( + callback, + Run(AllOf( + Field(&FamilyFetchResults::status, + api::passwords_private::FAMILY_FETCH_STATUS_UNKNOWN_ERROR), + Field(&FamilyFetchResults::family_members, IsEmpty())))); + + delegate()->FetchFamilyMembers(callback.Get()); + task_environment()->RunUntilIdle(); +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h index e5dd4d1906a..c6914c2d038 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils.h @@ -7,7 +7,6 @@ #include <functional> #include <string> -#include <unordered_map> #include "base/containers/flat_map.h" #include "chrome/common/extensions/api/passwords_private.h" @@ -67,10 +66,9 @@ class IdGenerator { private: // Maps credential key to id. - std::unordered_map<std::string, int> key_to_id_; + base::flat_map<std::string, int> key_to_id_; // Maps id to the credential. - std::unordered_map<int, password_manager::CredentialUIEntry> - id_to_credential_; + base::flat_map<int, password_manager::CredentialUIEntry> id_to_credential_; int next_id_ = 0; }; diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc index 4f0a24376f8..5e23d6b4fea 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_chromeos.cc @@ -28,7 +28,8 @@ bool IsOsReauthAllowedAsh(Profile* profile, ash::ProfileHelper::Get()->GetUserByProfile(profile)->GetAccountId()); if (user_cannot_manually_enter_password) return true; - + // TODO (b/238606050): This code branch does not seem to be used now. + // Clean up the code, or add token as a parameter to this method. ash::quick_unlock::QuickUnlockStorage* quick_unlock_storage = ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile); const ash::quick_unlock::AuthToken* auth_token = diff --git a/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc b/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc index 5921a127e35..3963eba8ef7 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.cc @@ -26,9 +26,13 @@ constexpr size_t kNumMocks = 3; api::passwords_private::PasswordUiEntry CreateEntry(int id) { api::passwords_private::PasswordUiEntry entry; - entry.urls.shown = "test" + base::NumberToString(id) + ".com"; - entry.urls.signon_realm = "http://" + entry.urls.shown + "/login"; - entry.urls.link = entry.urls.signon_realm; + entry.affiliated_domains.emplace_back(); + entry.affiliated_domains.back().name = + "test" + base::NumberToString(id) + ".com"; + entry.affiliated_domains.back().signon_realm = + "http://" + entry.affiliated_domains.back().name + "/login"; + entry.affiliated_domains.back().url = + entry.affiliated_domains.back().signon_realm; entry.username = "testName" + base::NumberToString(id); entry.id = id; entry.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; @@ -105,19 +109,6 @@ bool TestPasswordsPrivateDelegate::AddPassword( return !url.empty() && !password.empty(); } -absl::optional<int> TestPasswordsPrivateDelegate::ChangeSavedPassword( - const int id, - const api::passwords_private::ChangeSavedPasswordParams& params) { - if (static_cast<size_t>(id) >= current_entries_.size()) { - return absl::nullopt; - } - - if (params.password.empty()) - return absl::nullopt; - - return id; -} - bool TestPasswordsPrivateDelegate::ChangeCredential( const api::passwords_private::PasswordUiEntry& credential) { const auto existing = std::ranges::find_if( @@ -233,6 +224,21 @@ void TestPasswordsPrivateDelegate::ContinueImport( std::move(results_callback).Run(import_results_); } +void TestPasswordsPrivateDelegate::FetchFamilyMembers( + FetchFamilyResultsCallback callback) { + fetch_family_members_triggered_ = true; + + family_fetch_results_.status = + api::passwords_private::FamilyFetchStatus::FAMILY_FETCH_STATUS_SUCCESS; + std::move(callback).Run(family_fetch_results_); +} + +void TestPasswordsPrivateDelegate::SharePassword( + int id, + const ShareRecipients& recipients) { + share_password_triggered_ = true; +} + void TestPasswordsPrivateDelegate::ResetImporter(bool delete_file) { reset_importer_triggered_ = true; } @@ -246,10 +252,6 @@ void TestPasswordsPrivateDelegate::ExportPasswords( std::move(callback).Run(std::string()); } -void TestPasswordsPrivateDelegate::CancelExportPasswords() { - cancel_export_passwords_triggered_ = true; -} - api::passwords_private::ExportProgressStatus TestPasswordsPrivateDelegate::GetExportProgressStatus() { // The testing of password exporting itself should be handled via @@ -272,10 +274,11 @@ std::vector<api::passwords_private::PasswordUiEntry> TestPasswordsPrivateDelegate::GetInsecureCredentials() { api::passwords_private::PasswordUiEntry leaked_credential; leaked_credential.username = "alice"; - leaked_credential.urls.shown = "example.com"; - leaked_credential.urls.link = "https://example.com"; - leaked_credential.urls.signon_realm = "https://example.com"; - leaked_credential.is_android_credential = false; + leaked_credential.affiliated_domains.emplace_back(); + leaked_credential.affiliated_domains.back().name = "example.com"; + leaked_credential.affiliated_domains.back().url = "https://example.com"; + leaked_credential.affiliated_domains.back().signon_realm = + "https://example.com"; leaked_credential.change_password_url = "https://example.com/change-password"; leaked_credential.compromised_info.emplace(); // Mar 03 2020 12:00:00 UTC @@ -290,9 +293,9 @@ TestPasswordsPrivateDelegate::GetInsecureCredentials() { api::passwords_private::PasswordUiEntry weak_credential; weak_credential.username = "bob"; - weak_credential.urls.shown = "example.com"; - weak_credential.urls.link = "https://example.com"; - weak_credential.is_android_credential = false; + weak_credential.affiliated_domains.emplace_back(); + weak_credential.affiliated_domains.back().name = "example.com"; + weak_credential.affiliated_domains.back().url = "https://example.com"; weak_credential.change_password_url = "https://example.com/change-password"; weak_credential.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; weak_credential.compromised_info.emplace(); @@ -311,9 +314,9 @@ TestPasswordsPrivateDelegate::GetCredentialsWithReusedPassword() { api::passwords_private::PasswordUiEntry credential_1; credential_1.username = "bob"; - credential_1.urls.shown = "example.com"; - credential_1.urls.link = "https://example.com"; - credential_1.is_android_credential = false; + credential_1.affiliated_domains.emplace_back(); + credential_1.affiliated_domains.back().name = "example.com"; + credential_1.affiliated_domains.back().url = "https://example.com"; credential_1.change_password_url = "https://example.com/change-password"; credential_1.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; credential_1.compromised_info.emplace(); @@ -322,9 +325,9 @@ TestPasswordsPrivateDelegate::GetCredentialsWithReusedPassword() { api::passwords_private::PasswordUiEntry credential_2; credential_2.username = "angela"; - credential_2.urls.shown = "test.com"; - credential_2.urls.link = "https://test.com"; - credential_2.is_android_credential = false; + credential_2.affiliated_domains.emplace_back(); + credential_2.affiliated_domains.back().name = "test.com"; + credential_2.affiliated_domains.back().url = "https://test.com"; credential_2.stored_in = api::passwords_private::PASSWORD_STORE_SET_DEVICE; credential_2.compromised_info.emplace(); credential_2.compromised_info->compromise_types = { @@ -351,22 +354,12 @@ bool TestPasswordsPrivateDelegate::UnmuteInsecureCredential( return IsCredentialPresentInInsecureCredentialsList(credential); } -void TestPasswordsPrivateDelegate::RecordChangePasswordFlowStarted( - const api::passwords_private::PasswordUiEntry& credential) { - last_change_flow_url_ = - credential.change_password_url ? *credential.change_password_url : ""; -} - void TestPasswordsPrivateDelegate::StartPasswordCheck( StartPasswordCheckCallback callback) { start_password_check_triggered_ = true; std::move(callback).Run(start_password_check_state_); } -void TestPasswordsPrivateDelegate::StopPasswordCheck() { - stop_password_check_triggered_ = true; -} - api::passwords_private::PasswordCheckStatus TestPasswordsPrivateDelegate::GetPasswordCheckStatus() { api::passwords_private::PasswordCheckStatus status; diff --git a/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h b/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h index b2061d34e50..25c39a8de01 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h +++ b/chromium/chrome/browser/extensions/api/passwords_private/test_passwords_private_delegate.h @@ -40,11 +40,6 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { const std::u16string& note, bool use_account_store, content::WebContents* web_contents) override; - // Fake implementation of ChangeSavedPassword. This succeeds if the current - // list of entries has the id and if the new password isn't empty. - absl::optional<int> ChangeSavedPassword( - const int id, - const api::passwords_private::ChangeSavedPasswordParams& params) override; bool ChangeCredential( const api::passwords_private::PasswordUiEntry& credential) override; void RemoveCredential( @@ -62,6 +57,8 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { content::WebContents* web_contents) override; void MovePasswordsToAccount(const std::vector<int>& ids, content::WebContents* web_contents) override; + void FetchFamilyMembers(FetchFamilyResultsCallback callback) override; + void SharePassword(int id, const ShareRecipients& recipients) override; void ImportPasswords(api::passwords_private::PasswordStoreSet to_store, ImportResultsCallback results_callback, content::WebContents* web_contents) override; @@ -71,7 +68,6 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { void ResetImporter(bool delete_file) override; void ExportPasswords(base::OnceCallback<void(const std::string&)> callback, content::WebContents* web_contents) override; - void CancelExportPasswords() override; api::passwords_private::ExportProgressStatus GetExportProgressStatus() override; bool IsOptedInForAccountStorage() override; @@ -89,12 +85,7 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { // delegate knows of a insecure credential with the same id. bool UnmuteInsecureCredential( const api::passwords_private::PasswordUiEntry& credential) override; - // Fake implementation of `RecordChangePasswordFlowStarted`. Sets the url - // returned by `last_change_flow_url()`. - void RecordChangePasswordFlowStarted( - const api::passwords_private::PasswordUiEntry& credential) override; void StartPasswordCheck(StartPasswordCheckCallback callback) override; - void StopPasswordCheck() override; api::passwords_private::PasswordCheckStatus GetPasswordCheckStatus() override; password_manager::InsecureCredentialsManager* GetInsecureCredentialsManager() override; @@ -116,22 +107,18 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { bool ContinueImportTriggered() const { return continue_import_triggered_; } bool ResetImporterTriggered() const { return reset_importer_triggered_; } bool ExportPasswordsTriggered() const { return export_passwords_triggered_; } - bool CancelExportPasswordsTriggered() const { - return cancel_export_passwords_triggered_; + bool FetchFamilyMembersTriggered() const { + return fetch_family_members_triggered_; } + bool SharePasswordTriggered() const { return share_password_triggered_; } bool StartPasswordCheckTriggered() const { return start_password_check_triggered_; } - bool StopPasswordCheckTriggered() const { - return stop_password_check_triggered_; - } void SetStartPasswordCheckState( password_manager::BulkLeakCheckService::State state) { start_password_check_state_ = state; } - const std::string& last_change_flow_url() { return last_change_flow_url_; } - const std::vector<int>& last_moved_passwords() const { return last_moved_passwords_; } @@ -173,6 +160,8 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { api::passwords_private::ImportResults import_results_; + api::passwords_private::FamilyFetchResults family_fetch_results_; + // List of insecure credentials. std::vector<api::passwords_private::PasswordUiEntry> insecure_credentials_; raw_ptr<Profile, DanglingUntriaged> profile_ = nullptr; @@ -180,23 +169,21 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { bool is_opted_in_for_account_storage_ = false; bool is_account_store_default_ = false; + // Flags for detecting whether password sharing operations have been invoked. + bool fetch_family_members_triggered_ = false; + bool share_password_triggered_ = false; + // Flags for detecting whether import/export operations have been invoked. bool import_passwords_triggered_ = false; bool continue_import_triggered_ = false; bool reset_importer_triggered_ = false; bool export_passwords_triggered_ = false; - bool cancel_export_passwords_triggered_ = false; // Flags for detecting whether password check operations have been invoked. bool start_password_check_triggered_ = false; - bool stop_password_check_triggered_ = false; password_manager::BulkLeakCheckService::State start_password_check_state_ = password_manager::BulkLeakCheckService::State::kRunning; - // Url of the last reported change password flow. Defaults to empty if - // none has been registered. - std::string last_change_flow_url_; - // Records the ids of the passwords that were last moved. std::vector<int> last_moved_passwords_; diff --git a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc index fa93ed64865..4d5278e8c53 100644 --- a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc +++ b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.cc @@ -12,11 +12,11 @@ namespace extensions { // static -PdfViewerPrivateEventRouter* PdfViewerPrivateEventRouter::Create( - content::BrowserContext* context) { +std::unique_ptr<PdfViewerPrivateEventRouter> +PdfViewerPrivateEventRouter::Create(content::BrowserContext* context) { DCHECK(context); Profile* profile = Profile::FromBrowserContext(context); - return new PdfViewerPrivateEventRouter(profile); + return std::make_unique<PdfViewerPrivateEventRouter>(profile); } PdfViewerPrivateEventRouter::PdfViewerPrivateEventRouter(Profile* profile) diff --git a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h index 4035fac9170..1d4ffd7cf2f 100644 --- a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h +++ b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router.h @@ -20,7 +20,8 @@ namespace extensions { class PdfViewerPrivateEventRouter : public KeyedService, public EventRouter::Observer { public: - static PdfViewerPrivateEventRouter* Create(content::BrowserContext* context); + static std::unique_ptr<PdfViewerPrivateEventRouter> Create( + content::BrowserContext* context); explicit PdfViewerPrivateEventRouter(Profile* profile); diff --git a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc index 77bb8787bb8..3a1c61b2763 100644 --- a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc +++ b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.cc @@ -42,7 +42,8 @@ PdfViewerPrivateEventRouterFactory::PdfViewerPrivateEventRouterFactory() PdfViewerPrivateEventRouterFactory::~PdfViewerPrivateEventRouterFactory() = default; -KeyedService* PdfViewerPrivateEventRouterFactory::BuildServiceInstanceFor( +std::unique_ptr<KeyedService> +PdfViewerPrivateEventRouterFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const { return PdfViewerPrivateEventRouter::Create(context); } diff --git a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h index 97612812ac9..5cb6e8c7731 100644 --- a/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h +++ b/chromium/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_event_router_factory.h @@ -42,7 +42,7 @@ class PdfViewerPrivateEventRouterFactory : public ProfileKeyedServiceFactory { ~PdfViewerPrivateEventRouterFactory() override; // BrowserContextKeyedServiceFactory: - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const override; }; diff --git a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc index 7e45a793576..4eb304afe20 100644 --- a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc +++ b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc @@ -21,6 +21,7 @@ #include "chromeos/crosapi/mojom/keystore_error.mojom-shared.h" #include "chromeos/crosapi/mojom/keystore_service.mojom.h" #include "components/web_modal/web_contents_modal_dialog_manager.h" +#include "net/base/net_errors.h" #include "net/cert/asn1_util.h" #include "net/cert/cert_status_flags.h" #include "net/cert/x509_util.h" diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api.cc b/chromium/chrome/browser/extensions/api/preference/preference_api.cc index 8f74c295315..aaf07e4ced3 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_api.cc +++ b/chromium/chrome/browser/extensions/api/preference/preference_api.cc @@ -41,6 +41,7 @@ #include "extensions/browser/extension_system_provider.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/pref_names.h" +#include "extensions/common/api/types.h" #include "extensions/common/constants.h" #include "extensions/common/error_utils.h" #include "extensions/common/extension_id.h" @@ -61,6 +62,8 @@ namespace extensions { namespace { +using extensions::api::types::ChromeSettingScope; + constexpr char kConversionErrorMessage[] = "Internal error: Stored value for preference '*' cannot be converted " "properly."; @@ -79,6 +82,37 @@ constexpr char kIncognitoSpecific[] = "incognitoSpecific"; constexpr char kLevelOfControl[] = "levelOfControl"; constexpr char kValue[] = "value"; +#if BUILDFLAG(IS_CHROMEOS_LACROS) +// Returns true if the get, set or clear requests for the preference associated +// with `pref_path` should only be applied at browser level. Returns false if +// the requests should be forwarded to Ash. +// All preferences explicitly added to`crosapi::mojom::PrefPath` should be +// handled by Ash. The only exception is the `crosapi::mojom::PrefPath::kProxy` +// pref which, for secondary profiles only, is applied at browser scope. +bool IsBrowserScopePrefOperation(crosapi::mojom::PrefPath pref_path, + Profile* profile) { + if (pref_path == crosapi::mojom::PrefPath::kUnknown) { + return true; + } + if (pref_path == crosapi::mojom::PrefPath::kProxy) { + if (!profile->IsMainProfile()) { + return true; + } + // TODO(acostinas,b/267719988) If the current version of Ash does not + // support syncing the proxy pref via the Prefs mojo service, the proxy pref + // can be set at browser scope only and it will be synced with Ash via the + // NetworkSettingsService mojo API. + static constexpr int kMinVersionProxyPref = 4; + const int version = chromeos::LacrosService::Get() + ->GetInterfaceVersion<crosapi::mojom::Prefs>(); + if (version < kMinVersionProxyPref) { + return true; + } + } + return false; +} +#endif + // Transform the thirdPartyCookiesAllowed extension api to CookieControlsMode // enum values. class CookieControlsModeTransformer : public PrefTransformerInterface { @@ -188,27 +222,9 @@ class PrivacySandboxTransformer : public PrefTransformerInterface { } }; -constexpr char kIncognitoPersistent[] = "incognito_persistent"; -constexpr char kIncognitoSessionOnly[] = "incognito_session_only"; -constexpr char kRegular[] = "regular"; -constexpr char kRegularOnly[] = "regular_only"; - -// TODO(crbug.com/1366445): Consider using the ChromeSettingScope -// enum instead of ExtensionPrefsScope. That way, we could remove -// this function and the preceding string constants. -bool StringToScope(const std::string& s, ExtensionPrefsScope* scope) { - if (s == kRegular) { - *scope = kExtensionPrefsScopeRegular; - } else if (s == kRegularOnly) { - *scope = kExtensionPrefsScopeRegularOnly; - } else if (s == kIncognitoPersistent) { - *scope = kExtensionPrefsScopeIncognitoPersistent; - } else if (s == kIncognitoSessionOnly) { - *scope = kExtensionPrefsScopeIncognitoSessionOnly; - } else { - return false; - } - return true; +bool StringToScope(const std::string& s, ChromeSettingScope& scope) { + scope = extensions::api::types::ParseChromeSettingScope(s); + return scope != ChromeSettingScope::kNone; } } // namespace @@ -232,7 +248,7 @@ PreferenceEventRouter::PreferenceEventRouter(Profile* profile) #if BUILDFLAG(IS_CHROMEOS_LACROS) crosapi::mojom::PrefPath pref_path = PrefMapping::GetInstance()->GetPrefPathForPrefName(pref.browser_pref); - if (pref_path != crosapi::mojom::PrefPath::kUnknown && + if (!IsBrowserScopePrefOperation(pref_path, profile) && ash_supports_crosapi_observers) { // Extension-controlled pref with the real value to watch in ash. // This base::Unretained() is safe because PreferenceEventRouter owns @@ -527,19 +543,19 @@ void PreferenceAPI::OnContentSettingChanged(const std::string& extension_id, ExtensionPrefs::Get(profile_)->UpdateExtensionPref( extension_id, pref_names::kPrefIncognitoContentSettings, base::Value(content_settings_store()->GetSettingsForExtension( - extension_id, kExtensionPrefsScopeIncognitoPersistent))); + extension_id, ChromeSettingScope::kIncognitoPersistent))); } else { ExtensionPrefs::Get(profile_)->UpdateExtensionPref( extension_id, pref_names::kPrefContentSettings, base::Value(content_settings_store()->GetSettingsForExtension( - extension_id, kExtensionPrefsScopeRegular))); + extension_id, ChromeSettingScope::kRegular))); } } void PreferenceAPI::ClearIncognitoSessionOnlyContentSettings() { for (const auto& id : ExtensionPrefs::Get(profile_)->GetExtensions()) { content_settings_store()->ClearContentSettingsForExtension( - id, kExtensionPrefsScopeIncognitoSessionOnly); + id, ChromeSettingScope::kIncognitoSessionOnly); } } @@ -623,8 +639,12 @@ ExtensionFunction::ResponseAction GetPreferenceFunction::Run() { cached_browser_pref_ = browser_pref; crosapi::mojom::PrefPath pref_path = PrefMapping::GetInstance()->GetPrefPathForPrefName(cached_browser_pref_); - if (pref_path != crosapi::mojom::PrefPath::kUnknown) { - if (!profile->IsMainProfile()) { + if (!IsBrowserScopePrefOperation(pref_path, profile)) { + // Exclude chrome.privacy.website.protectedContentID (mapped to + // kProtectedContentDefault) from secondary profile access + // (crbug.com/1450718). + if (!profile->IsMainProfile() && + pref_path == crosapi::mojom::PrefPath::kProtectedContentDefault) { return RespondNow(Error(kPrimaryProfileOnlyErrorMessage, pref_key)); } // This pref should be read from ash. @@ -749,15 +769,14 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() { const base::Value* value = details.Find(kValue); EXTENSION_FUNCTION_VALIDATE(value); - ExtensionPrefsScope scope = kExtensionPrefsScopeRegular; + ChromeSettingScope scope = ChromeSettingScope::kRegular; if (const std::string* scope_str = details.FindString(kScopeKey)) { - EXTENSION_FUNCTION_VALIDATE(StringToScope(*scope_str, &scope)); + EXTENSION_FUNCTION_VALIDATE(StringToScope(*scope_str, scope)); } // Check incognito scope. - bool incognito = - (scope == kExtensionPrefsScopeIncognitoPersistent || - scope == kExtensionPrefsScopeIncognitoSessionOnly); + bool incognito = scope == ChromeSettingScope::kIncognitoPersistent || + scope == ChromeSettingScope::kIncognitoSessionOnly; if (incognito) { // Regular profiles can't access incognito unless // include_incognito_information is true. @@ -775,7 +794,7 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() { } Profile* profile = Profile::FromBrowserContext(browser_context()); - if (scope == kExtensionPrefsScopeIncognitoSessionOnly && + if (scope == ChromeSettingScope::kIncognitoSessionOnly && !profile->HasPrimaryOTRProfile()) { return RespondNow(Error(extension_misc::kIncognitoSessionOnlyErrorMessage)); } @@ -796,7 +815,7 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() { crosapi::mojom::PrefPath pref_path = PrefMapping::GetInstance()->GetPrefPathForPrefName(browser_pref); chromeos::LacrosService* lacros_service; - if (pref_path != crosapi::mojom::PrefPath::kUnknown) { + if (!IsBrowserScopePrefOperation(pref_path, profile)) { if (!profile->IsMainProfile()) { return RespondNow(Error(kPrimaryProfileOnlyErrorMessage, pref_key)); } @@ -899,7 +918,7 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() { prefs_helper->SetExtensionControlledPref(extension_id(), browser_pref, scope, browser_pref_value->Clone()); #if BUILDFLAG(IS_CHROMEOS_LACROS) - if (pref_path != crosapi::mojom::PrefPath::kUnknown && + if (!IsBrowserScopePrefOperation(pref_path, profile) && prefs_helper->DoesExtensionControlPref(extension_id(), browser_pref, nullptr)) { lacros_service->GetRemote<crosapi::mojom::Prefs>()->SetPref( @@ -928,15 +947,14 @@ ExtensionFunction::ResponseAction ClearPreferenceFunction::Run() { std::string pref_key = args()[0].GetString(); const base::Value::Dict& details = args()[1].GetDict(); - ExtensionPrefsScope scope = kExtensionPrefsScopeRegular; + ChromeSettingScope scope = ChromeSettingScope::kRegular; if (const std::string* scope_str = details.FindString(kScopeKey)) { - EXTENSION_FUNCTION_VALIDATE(StringToScope(*scope_str, &scope)); + EXTENSION_FUNCTION_VALIDATE(StringToScope(*scope_str, scope)); } // Check incognito scope. - bool incognito = - (scope == kExtensionPrefsScopeIncognitoPersistent || - scope == kExtensionPrefsScopeIncognitoSessionOnly); + bool incognito = scope == ChromeSettingScope::kIncognitoPersistent || + scope == ChromeSettingScope::kIncognitoSessionOnly; if (incognito) { // We don't check incognito permissions here, as an extension should be // always allowed to clear its own settings. @@ -964,8 +982,8 @@ ExtensionFunction::ResponseAction ClearPreferenceFunction::Run() { crosapi::mojom::PrefPath pref_path = PrefMapping::GetInstance()->GetPrefPathForPrefName(browser_pref); chromeos::LacrosService* lacros_service; - if (pref_path != crosapi::mojom::PrefPath::kUnknown) { - Profile* profile = Profile::FromBrowserContext(browser_context()); + Profile* profile = Profile::FromBrowserContext(browser_context()); + if (!IsBrowserScopePrefOperation(pref_path, profile)) { if (!profile->IsMainProfile()) { return RespondNow(Error(kPrimaryProfileOnlyErrorMessage, pref_key)); } @@ -1015,14 +1033,13 @@ ExtensionFunction::ResponseAction ClearPreferenceFunction::Run() { extension_id(), prefs::kSafeBrowsingEnhanced, scope); } #if BUILDFLAG(IS_CHROMEOS_LACROS) - if (pref_path != crosapi::mojom::PrefPath::kUnknown && + if (!IsBrowserScopePrefOperation(pref_path, profile) && did_just_control_pref) { // This is an ash pref and we need to update ash because the extension that // just cleared the pref used to control it. Now, either another extension // of lower precedence controls the pref (in which case we update the pref // to that value), or no other extension has set the pref (in which case // we can clear the value set by extensions in ash). - Profile* profile = Profile::FromBrowserContext(browser_context()); PrefService* pref_service = extensions::preference_helpers::GetProfilePrefService(profile, incognito); diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api.h b/chromium/chrome/browser/extensions/api/preference/preference_api.h index a1d0c204081..abee3457484 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_api.h +++ b/chromium/chrome/browser/extensions/api/preference/preference_api.h @@ -18,6 +18,7 @@ #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_function.h" +#include "extensions/common/api/types.h" #if BUILDFLAG(IS_CHROMEOS_LACROS) #include "chromeos/crosapi/mojom/prefs.mojom-shared.h" @@ -96,6 +97,7 @@ class PreferenceAPI : public BrowserContextKeyedAPI, public EventRouter::Observer, public ContentSettingsStore::Observer { public: + using ChromeSettingScope = extensions::api::types::ChromeSettingScope; explicit PreferenceAPI(content::BrowserContext* context); PreferenceAPI(const PreferenceAPI&) = delete; diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc b/chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc index 0ffff000462..b90472f5495 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc @@ -7,22 +7,31 @@ #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/task/single_thread_task_runner.h" +#include "base/test/test_future.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/profiles/profile_test_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/pref_names.h" -#include "chromeos/crosapi/mojom/prefs.mojom-test-utils.h" +#include "chromeos/crosapi/mojom/prefs.mojom-shared.h" #include "chromeos/crosapi/mojom/prefs.mojom.h" +#include "chromeos/lacros/crosapi_pref_observer.h" #include "chromeos/lacros/lacros_service.h" #include "chromeos/lacros/lacros_test_helper.h" #include "chromeos/startup/browser_params_proxy.h" #include "components/keep_alive_registry/keep_alive_types.h" #include "components/keep_alive_registry/scoped_keep_alive.h" #include "components/prefs/pref_service.h" +#include "components/proxy_config/proxy_config_dictionary.h" +#include "components/proxy_config/proxy_config_pref_names.h" #include "content/public/browser/notification_service.h" #include "content/public/test/browser_test.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" #include "extensions/browser/test_extension_registry_observer.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/result_catcher.h" @@ -30,8 +39,26 @@ #include "mojo/public/cpp/bindings/remote_set.h" #include "testing/gtest/include/gtest/gtest.h" +namespace { + using ContextType = extensions::ExtensionBrowserTest::ContextType; +void SetPref(crosapi::mojom::PrefPath path, base::Value value) { + base::test::TestFuture<void> future; + chromeos::LacrosService::Get()->GetRemote<crosapi::mojom::Prefs>()->SetPref( + path, std::move(value), future.GetCallback()); + ASSERT_TRUE(future.Wait()); +} + +absl::optional<base::Value> GetPref(crosapi::mojom::PrefPath path) { + base::test::TestFuture<absl::optional<base::Value>> future; + chromeos::LacrosService::Get()->GetRemote<crosapi::mojom::Prefs>()->GetPref( + path, future.GetCallback()); + return future.Take(); +} + +} // namespace + // Tests for extension-controlled prefs, where an extension in lacros sets a // pref where the underlying feature lives in ash. class ExtensionPreferenceApiLacrosBrowserTest @@ -56,6 +83,13 @@ class ExtensionPreferenceApiLacrosBrowserTest EXPECT_TRUE(pref->IsExtensionControlled()); EXPECT_TRUE( prefs->GetBoolean(prefs::kLacrosAccessibilitySpokenFeedbackEnabled)); + + const PrefService::Preference* proxy_pref = + prefs->FindPreference(proxy_config::prefs::kProxy); + ASSERT_TRUE(proxy_pref); + EXPECT_TRUE(proxy_pref->IsExtensionControlled()); + EXPECT_EQ(ProxyConfigDictionary::CreateDirect(), + proxy_pref->GetValue()->GetDict()); } void CheckPreferencesCleared() { @@ -66,6 +100,29 @@ class ExtensionPreferenceApiLacrosBrowserTest EXPECT_FALSE(pref->IsExtensionControlled()); EXPECT_FALSE( prefs->GetBoolean(prefs::kLacrosAccessibilitySpokenFeedbackEnabled)); + + const PrefService::Preference* proxy_pref = + prefs->FindPreference(proxy_config::prefs::kProxy); + ASSERT_TRUE(proxy_pref); + EXPECT_FALSE(proxy_pref->IsExtensionControlled()); + EXPECT_EQ(ProxyConfigDictionary::CreateSystem(), + proxy_pref->GetValue()->GetDict()); + } + + void SetUp() override { + // When the test changes the value of + // chrome.accessibilityFeatures.autoclick in Ash, the pref value change is + // observed by AccessibilityController and will trigger popping up a dialog + // in Ash with the prompt about confirmation of disabling autoclick. The + // dialog is not closed when the test is torn down in Lacros, and will + // affect other tests running after it if the test runs with shared Ash. + // Therefore, we start a unique Ash to run with this test suite to avoid + // the test isolation issue. + StartUniqueAshChrome( + {}, {}, {}, + "crbug.com/1435317 Switch to shared ash when autoclick disable " + "confirmation dialog issue is fixed"); + ExtensionApiTest::SetUp(); } void SetUpOnMainThread() override { @@ -107,6 +164,13 @@ class ExtensionPreferenceApiLacrosBrowserTest return true; } + bool IsLacrosServiceSyncingProxyPref() { + static constexpr int kMinVersionProxyPolicy = 4; + const int version = chromeos::LacrosService::Get() + ->GetInterfaceVersion<crosapi::mojom::Prefs>(); + return version >= kMinVersionProxyPolicy; + } + bool DoesAshSupportObservers() { // Versions of ash without this capability cannot create observers for prefs // writing to the ash standalone browser prefstore. @@ -130,14 +194,9 @@ INSTANTIATE_TEST_SUITE_P(ServiceWorker, ::testing::Values(ContextType::kServiceWorker)); IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, Lacros) { - absl::optional<::base::Value> out_value; - crosapi::mojom::PrefsAsyncWaiter async_waiter( - chromeos::LacrosService::Get()->GetRemote<crosapi::mojom::Prefs>().get()); - // At start, the value in ash should not be set. - async_waiter.GetPref( - crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled, - &out_value); + absl::optional<base::Value> out_value = + GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled); EXPECT_FALSE(out_value.value().GetBool()); extensions::ExtensionId test_extension_id; @@ -160,11 +219,14 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, Lacros) { CheckPreferencesSet(); // In ash, the value should now be set. - async_waiter.GetPref( - crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled, - &out_value); + out_value = + GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled); EXPECT_TRUE(out_value.value().GetBool()); - + if (IsLacrosServiceSyncingProxyPref()) { + out_value = GetPref(crosapi::mojom::PrefPath::kProxy); + EXPECT_EQ(out_value.value().GetDict(), + ProxyConfigDictionary::CreateDirect()); + } // The settings should not be reset when the extension is reloaded. { ExtensionTestMessageListener listener("ready", ReplyBehavior::kWillReply); @@ -186,10 +248,15 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, Lacros) { // When the extension in uninstalled, the pref in lacros should be the // default value (false). This only works if Ash correctly implements // extension-controlled pref observers. - async_waiter.GetPref( - crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled, - &out_value); + out_value = + GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled); EXPECT_FALSE(out_value.value().GetBool()); + + if (IsLacrosServiceSyncingProxyPref()) { + out_value = GetPref(crosapi::mojom::PrefPath::kProxy); + EXPECT_EQ(out_value.value().GetDict(), + ProxyConfigDictionary::CreateSystem()); + } } { @@ -201,6 +268,69 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, Lacros) { CheckPreferencesCleared(); } +IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, + LacrosSecondaryProfile) { + // At start, the value in ash should not be set. + absl::optional<base::Value> out_value = + GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled); + EXPECT_FALSE(out_value.value().GetBool()); + + // Create a secondary profile. + ProfileManager* profile_manager = g_browser_process->profile_manager(); + Profile& secondary_profile = profiles::testing::CreateProfileSync( + profile_manager, profile_manager->GenerateNextProfileDirectoryPath()); + ASSERT_FALSE(secondary_profile.IsMainProfile()); + + // Load the testing extension in secondary profile. + extensions::ResultCatcher catcher; + ExtensionTestMessageListener listener_1("ready", ReplyBehavior::kWillReply); + extensions::ChromeTestExtensionLoader loader(&secondary_profile); + base::FilePath extension_path = + test_data_dir_.AppendASCII("preference/lacros_secondary_profile_read"); + scoped_refptr<const extensions::Extension> extension = + loader.LoadExtension(extension_path); + ASSERT_TRUE(extension); + EXPECT_TRUE(listener_1.WaitUntilSatisfied()); + + // Run the test to verify that testing extension running in secondary + // profile reads the default values of the Prefs correctly. + listener_1.Reply("run test default value"); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + + // Set the pref value in ash. + SetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled, + base::Value(true)); + + // Verify the value is set in ash side. + out_value = + GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled); + EXPECT_TRUE(out_value.value().GetBool()); + + // Reload the testing extension in the secondary profile. + ExtensionTestMessageListener listener_2("ready", ReplyBehavior::kWillReply); + extensions::TestExtensionRegistryObserver observer( + extensions::ExtensionRegistry::Get(&secondary_profile), extension->id()); + extensions::ExtensionService* extension_service = + extensions::ExtensionSystem::Get(&secondary_profile)->extension_service(); + extension_service->ReloadExtension(extension->id()); + observer.WaitForExtensionLoaded(); + EXPECT_TRUE(listener_2.WaitUntilSatisfied()); + + // Run the test to verify that testing extension running in secondary + // profile reads the changed value of the accessibilityFeatures correctly. + listener_2.Reply("run test changed value"); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); + + // Since lacros browser tests shared the same ash instance, we need to restore + // the modified pref in ash to default before exiting the test, so that + // it won't affect other lacros browser tests. + SetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled, + base::Value(false)); + out_value = + GetPref(crosapi::mojom::PrefPath::kAccessibilitySpokenFeedbackEnabled); + EXPECT_FALSE(out_value.value().GetBool()); +} + IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, OnChange) { if (!DoesAshSupportObservers()) { LOG(WARNING) << "Ash does not support observers, skipping the test."; @@ -222,6 +352,144 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, << message_; } +base::Value::Dict GetAshProxyPrefValue() { + absl::optional<::base::Value> out_value = + GetPref(crosapi::mojom::PrefPath::kProxy); + return out_value.value().GetDict().Clone(); +} + +scoped_refptr<const extensions::Extension> InstallExtensionForProfile( + Profile* profile, + const base::FilePath& path) { + extensions::ResultCatcher catcher; + ExtensionTestMessageListener listener("ready", ReplyBehavior::kWillReply); + scoped_refptr<const extensions::Extension> extension = + extensions::ChromeTestExtensionLoader(profile).LoadExtension(path); + EXPECT_TRUE(listener.WaitUntilSatisfied()); + // Run the tests. + listener.Reply("run test"); + EXPECT_TRUE(catcher.GetNextResult()); + return extension; +} + +void ExpectThatProxyIsControlledByExtension(Profile* profile) { + const PrefService::Preference* pref = + profile->GetPrefs()->FindPreference(proxy_config::prefs::kProxy); + EXPECT_TRUE(pref->IsExtensionControlled()); + EXPECT_EQ(ProxyConfigDictionary::CreateDirect(), pref->GetValue()->GetDict()); +} + +void ExpectThatProxyHasDefaultValue(Profile* profile) { + const PrefService::Preference* pref = + profile->GetPrefs()->FindPreference(proxy_config::prefs::kProxy); + EXPECT_FALSE(pref->IsExtensionControlled()); + EXPECT_EQ(ProxyConfigDictionary::CreateSystem(), pref->GetValue()->GetDict()); +} + +// Secondary profiles should apply extension set proxy at browser level, but not +// in Ash. +IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, + SecondaryProfilePrefs) { + if (!IsServiceAvailable()) { + return; + } + if (!IsLacrosServiceSyncingProxyPref()) { + GTEST_SKIP() << "Skipping test because the current version of Ash does not " + "support getting the proxy preference from a Lacros " + "extension via the preferences service"; + } + ProfileManager* profile_manager = g_browser_process->profile_manager(); + base::FilePath path_profile = + profile_manager->GenerateNextProfileDirectoryPath(); + Profile& secondary_profile = + profiles::testing::CreateProfileSync(profile_manager, path_profile); + scoped_refptr<const extensions::Extension> extension = + InstallExtensionForProfile( + &secondary_profile, + test_data_dir_.AppendASCII("preference/lacros_secondary_profile")); + // Verify that the proxy is set by the extension for the secondary profile. + ExpectThatProxyIsControlledByExtension(&secondary_profile); + // The proxy should not be set in the primary profile and Ash. + ExpectThatProxyHasDefaultValue(profile()); + EXPECT_EQ(GetAshProxyPrefValue(), ProxyConfigDictionary::CreateSystem()); +} + +// Clearing an extension set proxy in a secondary profile should not clear the +// extension set proxy in the primary profile and Ash (if the primary profile +// has an extension which controls the proxy). The test setup: +// - Create a secondary profile; +// - Install an extension which controls the proxy pref in the primary profile; +// - Install an extension which controls the proxy pref in the secondary +// profile; +// - Verify that both profiles have extension controlled proxy prefs; +// - Uninstall the proxy controlling extension in the secondary profile; +// - Verify that the secondary profile does not have an extension set proxy; +// - Verify that the primary profile and Ash still have an extension set proxy. +// This test can be extended to other prefs for which the primary profile +// controls the value in Ash but secondary profiles only control the pref +// value at browser level. +IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, + SecondaryProfilePrefsClearPref) { + if (!IsServiceAvailable()) { + return; + } + if (!IsLacrosServiceSyncingProxyPref()) { + GTEST_SKIP() << "Skipping test because the current version of Ash does not " + "support getting the proxy preference from a Lacros " + "extension via the preferences service"; + } + ProfileManager* profile_manager = g_browser_process->profile_manager(); + base::FilePath path_profile = + profile_manager->GenerateNextProfileDirectoryPath(); + Profile& secondary_profile = + profiles::testing::CreateProfileSync(profile_manager, path_profile); + + scoped_refptr<const extensions::Extension> extension_primary = + InstallExtensionForProfile( + profile(), + test_data_dir_.AppendASCII("preference/lacros_secondary_profile")); + + scoped_refptr<const extensions::Extension> extension_secondary = + InstallExtensionForProfile( + &secondary_profile, + test_data_dir_.AppendASCII("preference/lacros_secondary_profile")); + + ExpectThatProxyIsControlledByExtension(&secondary_profile); + ExpectThatProxyIsControlledByExtension(profile()); + + // Uninstall the extension in the secondary profile and test that Ash is still + // returning the pref set by the extension running in the Lacros primary + // profile. + { + extensions::TestExtensionRegistryObserver observer( + extensions::ExtensionRegistry::Get(&secondary_profile), + extension_secondary->id()); + auto* service_ = extensions::ExtensionSystem::Get(&secondary_profile) + ->extension_service(); + service_->UninstallExtension(extension_secondary->id(), + extensions::UNINSTALL_REASON_FOR_TESTING, + NULL); + observer.WaitForExtensionUninstalled(); + } + + ExpectThatProxyHasDefaultValue(&secondary_profile); + ExpectThatProxyIsControlledByExtension(profile()); + EXPECT_EQ(GetAshProxyPrefValue(), ProxyConfigDictionary::CreateDirect()); + + // Uninstall the extension in the primary profile. + { + extensions::TestExtensionRegistryObserver observer( + extensions::ExtensionRegistry::Get(profile()), extension_primary->id()); + auto* service_ = + extensions::ExtensionSystem::Get(profile())->extension_service(); + service_->UninstallExtension(extension_primary->id(), + extensions::UNINSTALL_REASON_FOR_TESTING, + NULL); + observer.WaitForExtensionUninstalled(); + } + EXPECT_EQ(GetAshProxyPrefValue(), ProxyConfigDictionary::CreateSystem()); +} + // An implementation of the `crosapi::mojom::Prefs` mojo service which returns // null when fetching a pref value. Used for testing the Preference API against // Ash-Lacros version skew where Ash does not recognize the Lacros extension diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc b/chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc index 78e39d8bad4..8ca9d8be440 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc +++ b/chromium/chrome/browser/extensions/api/preference/preference_api_prefs_unittest.cc @@ -17,10 +17,12 @@ #include "extensions/browser/api/content_settings/content_settings_service.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs_helper.h" +#include "extensions/common/api/types.h" #include "extensions/common/extension.h" #include "testing/gtest/include/gtest/gtest.h" using base::Value; +using extensions::api::types::ChromeSettingScope; namespace extensions { @@ -95,7 +97,7 @@ void ExtensionControlledPrefsTest::InstallExtensionControlledPref( base::Value value) { EnsureExtensionInstalled(extension); prefs_helper_.SetExtensionControlledPref( - extension->id(), key, kExtensionPrefsScopeRegular, std::move(value)); + extension->id(), key, ChromeSettingScope::kRegular, std::move(value)); } void ExtensionControlledPrefsTest::InstallExtensionControlledPrefIncognito( @@ -104,7 +106,7 @@ void ExtensionControlledPrefsTest::InstallExtensionControlledPrefIncognito( base::Value value) { EnsureExtensionInstalled(extension); prefs_helper_.SetExtensionControlledPref( - extension->id(), key, kExtensionPrefsScopeIncognitoPersistent, + extension->id(), key, ChromeSettingScope::kIncognitoPersistent, std::move(value)); } @@ -114,7 +116,7 @@ void ExtensionControlledPrefsTest:: base::Value value) { EnsureExtensionInstalled(extension); prefs_helper_.SetExtensionControlledPref( - extension->id(), key, kExtensionPrefsScopeIncognitoSessionOnly, + extension->id(), key, ChromeSettingScope::kIncognitoSessionOnly, std::move(value)); } @@ -246,7 +248,7 @@ class ControlledPrefsUninstallExtension : public ExtensionControlledPrefsTest { ContentSettingsPattern::FromString("http://[*.]example.com"); store->SetExtensionContentSetting( extension1()->id(), pattern, pattern, ContentSettingsType::IMAGES, - CONTENT_SETTING_BLOCK, kExtensionPrefsScopeRegular); + CONTENT_SETTING_BLOCK, ChromeSettingScope::kRegular); UninstallExtension(extension1()->id()); } diff --git a/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc b/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc index 55f11fa80f9..fd399146cb9 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc +++ b/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc @@ -693,7 +693,9 @@ IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiEventPageTest, // This check is not done in the Standard test so we can test if the granular // Privacy Sandbox APIs are turned off, when |kPrivacySandboxApisEnabled| is // turned off, in isolation of controlling them directly. -IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiTest, PrivacySandboxMigration) { +// TODO(crbug.com/1470295): Test is flaky on all platforms. +IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiTest, + DISABLED_PrivacySandboxMigration) { PrefService* prefs = profile_->GetPrefs(); prefs->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, true); prefs->SetBoolean(prefs::kPrivacySandboxM1FledgeEnabled, true); diff --git a/chromium/chrome/browser/extensions/api/preference/preference_helpers.h b/chromium/chrome/browser/extensions/api/preference/preference_helpers.h index 2ab0bcfdf64..2e4c116866c 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_helpers.h +++ b/chromium/chrome/browser/extensions/api/preference/preference_helpers.h @@ -10,7 +10,6 @@ #include "base/values.h" #include "build/chromeos_buildflags.h" #include "extensions/browser/extension_event_histogram_value.h" -#include "extensions/browser/extension_prefs_scope.h" #include "extensions/common/mojom/api_permission_id.mojom-shared.h" #include "extensions/common/permissions/permission_set.h" diff --git a/chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc b/chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc index 382bd6d92ba..e4852e7ed53 100644 --- a/chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc +++ b/chromium/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc @@ -419,12 +419,12 @@ IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersAsyncSuccess) { IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersTwoExtensions) { ResultCatcher catcher; - std::string extension_id_1; + ExtensionId extension_id_1; InitializePrinterProviderTestExtension("printer_provider/request_printers", "OK", &extension_id_1); ASSERT_FALSE(extension_id_1.empty()); - std::string extension_id_2; + ExtensionId extension_id_2; InitializePrinterProviderTestExtension( "printer_provider/request_printers_second", "OK", &extension_id_2); ASSERT_FALSE(extension_id_2.empty()); @@ -475,12 +475,12 @@ IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersTwoExtensionsBothUnloaded) { ResultCatcher catcher; - std::string extension_id_1; + ExtensionId extension_id_1; InitializePrinterProviderTestExtension("printer_provider/request_printers", "IGNORE_CALLBACK", &extension_id_1); ASSERT_FALSE(extension_id_1.empty()); - std::string extension_id_2; + ExtensionId extension_id_2; InitializePrinterProviderTestExtension( "printer_provider/request_printers_second", "IGNORE_CALLBACK", &extension_id_2); @@ -505,12 +505,12 @@ IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersTwoExtensionsOneFails) { ResultCatcher catcher; - std::string extension_id_1; + ExtensionId extension_id_1; InitializePrinterProviderTestExtension("printer_provider/request_printers", "NOT_ARRAY", &extension_id_1); ASSERT_FALSE(extension_id_1.empty()); - std::string extension_id_2; + ExtensionId extension_id_2; InitializePrinterProviderTestExtension( "printer_provider/request_printers_second", "OK", &extension_id_2); ASSERT_FALSE(extension_id_2.empty()); @@ -547,12 +547,12 @@ IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersTwoExtensionsOneWithNoListener) { ResultCatcher catcher; - std::string extension_id_1; + ExtensionId extension_id_1; InitializePrinterProviderTestExtension("printer_provider/request_printers", "NO_LISTENER", &extension_id_1); ASSERT_FALSE(extension_id_1.empty()); - std::string extension_id_2; + ExtensionId extension_id_2; InitializePrinterProviderTestExtension( "printer_provider/request_printers_second", "OK", &extension_id_2); ASSERT_FALSE(extension_id_2.empty()); diff --git a/chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h b/chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h index 9558b5e5258..64779176f0d 100644 --- a/chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h +++ b/chromium/chrome/browser/extensions/api/printing/fake_print_job_controller_ash.h @@ -8,6 +8,7 @@ #include <memory> #include "base/containers/flat_map.h" +#include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/ash/printing/cups_print_job_manager.h" #include "chrome/browser/extensions/api/printing/print_job_controller.h" @@ -47,8 +48,8 @@ class FakePrintJobControllerAsh : public PrintJobController, std::unique_ptr<printing::PrintSettings> settings); // Not owned by FakePrintJobControllerAsh. - ash::TestCupsPrintJobManager* const print_job_manager_; - ash::CupsPrintersManager* const printers_manager_; + const raw_ptr<ash::TestCupsPrintJobManager> print_job_manager_; + const raw_ptr<ash::CupsPrintersManager> printers_manager_; // Stores ongoing print jobs as a mapping from job id to CupsPrintJob. base::flat_map<std::string, std::unique_ptr<ash::CupsPrintJob>> jobs_; diff --git a/chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc b/chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc index 7c74e3a140a..53b228cd56e 100644 --- a/chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc +++ b/chromium/chrome/browser/extensions/api/printing/printing_api_handler_unittest.cc @@ -37,6 +37,7 @@ #include "extensions/browser/event_router_factory.h" #include "extensions/browser/test_event_router.h" #include "extensions/common/extension_builder.h" +#include "extensions/common/extension_id.h" #include "printing/backend/print_backend.h" #include "printing/backend/test_print_backend.h" #include "printing/mojom/print.mojom.h" @@ -88,7 +89,7 @@ class PrintingEventObserver : public TestEventRouter::EventObserver { } } - const std::string& extension_id() const { return extension_id_; } + const ExtensionId& extension_id() const { return extension_id_; } const base::Value& event_args() const { return event_args_; } @@ -100,7 +101,7 @@ class PrintingEventObserver : public TestEventRouter::EventObserver { const std::string event_name_; // The extension id passed for the last observed event. - std::string extension_id_; + ExtensionId extension_id_; // The arguments passed for the last observed event. base::Value event_args_; @@ -204,10 +205,10 @@ ConstructPrinterCapabilities() { capabilities.duplex_modes.push_back(printing::mojom::DuplexMode::kSimplex); capabilities.copies_max = 5; capabilities.dpis.emplace_back(kHorizontalDpi, kVerticalDpi); - printing::PrinterSemanticCapsAndDefaults::Paper paper; - paper.vendor_id = kMediaSizeVendorId; - paper.size_um = gfx::Size(kMediaSizeWidth, kMediaSizeHeight); - capabilities.papers.push_back(paper); + printing::PrinterSemanticCapsAndDefaults::Paper paper( + /*display_name=*/"", kMediaSizeVendorId, + {kMediaSizeWidth, kMediaSizeHeight}); + capabilities.papers.push_back(std::move(paper)); capabilities.collate_capable = true; return capabilities; } diff --git a/chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc b/chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc index 326699bf56f..bdd84766925 100644 --- a/chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc +++ b/chromium/chrome/browser/extensions/api/printing/printing_api_utils.cc @@ -305,7 +305,7 @@ bool CheckSettingsAndCapabilitiesCompatibility( capabilities.papers, [&requested_media]( const printing::PrinterSemanticCapsAndDefaults::Paper& paper) { - return paper.size_um == requested_media.size_microns; + return paper.IsSizeWithinBounds(requested_media.size_microns); }); } diff --git a/chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc b/chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc index e3992329609..2982d3e640e 100644 --- a/chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc +++ b/chromium/chrome/browser/extensions/api/printing/printing_api_utils_unittest.cc @@ -32,6 +32,7 @@ constexpr int kHorizontalDpi = 300; constexpr int kVerticalDpi = 400; constexpr int kMediaSizeWidth = 210000; constexpr int kMediaSizeHeight = 297000; +constexpr int kCustomMediaSizeMin = 2540; constexpr char kMediaSizeVendorId[] = "iso_a4_210x297mm"; constexpr char kVendorItemId[] = "finishings"; constexpr char kVendorItemValue[] = "trim"; @@ -151,14 +152,29 @@ printing::PrinterSemanticCapsAndDefaults ConstructPrinterCapabilities() { capabilities.duplex_modes.push_back(printing::mojom::DuplexMode::kLongEdge); capabilities.copies_max = kCopies; capabilities.dpis.push_back(gfx::Size(kHorizontalDpi, kVerticalDpi)); - printing::PrinterSemanticCapsAndDefaults::Paper paper; - paper.vendor_id = kMediaSizeVendorId; - paper.size_um = gfx::Size(kMediaSizeWidth, kMediaSizeHeight); + printing::PrinterSemanticCapsAndDefaults::Paper paper( + /*display_name=*/"", kMediaSizeVendorId, + gfx::Size(kMediaSizeWidth, kMediaSizeHeight)); capabilities.papers.push_back(paper); capabilities.collate_capable = true; return capabilities; } +printing::PrinterSemanticCapsAndDefaults +ConstructPrinterCapabilitiesWithCustomSize() { + printing::PrinterSemanticCapsAndDefaults capabilities = + ConstructPrinterCapabilities(); + // Reset our papers and create a new paper with a custom size range. + capabilities.papers.clear(); + printing::PrinterSemanticCapsAndDefaults::Paper paper( + /*display_name=*/"", kMediaSizeVendorId, + gfx::Size(kMediaSizeWidth, kCustomMediaSizeMin), + /*printable_area_um=*/gfx::Rect(), kMediaSizeHeight); + capabilities.papers.push_back(paper); + + return capabilities; +} + } // namespace TEST(PrintingApiUtilsTest, GetDefaultPrinterRules) { @@ -287,6 +303,41 @@ TEST(PrintingApiUtilsTest, CheckSettingsAndCapabilitiesCompatibility(*settings, capabilities)); } +TEST(PrintingApiUtilsTest, + CheckSettingsAndCapabilitiesCompatibilityCustomMediaSize) { + std::unique_ptr<printing::PrintSettings> settings = ConstructPrintSettings(); + printing::PrinterSemanticCapsAndDefaults capabilities = + ConstructPrinterCapabilitiesWithCustomSize(); + EXPECT_TRUE( + CheckSettingsAndCapabilitiesCompatibility(*settings, capabilities)); +} + +TEST(PrintingApiUtilsTest, + CheckSettingsAndCapabilitiesCompatibilityCustomMediaSizeLongWidth) { + std::unique_ptr<printing::PrintSettings> settings = ConstructPrintSettings(); + // Update the requested media so the width is wider than our custom size. + printing::PrintSettings::RequestedMedia media = settings->requested_media(); + media.size_microns.set_width(kMediaSizeWidth + 1); + settings->set_requested_media(media); + printing::PrinterSemanticCapsAndDefaults capabilities = + ConstructPrinterCapabilitiesWithCustomSize(); + EXPECT_FALSE( + CheckSettingsAndCapabilitiesCompatibility(*settings, capabilities)); +} + +TEST(PrintingApiUtilsTest, + CheckSettingsAndCapabilitiesCompatibilityCustomMediaSizeShortHeight) { + std::unique_ptr<printing::PrintSettings> settings = ConstructPrintSettings(); + // Update the requested media so the length is shorter than our custom size. + printing::PrintSettings::RequestedMedia media = settings->requested_media(); + media.size_microns.set_height(kCustomMediaSizeMin - 1); + settings->set_requested_media(media); + printing::PrinterSemanticCapsAndDefaults capabilities = + ConstructPrinterCapabilitiesWithCustomSize(); + EXPECT_FALSE( + CheckSettingsAndCapabilitiesCompatibility(*settings, capabilities)); +} + TEST(PrintingApiUtilsTest, CheckSettingsAndCapabilitiesCompatibility_Collate) { std::unique_ptr<printing::PrintSettings> settings = ConstructPrintSettings(); printing::PrinterSemanticCapsAndDefaults capabilities = diff --git a/chromium/chrome/browser/extensions/api/printing/printing_apitest.cc b/chromium/chrome/browser/extensions/api/printing/printing_apitest.cc index 9f5bdc0364c..544f7370dab 100644 --- a/chromium/chrome/browser/extensions/api/printing/printing_apitest.cc +++ b/chromium/chrome/browser/extensions/api/printing/printing_apitest.cc @@ -61,10 +61,10 @@ ConstructPrinterCapabilities() { capabilities->duplex_modes.push_back(printing::mojom::DuplexMode::kSimplex); capabilities->copies_max = 2; capabilities->dpis.emplace_back(kHorizontalDpi, kVerticalDpi); - printing::PrinterSemanticCapsAndDefaults::Paper paper; - paper.vendor_id = kMediaSizeVendorId; - paper.size_um = gfx::Size(kMediaSizeWidth, kMediaSizeHeight); - capabilities->papers.push_back(paper); + printing::PrinterSemanticCapsAndDefaults::Paper paper( + /*display_name=*/"", kMediaSizeVendorId, + {kMediaSizeWidth, kMediaSizeHeight}); + capabilities->papers.push_back(std::move(paper)); capabilities->collate_capable = true; return capabilities; } diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc index 5ecb6201024..0d43a09f5d0 100644 --- a/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc +++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc @@ -72,13 +72,10 @@ void ProxyEventRouter::OnPACScriptError(EventRouterForwarder* event_router, base::Value::Dict dict; dict.Set(kProxyEventFatalKey, false); dict.Set(kProxyEventErrorKey, net::ErrorToString(net::ERR_PAC_SCRIPT_FAILED)); - std::string error_msg; + std::string error_msg = base::UTF16ToUTF8(error); if (line_number != -1) { - base::SStringPrintf(&error_msg, - "line: %d: %s", - line_number, base::UTF16ToUTF8(error).c_str()); - } else { - error_msg = base::UTF16ToUTF8(error); + error_msg = + base::StringPrintf("line: %d: %s", line_number, error_msg.c_str()); } dict.Set(kProxyEventDetailsKey, error_msg); args.Append(base::Value(std::move(dict))); diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc index 26e38d794e3..0ce8aed91a4 100644 --- a/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc +++ b/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc @@ -34,6 +34,15 @@ const char kNoServer[] = ""; const char kNoBypass[] = ""; const char kNoPac[] = ""; +#if BUILDFLAG(IS_CHROMEOS_LACROS) +bool IsLacrosServiceSyncingProxyPref() { + static constexpr int kMinVersionProxyPolicy = 4; + const int version = chromeos::LacrosService::Get() + ->GetInterfaceVersion<crosapi::mojom::Prefs>(); + return version >= kMinVersionProxyPolicy; +} +#endif + } // namespace class ProxySettingsApiTest : public ExtensionApiTest { @@ -50,10 +59,22 @@ class ProxySettingsApiTest : public ExtensionApiTest { // used for all tests in the target. Setting a proxy will prevent other // tests which require a direct connection to complete successfully. auto* lacros_service = chromeos::LacrosService::Get(); - if (lacros_service && - lacros_service->IsAvailable<crosapi::mojom::NetworkSettingsService>()) { - lacros_service->GetRemote<crosapi::mojom::NetworkSettingsService>() - ->ClearExtensionProxy(); + if (!lacros_service) { + ExtensionApiTest::TearDownOnMainThread(); + return; + } + if (IsLacrosServiceSyncingProxyPref()) { + if (lacros_service->IsAvailable<crosapi::mojom::Prefs>()) { + lacros_service->GetRemote<crosapi::mojom::Prefs>() + ->ClearExtensionControlledPref(crosapi::mojom::PrefPath::kProxy, + base::DoNothing()); + } + } else { + if (lacros_service + ->IsAvailable<crosapi::mojom::NetworkSettingsService>()) { + lacros_service->GetRemote<crosapi::mojom::NetworkSettingsService>() + ->ClearExtensionProxy(); + } } ExtensionApiTest::TearDownOnMainThread(); } diff --git a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc index 97f35f6a112..82f7ff830fb 100644 --- a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc +++ b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.cc @@ -24,11 +24,13 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chromeos/ash/components/login/auth/public/authentication_error.h" #include "chromeos/ash/components/login/auth/public/user_context.h" +#include "chromeos/ash/components/osauth/public/auth_session_storage.h" #include "components/prefs/pref_service.h" #include "components/user_manager/user_manager.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/event_router.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace extensions { @@ -201,11 +203,31 @@ Profile* GetActiveProfile(content::BrowserContext* browser_context) { } AuthToken* GetActiveProfileAuthToken(content::BrowserContext* browser_context) { + CHECK(!ash::features::ShouldUseAuthSessionStorage()); return ash::quick_unlock::QuickUnlockFactory::GetForProfile( GetActiveProfile(browser_context)) ->GetAuthToken(); } +absl::optional<std::string> CheckTokenValidity( + content::BrowserContext* browser_context, + const std::string& token) { + if (ash::features::ShouldUseAuthSessionStorage()) { + if (!ash::AuthSessionStorage::Get()->IsValid(token)) { + return kAuthTokenExpired; + } + } else { + AuthToken* auth_token = GetActiveProfileAuthToken(browser_context); + if (!auth_token) { + return kAuthTokenExpired; + } + if (token != auth_token->Identifier()) { + return kAuthTokenInvalid; + } + } + return absl::nullopt; +} + } // namespace // quickUnlockPrivate.getAuthToken @@ -260,11 +282,11 @@ ExtensionFunction::ResponseAction QuickUnlockPrivateSetLockScreenEnabledFunction::Run() { auto params = quick_unlock_private::SetLockScreenEnabled::Params::Create(args()); - AuthToken* auth_token = GetActiveProfileAuthToken(browser_context()); - if (!auth_token) - return RespondNow(Error(kAuthTokenExpired)); - if (params->token != auth_token->Identifier()) - return RespondNow(Error(kAuthTokenInvalid)); + absl::optional<std::string> error = + CheckTokenValidity(browser_context(), params->token); + if (error.has_value()) { + return RespondNow(Error(error.value())); + } GetActiveProfile(browser_context()) ->GetPrefs() @@ -288,11 +310,11 @@ QuickUnlockPrivateSetPinAutosubmitEnabledFunction::Run() { auto params = quick_unlock_private::SetPinAutosubmitEnabled::Params::Create(args()); - AuthToken* auth_token = GetActiveProfileAuthToken(browser_context()); - if (!auth_token) - return RespondNow(Error(kAuthTokenExpired)); - if (params->token != auth_token->Identifier()) - return RespondNow(Error(kAuthTokenInvalid)); + absl::optional<std::string> error = + CheckTokenValidity(browser_context(), params->token); + if (error.has_value()) { + return RespondNow(Error(error.value())); + } Profile* profile = GetActiveProfile(browser_context()); user_manager::User* user = @@ -324,10 +346,6 @@ QuickUnlockPrivateCanAuthenticatePinFunction:: ExtensionFunction::ResponseAction QuickUnlockPrivateCanAuthenticatePinFunction::Run() { - AuthToken* auth_token = GetActiveProfileAuthToken(browser_context()); - if (!auth_token) - return RespondNow(Error(kAuthTokenExpired)); - Profile* profile = GetActiveProfile(browser_context()); user_manager::User* user = ash::ProfileHelper::Get()->GetUserByProfile(profile); @@ -490,11 +508,11 @@ ExtensionFunction::ResponseAction QuickUnlockPrivateSetModesFunction::Run() { if (params_->modes.size() > 1) return RespondNow(Error(kMultipleModesNotSupported)); - AuthToken* auth_token = GetActiveProfileAuthToken(browser_context()); - if (!auth_token) - return RespondNow(Error(kAuthTokenExpired)); - if (params_->token != auth_token->Identifier()) - return RespondNow(Error(kAuthTokenInvalid)); + absl::optional<std::string> error = + CheckTokenValidity(browser_context(), params_->token); + if (error.has_value()) { + return RespondNow(Error(error.value())); + } // Verify every credential is valid based on policies. PrefService* pref_service = GetActiveProfile(browser_context())->GetPrefs(); diff --git a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc index 72b99b2dd5c..ef287bff9e0 100644 --- a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api_unittest.cc @@ -7,6 +7,7 @@ #include "chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_api.h" #include <memory> +#include <utility> #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" @@ -44,6 +45,8 @@ #include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h" #include "chromeos/ash/components/login/auth/fake_extended_authenticator.h" #include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h" +#include "chromeos/ash/components/osauth/impl/auth_parts_impl.h" +#include "chromeos/ash/components/osauth/impl/auth_session_storage_impl.h" #include "chromeos/ash/services/device_sync/public/cpp/fake_device_sync_client.h" #include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h" #include "chromeos/ash/services/secure_channel/public/cpp/client/fake_secure_channel_client.h" @@ -202,6 +205,10 @@ class QuickUnlockPrivateUnitTest fake_user_manager_ = fake_user_manager.get(); scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>( std::move(fake_user_manager)); + auth_parts_ = ash::AuthPartsImpl::CreateTestInstance(); + auth_parts_->SetAuthSessionStorage( + std::make_unique<ash::AuthSessionStorageImpl>( + ash::UserDataAuthClient::Get())); ExtensionApiUnittest::SetUp(); @@ -262,8 +269,14 @@ class QuickUnlockPrivateUnitTest ash::AuthFactorsConfiguration()); } - token_ = ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile) - ->CreateAuthToken(auth_token_user_context_); + if (ash::features::ShouldUseAuthSessionStorage()) { + token_ = ash::AuthSessionStorage::Get()->Store( + std::make_unique<ash::UserContext>(auth_token_user_context_)); + } else { + token_ = ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile) + ->CreateAuthToken(auth_token_user_context_); + } + base::RunLoop().RunUntilIdle(); return profile; @@ -639,7 +652,8 @@ class QuickUnlockPrivateUnitTest } base::test::ScopedFeatureList feature_list_; - raw_ptr<sync_preferences::TestingPrefServiceSyncable, ExperimentalAsh> + raw_ptr<sync_preferences::TestingPrefServiceSyncable, + DanglingUntriaged | ExperimentalAsh> test_pref_service_; private: @@ -677,6 +691,7 @@ class QuickUnlockPrivateUnitTest expect_modes_changed_ = false; } + std::unique_ptr<ash::AuthPartsImpl> auth_parts_; raw_ptr<ash::FakeChromeUserManager, ExperimentalAsh> fake_user_manager_ = nullptr; std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_; @@ -695,8 +710,12 @@ TEST_P(QuickUnlockPrivateUnitTest, GetAuthTokenValid) { ash::quick_unlock::QuickUnlockStorage* quick_unlock_storage = ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile()); - EXPECT_EQ(token_info->token, - quick_unlock_storage->GetAuthToken()->Identifier()); + if (ash::features::ShouldUseAuthSessionStorage()) { + EXPECT_TRUE(ash::AuthSessionStorage::Get()->IsValid(token_info->token)); + } else { + EXPECT_EQ(token_info->token, + quick_unlock_storage->GetAuthToken()->Identifier()); + } EXPECT_EQ(token_info->lifetime_seconds, ash::quick_unlock::AuthToken::kTokenExpiration.InSeconds()); } diff --git a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc index bf66ae0ca12..f9c9848c791 100644 --- a/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc +++ b/chromium/chrome/browser/extensions/api/quick_unlock_private/quick_unlock_private_ash_utils.cc @@ -14,11 +14,14 @@ #include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h" #include "chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h" #include "chrome/browser/ash/profiles/profile_helper.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/quick_unlock_private.h" #include "chromeos/ash/components/login/auth/auth_performer.h" #include "chromeos/ash/components/login/auth/extended_authenticator.h" #include "chromeos/ash/components/login/auth/public/user_context.h" +#include "chromeos/ash/components/osauth/public/auth_session_storage.h" +#include "components/user_manager/known_user.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -75,8 +78,15 @@ void LegacyQuickUnlockPrivateGetAuthTokenHelper::OnAuthSuccess( QuickUnlockStorage* quick_unlock_storage = ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile_); quick_unlock_storage->MarkStrongAuth(); - token_info->token = quick_unlock_storage->CreateAuthToken(user_context); - token_info->lifetime_seconds = AuthToken::kTokenExpiration.InSeconds(); + if (ash::features::ShouldUseAuthSessionStorage()) { + token_info->token = ash::AuthSessionStorage::Get()->Store( + std::make_unique<ash::UserContext>(user_context)); + // TODO(b/238606050): Determine authsession lifetime. + token_info->lifetime_seconds = AuthToken::kTokenExpiration.InSeconds(); + } else { + token_info->token = quick_unlock_storage->CreateAuthToken(user_context); + token_info->lifetime_seconds = AuthToken::kTokenExpiration.InSeconds(); + } // The user has successfully authenticated, so we should reset pin/fingerprint // attempt counts. @@ -93,7 +103,8 @@ QuickUnlockPrivateGetAuthTokenHelper::QuickUnlockPrivateGetAuthTokenHelper( std::string password) : profile_(profile), password_(std::move(password)), - auth_performer_(ash::UserDataAuthClient::Get()) {} + auth_performer_(ash::UserDataAuthClient::Get()), + auth_factor_editor_(ash::UserDataAuthClient::Get()) {} QuickUnlockPrivateGetAuthTokenHelper::~QuickUnlockPrivateGetAuthTokenHelper() = default; @@ -137,8 +148,9 @@ void QuickUnlockPrivateGetAuthTokenHelper::OnAuthSessionStarted( return; } - const cryptohome::AuthFactor* password_factor = - user_context->GetAuthFactorsData().FindOnlinePasswordFactor(); + const auto* password_factor = + user_context->GetAuthFactorsData().FindFactorByType( + cryptohome::AuthFactorType::kPassword); if (!password_factor) { LOG(ERROR) << "Could not find password key"; std::move(callback).Run( @@ -188,6 +200,14 @@ void QuickUnlockPrivateGetAuthTokenHelper::OnAuthFactorsConfiguration( return; } + // The user context stored in quick_unlock storage must have a device ID, so + // we retrieve and set it here. + user_manager::KnownUser known_user{g_browser_process->local_state()}; + std::string device_id = known_user.GetDeviceId(user_context->GetAccountId()); + LOG_IF(WARNING, device_id.empty()) + << "Missing DeviceID for auth factor edits"; + user_context->SetDeviceId(std::move(device_id)); + QuickUnlockStorage* quick_unlock_storage = ash::quick_unlock::QuickUnlockFactory::GetForProfile(profile_); quick_unlock_storage->MarkStrongAuth(); @@ -197,9 +217,16 @@ void QuickUnlockPrivateGetAuthTokenHelper::OnAuthFactorsConfiguration( quick_unlock_storage->fingerprint_storage()->ResetUnlockAttemptCount(); TokenInfo token_info; - token_info.token = - quick_unlock_storage->CreateAuthToken(std::move(*user_context)); - token_info.lifetime_seconds = AuthToken::kTokenExpiration.InSeconds(); + if (ash::features::ShouldUseAuthSessionStorage()) { + token_info.token = + ash::AuthSessionStorage::Get()->Store(std::move(user_context)); + // TODO(b/238606050): Determine authsession lifetime. + token_info.lifetime_seconds = AuthToken::kTokenExpiration.InSeconds(); + } else { + token_info.token = + quick_unlock_storage->CreateAuthToken(std::move(*user_context)); + token_info.lifetime_seconds = AuthToken::kTokenExpiration.InSeconds(); + } std::move(callback).Run(std::move(token_info), absl::nullopt); } diff --git a/chromium/chrome/browser/extensions/api/reading_list/OWNERS b/chromium/chrome/browser/extensions/api/reading_list/OWNERS new file mode 100644 index 00000000000..a3823270d04 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/OWNERS @@ -0,0 +1,2 @@ +dljames@chromium.org +dpenning@chromium.org diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.cc new file mode 100644 index 00000000000..6fad2e28c28 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.cc @@ -0,0 +1,266 @@ +// Copyright 2023 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/extensions/api/reading_list/reading_list_api.h" + +#include "base/containers/flat_set.h" +#include "base/time/time.h" +#include "chrome/browser/extensions/api/reading_list/reading_list_api_constants.h" +#include "chrome/browser/extensions/api/reading_list/reading_list_util.h" +#include "chrome/browser/reading_list/reading_list_model_factory.h" +#include "chrome/common/extensions/api/reading_list.h" +#include "components/reading_list/core/reading_list_entry.h" +#include "components/reading_list/core/reading_list_model.h" +#include "components/reading_list/core/reading_list_model_observer.h" +#include "extensions/browser/extension_function.h" +#include "url/gurl.h" + +namespace extensions { + +////////////////////////////////////////////////////////////////////////////// +/////////////////////// ReadingListAddEntryFunction ////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +ReadingListAddEntryFunction::ReadingListAddEntryFunction() = default; +ReadingListAddEntryFunction::~ReadingListAddEntryFunction() = default; + +ExtensionFunction::ResponseAction ReadingListAddEntryFunction::Run() { + auto params = api::reading_list::AddEntry::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + + title_ = std::move(params->entry.title); + url_ = GURL(params->entry.url); + has_been_read_ = params->entry.has_been_read; + + if (!url_.is_valid()) { + return RespondNow(Error(reading_list_api_constants::kInvalidURLError)); + } + + reading_list_model_ = + ReadingListModelFactory::GetForBrowserContext(browser_context()); + + if (!reading_list_model_->loaded()) { + reading_list_observation_.Observe(reading_list_model_); + AddRef(); + return RespondLater(); + } + + auto response = AddEntryToReadingList(); + return RespondNow(std::move(response)); +} + +void ReadingListAddEntryFunction::ReadingListModelLoaded( + const ReadingListModel* model) { + reading_list_observation_.Reset(); + auto response = AddEntryToReadingList(); + Respond(std::move(response)); + Release(); // Balanced in Run(). +} + +ExtensionFunction::ResponseValue +ReadingListAddEntryFunction::AddEntryToReadingList() { + if (!reading_list_model_->IsUrlSupported(url_)) { + return Error(reading_list_api_constants::kNotSupportedURLError); + } + + if (reading_list_model_->GetEntryByURL(url_)) { + return Error(reading_list_api_constants::kDuplicateURLError); + } + + reading_list_model_->AddOrReplaceEntry( + url_, title_, reading_list::EntrySource::ADDED_VIA_EXTENSION, + /*estimated_read_time=*/base::TimeDelta()); + reading_list_model_->SetReadStatusIfExists(url_, has_been_read_); + + return NoArguments(); +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////// ReadingListRemoveEntryFunction ///////////////////////// +////////////////////////////////////////////////////////////////////////////// + +ReadingListRemoveEntryFunction::ReadingListRemoveEntryFunction() = default; +ReadingListRemoveEntryFunction::~ReadingListRemoveEntryFunction() = default; + +ExtensionFunction::ResponseAction ReadingListRemoveEntryFunction::Run() { + auto params = api::reading_list::RemoveEntry::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + + url_ = GURL(params->info.url); + if (!url_.is_valid()) { + return RespondNow(Error(reading_list_api_constants::kInvalidURLError)); + } + + reading_list_model_ = + ReadingListModelFactory::GetForBrowserContext(browser_context()); + + if (!reading_list_model_->loaded()) { + reading_list_observation_.Observe(reading_list_model_); + AddRef(); + return RespondLater(); + } + + auto response = RemoveEntryFromReadingList(); + return RespondNow(std::move(response)); +} + +void ReadingListRemoveEntryFunction::ReadingListModelLoaded( + const ReadingListModel* model) { + reading_list_observation_.Reset(); + auto response = RemoveEntryFromReadingList(); + Respond(std::move(response)); + Release(); // Balanced in Run(). +} + +ExtensionFunction::ResponseValue +ReadingListRemoveEntryFunction::RemoveEntryFromReadingList() { + if (!reading_list_model_->IsUrlSupported(url_)) { + return Error(reading_list_api_constants::kNotSupportedURLError); + } + + if (!reading_list_model_->GetEntryByURL(url_)) { + return Error(reading_list_api_constants::kURLNotFoundError); + } + + reading_list_model_->RemoveEntryByURL(url_); + + return NoArguments(); +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////// ReadingListUpdateEntryFunction ///////////////////////// +////////////////////////////////////////////////////////////////////////////// + +ReadingListUpdateEntryFunction::ReadingListUpdateEntryFunction() = default; +ReadingListUpdateEntryFunction::~ReadingListUpdateEntryFunction() = default; + +ExtensionFunction::ResponseAction ReadingListUpdateEntryFunction::Run() { + auto params = api::reading_list::UpdateEntry::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + + title_ = params->info.title; + has_been_read_ = params->info.has_been_read; + + if (!title_.has_value() && !has_been_read_.has_value()) { + return RespondNow(Error(reading_list_api_constants::kNoUpdateProvided)); + } + + url_ = GURL(params->info.url); + if (!url_.is_valid()) { + return RespondNow(Error(reading_list_api_constants::kInvalidURLError)); + } + + reading_list_model_ = + ReadingListModelFactory::GetForBrowserContext(browser_context()); + + if (!reading_list_model_->loaded()) { + reading_list_observation_.Observe(reading_list_model_); + AddRef(); + return RespondLater(); + } + + auto response = UpdateEntriesInTheReadingList(); + return RespondNow(std::move(response)); +} + +void ReadingListUpdateEntryFunction::ReadingListModelLoaded( + const ReadingListModel* model) { + reading_list_observation_.Reset(); + auto response = UpdateEntriesInTheReadingList(); + Respond(std::move(response)); + Release(); // Balanced in Run(). +} + +ExtensionFunction::ResponseValue +ReadingListUpdateEntryFunction::UpdateEntriesInTheReadingList() { + if (!reading_list_model_->IsUrlSupported(url_)) { + return Error(reading_list_api_constants::kNotSupportedURLError); + } + + if (!reading_list_model_->GetEntryByURL(url_)) { + return Error(reading_list_api_constants::kURLNotFoundError); + } + + if (title_.has_value()) { + reading_list_model_->SetEntryTitleIfExists(url_, title_.value()); + } + + if (has_been_read_.has_value()) { + reading_list_model_->SetReadStatusIfExists(url_, has_been_read_.value()); + } + + return NoArguments(); +} + +////////////////////////////////////////////////////////////////////////////// +///////////////////////// ReadingListQueryFunction /////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +ReadingListQueryFunction::ReadingListQueryFunction() = default; +ReadingListQueryFunction::~ReadingListQueryFunction() = default; + +ExtensionFunction::ResponseAction ReadingListQueryFunction::Run() { + auto params = api::reading_list::Query::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + + if (params->info.url.has_value()) { + url_ = GURL(params->info.url.value()); + if (!url_->is_valid()) { + return RespondNow(Error(reading_list_api_constants::kInvalidURLError)); + } + } + + title_ = params->info.title; + has_been_read_ = params->info.has_been_read; + + reading_list_model_ = + ReadingListModelFactory::GetForBrowserContext(browser_context()); + + if (!reading_list_model_->loaded()) { + reading_list_observation_.Observe(reading_list_model_); + AddRef(); + return RespondLater(); + } + + auto response = MatchEntries(); + return RespondNow(std::move(response)); +} + +void ReadingListQueryFunction::ReadingListModelLoaded( + const ReadingListModel* model) { + reading_list_observation_.Reset(); + auto response = MatchEntries(); + Respond(std::move(response)); + Release(); // Balanced in Run(). +} + +ExtensionFunction::ResponseValue ReadingListQueryFunction::MatchEntries() { + if (url_.has_value() && !reading_list_model_->IsUrlSupported(url_.value())) { + return Error(reading_list_api_constants::kNotSupportedURLError); + } + + base::flat_set<GURL> urls = reading_list_model_->GetKeys(); + std::vector<api::reading_list::ReadingListEntry> matching_entries; + + for (const auto& url : urls) { + scoped_refptr<const ReadingListEntry> entry = + reading_list_model_->GetEntryByURL(url); + + if (url_.has_value() && entry->URL() != url_) { + continue; + } + if (title_.has_value() && entry->Title() != title_) { + continue; + } + if (has_been_read_.has_value() && entry->IsRead() != has_been_read_) { + continue; + } + matching_entries.emplace_back(reading_list_util::ParseEntry(*entry)); + } + + return ArgumentList( + api::reading_list::Query::Results::Create(std::move(matching_entries))); +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.h b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.h new file mode 100644 index 00000000000..74288697206 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api.h @@ -0,0 +1,133 @@ +// Copyright 2023 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_EXTENSIONS_API_READING_LIST_READING_LIST_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_H_ + +#include "base/scoped_observation.h" +#include "components/reading_list/core/reading_list_model.h" +#include "components/reading_list/core/reading_list_model_observer.h" +#include "extensions/browser/extension_function.h" + +namespace extensions { + +class ReadingListAddEntryFunction : public ExtensionFunction, + public ReadingListModelObserver { + public: + DECLARE_EXTENSION_FUNCTION("readingList.addEntry", READINGLIST_ADDENTRY) + + ReadingListAddEntryFunction(); + ReadingListAddEntryFunction(const ReadingListAddEntryFunction&) = delete; + ReadingListAddEntryFunction& operator=(const ReadingListAddEntryFunction&) = + delete; + + // ExtensionFunction: + ResponseAction Run() override; + + private: + ~ReadingListAddEntryFunction() override; + + ResponseValue AddEntryToReadingList(); + + // ReadingListModelObserver: + void ReadingListModelLoaded(const ReadingListModel* model) override; + + base::ScopedObservation<ReadingListModel, ReadingListModelObserver> + reading_list_observation_{this}; + raw_ptr<ReadingListModel> reading_list_model_; + GURL url_; + std::string title_; + bool has_been_read_; +}; + +class ReadingListRemoveEntryFunction : public ExtensionFunction, + public ReadingListModelObserver { + public: + DECLARE_EXTENSION_FUNCTION("readingList.removeEntry", READINGLIST_REMOVEENTRY) + + ReadingListRemoveEntryFunction(); + ReadingListRemoveEntryFunction(const ReadingListRemoveEntryFunction&) = + delete; + ReadingListRemoveEntryFunction& operator=( + const ReadingListRemoveEntryFunction&) = delete; + + // ExtensionFunction: + ResponseAction Run() override; + + private: + ~ReadingListRemoveEntryFunction() override; + + ResponseValue RemoveEntryFromReadingList(); + + // ReadingListModelObserver: + void ReadingListModelLoaded(const ReadingListModel* model) override; + + base::ScopedObservation<ReadingListModel, ReadingListModelObserver> + reading_list_observation_{this}; + raw_ptr<ReadingListModel> reading_list_model_; + GURL url_; +}; + +class ReadingListUpdateEntryFunction : public ExtensionFunction, + public ReadingListModelObserver { + public: + DECLARE_EXTENSION_FUNCTION("readingList.updateEntry", READINGLIST_UPDATEENTRY) + + ReadingListUpdateEntryFunction(); + ReadingListUpdateEntryFunction(const ReadingListUpdateEntryFunction&) = + delete; + ReadingListUpdateEntryFunction& operator=( + const ReadingListUpdateEntryFunction&) = delete; + + // ExtensionFunction: + ResponseAction Run() override; + + private: + ~ReadingListUpdateEntryFunction() override; + + ResponseValue UpdateEntriesInTheReadingList(); + + // ReadingListModelObserver: + void ReadingListModelLoaded(const ReadingListModel* model) override; + + base::ScopedObservation<ReadingListModel, ReadingListModelObserver> + reading_list_observation_{this}; + raw_ptr<ReadingListModel> reading_list_model_; + GURL url_; + absl::optional<std::string> title_; + absl::optional<bool> has_been_read_; +}; + +class ReadingListQueryFunction : public ExtensionFunction, + public ReadingListModelObserver { + public: + DECLARE_EXTENSION_FUNCTION("readingList.query", READINGLIST_QUERY) + + ReadingListQueryFunction(); + ReadingListQueryFunction(const ReadingListQueryFunction&) = delete; + ReadingListQueryFunction& operator=(const ReadingListQueryFunction&) = delete; + + // ExtensionFunction: + ResponseAction Run() override; + + private: + ~ReadingListQueryFunction() override; + + // Returns the entries that match the provided features. + ResponseValue MatchEntries(); + + // ReadingListModelObserver: + void ReadingListModelLoaded(const ReadingListModel* model) override; + + base::ScopedObservation<ReadingListModel, ReadingListModelObserver> + reading_list_observation_{this}; + raw_ptr<ReadingListModel> reading_list_model_; + absl::optional<GURL> url_; + absl::optional<std::string> title_; + absl::optional<bool> has_been_read_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_H_ diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.cc new file mode 100644 index 00000000000..f96e06c941b --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.cc @@ -0,0 +1,17 @@ +// Copyright 2023 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/extensions/api/reading_list/reading_list_api_constants.h" + +namespace extensions::reading_list_api_constants { + +// Error messages. +const char kInvalidURLError[] = "URL is not valid."; +const char kNotSupportedURLError[] = "URL is not supported."; +const char kDuplicateURLError[] = "Duplicate URL."; +const char kURLNotFoundError[] = "URL not found."; +const char kNoUpdateProvided[] = + "At least one of `title` or `hasBeenRead` must be provided."; + +} // namespace extensions::reading_list_api_constants diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.h b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.h new file mode 100644 index 00000000000..6a384f89d10 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_constants.h @@ -0,0 +1,20 @@ +// Copyright 2023 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_EXTENSIONS_API_READING_LIST_READING_LIST_API_CONSTANTS_H_ +#define CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_CONSTANTS_H_ + +// Constants used for the Reading List API. +namespace extensions::reading_list_api_constants { + +// Error messages. +extern const char kInvalidURLError[]; +extern const char kNotSupportedURLError[]; +extern const char kDuplicateURLError[]; +extern const char kURLNotFoundError[]; +extern const char kNoUpdateProvided[]; + +} // namespace extensions::reading_list_api_constants + +#endif // CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_API_CONSTANTS_H_ diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_unittest.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_unittest.cc new file mode 100644 index 00000000000..de1b24b9173 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_api_unittest.cc @@ -0,0 +1,538 @@ +// Copyright 2023 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/extensions/api/reading_list/reading_list_api.h" + +#include <memory> + +#include "base/test/values_test_util.h" +#include "base/time/time.h" +#include "chrome/browser/extensions/api/reading_list/reading_list_api_constants.h" +#include "chrome/browser/extensions/api/reading_list/reading_list_event_router.h" +#include "chrome/browser/extensions/extension_service_test_base.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/reading_list/reading_list_model_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/extensions/api/reading_list.h" +#include "chrome/test/base/test_browser_window.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/reading_list/core/reading_list_entry.h" +#include "components/reading_list/core/reading_list_model.h" +#include "components/reading_list/core/reading_list_test_utils.h" +#include "components/version_info/channel.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/api_test_utils.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/event_router_factory.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/test_event_router_observer.h" +#include "extensions/common/extension_builder.h" +#include "extensions/common/features/feature_channel.h" +#include "url/gurl.h" + +namespace extensions { + +namespace { + +// Create an extension with "readingList" permission. +scoped_refptr<const Extension> CreateReadingListExtension() { + return ExtensionBuilder("Extension with readingList permission") + .AddPermission("readingList") + .Build(); +} + +void AddReadingListEntry(ReadingListModel* reading_list_model, + const GURL& url, + const std::string& title, + bool has_been_read) { + reading_list_model->AddOrReplaceEntry( + url, title, reading_list::EntrySource::ADDED_VIA_CURRENT_APP, + base::TimeDelta()); + reading_list_model->SetReadStatusIfExists(url, has_been_read); +} + +std::unique_ptr<KeyedService> BuildReadingListEventRouter( + content::BrowserContext* context) { + return std::make_unique<ReadingListEventRouter>(context); +} + +std::unique_ptr<KeyedService> BuildEventRouter( + content::BrowserContext* context) { + return std::make_unique<extensions::EventRouter>( + context, ExtensionPrefs::Get(context)); +} + +} // namespace + +class ReadingListApiUnitTest : public ExtensionServiceTestBase { + public: + ReadingListApiUnitTest() = default; + ReadingListApiUnitTest(const ReadingListApiUnitTest&) = delete; + ReadingListApiUnitTest& operator=(const ReadingListApiUnitTest&) = delete; + ~ReadingListApiUnitTest() override = default; + + protected: + Browser* browser() { return browser_.get(); } + TestBrowserWindow* browser_window() { return browser_window_.get(); } + + private: + void SetUp() override; + void TearDown() override; + + std::unique_ptr<TestBrowserWindow> browser_window_; + std::unique_ptr<Browser> browser_; + ScopedCurrentChannel channel_{version_info::Channel::UNKNOWN}; +}; + +void ReadingListApiUnitTest::SetUp() { + ExtensionServiceTestBase::SetUp(); + InitializeEmptyExtensionService(); + + // Create a browser window. + browser_window_ = std::make_unique<TestBrowserWindow>(); + Browser::CreateParams params(profile(), /*user_gesture*/ true); + params.type = Browser::TYPE_NORMAL; + params.window = browser_window_.get(); + browser_ = std::unique_ptr<Browser>(Browser::Create(params)); + + ReadingListEventRouter::GetFactoryInstance()->SetTestingFactory( + browser_context(), base::BindRepeating(&BuildReadingListEventRouter)); + + EventRouterFactory::GetInstance()->SetTestingFactory( + browser_context(), base::BindRepeating(&BuildEventRouter)); + + // We need to call ReadingListEventRouterFactory::GetForProfile() in order to + // instantiate the keyed service, since it's not created by default in unit + // tests. + ReadingListEventRouter::Get(browser_context()); +} + +void ReadingListApiUnitTest::TearDown() { + browser_->tab_strip_model()->CloseAllTabs(); + browser_.reset(); + browser_window_.reset(); + ExtensionServiceTestBase::TearDown(); +} + +// Test that it is possible to add a unique URL. +TEST_F(ReadingListApiUnitTest, AddUniqueURL) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + static constexpr char kArgs[] = + R"([{ + "url": "https://www.example.com", + "title": "example of title", + "hasBeenRead": false + }])"; + auto function = base::MakeRefCounted<ReadingListAddEntryFunction>(); + function->set_extension(extension); + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + // Add the entry. + api_test_utils::RunFunction(function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + + EXPECT_EQ(reading_list_model->size(), 1u); + + // Verify the features of the entry. + GURL url = GURL("https://www.example.com"); + auto entry = reading_list_model->GetEntryByURL(url); + EXPECT_EQ(entry->URL(), url); + EXPECT_EQ(entry->Title(), "example of title"); + EXPECT_FALSE(entry->IsRead()); +} + +// Test that it is possible to add an already read entry. +TEST_F(ReadingListApiUnitTest, AddEntryThatHasBeenRead) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + static constexpr char kArgs[] = + R"([{ + "url": "https://www.example.com", + "title": "example of title", + "hasBeenRead": true + }])"; + auto function = base::MakeRefCounted<ReadingListAddEntryFunction>(); + function->set_extension(extension); + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + // Add the entry. + api_test_utils::RunFunction(function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + + EXPECT_EQ(reading_list_model->size(), 1u); + + // Verify the features of the entry. + GURL url = GURL("https://www.example.com"); + auto entry = reading_list_model->GetEntryByURL(url); + EXPECT_EQ(entry->URL(), url); + EXPECT_EQ(entry->Title(), "example of title"); + EXPECT_TRUE(entry->IsRead()); +} + +// Test that adding a duplicate URL generates an error. +TEST_F(ReadingListApiUnitTest, AddDuplicateURL) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + static constexpr char kArgs[] = + R"([{ + "url": "https://www.example.com", + "title": "example of title", + "hasBeenRead": false + }])"; + auto function = base::MakeRefCounted<ReadingListAddEntryFunction>(); + function->set_extension(extension); + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + // Add the entry. + api_test_utils::RunFunction(function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + + EXPECT_EQ(reading_list_model->size(), 1u); + + // Verify the features of the entry. + GURL url = GURL("https://www.example.com"); + auto entry = reading_list_model->GetEntryByURL(url); + EXPECT_EQ(entry->URL(), url); + EXPECT_EQ(entry->Title(), "example of title"); + EXPECT_FALSE(entry->IsRead()); + + // Try to add a duplicate URL and expect an error. + function = base::MakeRefCounted<ReadingListAddEntryFunction>(); + function->set_extension(extension); + std::string error = api_test_utils::RunFunctionAndReturnError( + function.get(), kArgs, profile(), api_test_utils::FunctionMode::kNone); + EXPECT_EQ(error, reading_list_api_constants::kDuplicateURLError); + + // Review that the URL added earlier still exists and there is only 1 entry in + // the Reading List. + EXPECT_EQ(reading_list_model->size(), 1u); + entry = reading_list_model->GetEntryByURL(url); + EXPECT_EQ(entry->URL(), url); + EXPECT_EQ(entry->Title(), "example of title"); + EXPECT_FALSE(entry->IsRead()); +} + +// Test that it is possible to remove a URL. +TEST_F(ReadingListApiUnitTest, RemoveURL) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + AddReadingListEntry(reading_list_model, GURL("https://www.example.com"), + "example of title", /*has_been_read=*/false); + + // Verify that the entry has been added. + EXPECT_EQ(reading_list_model->size(), 1u); + + // Remove the URL that was added before. + auto remove_function = base::MakeRefCounted<ReadingListRemoveEntryFunction>(); + remove_function->set_extension(extension); + static constexpr char kArgs[] = + R"([{ + "url": "https://www.example.com" + }])"; + api_test_utils::RunFunction(remove_function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + + // Verify the size of the reading list model. + EXPECT_EQ(reading_list_model->size(), 0u); +} + +// Test that trying to remove a URL that is not in the Reading List, generates +// an error. +TEST_F(ReadingListApiUnitTest, RemoveNonExistentURL) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + static constexpr char kArgs[] = + R"([{ + "url": "https://www.example.com" + }])"; + auto function = base::MakeRefCounted<ReadingListRemoveEntryFunction>(); + function->set_extension(extension); + + // Remove the entry. + std::string error = api_test_utils::RunFunctionAndReturnError( + function.get(), kArgs, profile(), api_test_utils::FunctionMode::kNone); + EXPECT_EQ(error, reading_list_api_constants::kURLNotFoundError); +} + +// Test that it is possible to update the features of an entry. +TEST_F(ReadingListApiUnitTest, UpdateEntryFeatures) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + AddReadingListEntry(reading_list_model, GURL("https://www.example.com"), + "example of title", /*has_been_read=*/false); + + // Verify that the entry has been added. + EXPECT_EQ(reading_list_model->size(), 1u); + + // Update the entry that was added before. + auto update_function = base::MakeRefCounted<ReadingListUpdateEntryFunction>(); + update_function->set_extension(extension); + static constexpr char kArgs[] = + R"([{ + "url": "https://www.example.com", + "title": "Title", + "hasBeenRead": true + }])"; + api_test_utils::RunFunction(update_function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + + // Verify that the size of the reading list model is still the same. + EXPECT_EQ(reading_list_model->size(), 1u); + + // Verify the features of the entry. + GURL url = GURL("https://www.example.com"); + auto entry = reading_list_model->GetEntryByURL(url); + EXPECT_EQ(entry->URL(), url); + EXPECT_EQ(entry->Title(), "Title"); + EXPECT_TRUE(entry->IsRead()); +} + +// Test that trying to update an entry by providing only the URL, generates an +// error. +TEST_F(ReadingListApiUnitTest, UpdateEntryOnlyWithTheURL) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + AddReadingListEntry(reading_list_model, GURL("https://www.example.com"), + "example of title", /*has_been_read=*/false); + + // Verify that the entry has been added. + EXPECT_EQ(reading_list_model->size(), 1u); + + // Update the entry that was added before. + auto update_function = base::MakeRefCounted<ReadingListUpdateEntryFunction>(); + update_function->set_extension(extension); + static constexpr char kArgs[] = + R"([{ + "url": "https://www.example.com", + }])"; + std::string error = api_test_utils::RunFunctionAndReturnError( + update_function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + EXPECT_EQ(error, reading_list_api_constants::kNoUpdateProvided); + + // Verify that the size of the reading list model is still the same. + EXPECT_EQ(reading_list_model->size(), 1u); + + // Verify the features of the entry. + GURL url = GURL("https://www.example.com"); + auto entry = reading_list_model->GetEntryByURL(url); + EXPECT_EQ(entry->URL(), url); + EXPECT_EQ(entry->Title(), "example of title"); + EXPECT_FALSE(entry->IsRead()); +} + +// Test that it is possible to retrieve all the entries. +TEST_F(ReadingListApiUnitTest, RetrieveAllEntries) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + AddReadingListEntry(reading_list_model, GURL("https://www.example.com"), + "example of title", /*has_been_read=*/false); + AddReadingListEntry(reading_list_model, GURL("https://www.example2.com"), + "Title #2", /*has_been_read=*/false); + + // Verify that the entries have been added. + EXPECT_EQ(reading_list_model->size(), 2u); + + // Retrieve all the entries in the Reading List. + auto update_function = base::MakeRefCounted<ReadingListQueryFunction>(); + update_function->set_extension(extension); + static constexpr char kArgs[] = "[{}]"; + + auto entries = api_test_utils::RunFunctionAndReturnSingleResult( + update_function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + + // Verify that all the entries were retrieved. + EXPECT_EQ(entries.value().GetList().size(), 2u); + + // Verify that the size of the reading list model is still the same. + EXPECT_EQ(reading_list_model->size(), 2u); +} + +// Test that it is possible to retrieve entries with certain features. +TEST_F(ReadingListApiUnitTest, RetrieveCertainEntries) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + AddReadingListEntry(reading_list_model, GURL("https://www.example.com"), + "example of title", /*has_been_read=*/false); + AddReadingListEntry(reading_list_model, GURL("https://www.example2.com"), + "Example", /*has_been_read=*/false); + AddReadingListEntry(reading_list_model, GURL("https://www.example3.com"), + "Example", /*has_been_read=*/false); + + // Verify that the entries have been added. + EXPECT_EQ(reading_list_model->size(), 3u); + + // Retrieve entries whose title is "Example". + auto update_function = base::MakeRefCounted<ReadingListQueryFunction>(); + update_function->set_extension(extension); + static constexpr char kArgs[] = + R"([{ + "title": "Example" + }])"; + auto entries = api_test_utils::RunFunctionAndReturnSingleResult( + update_function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + + // Verify only 2 entries were retrieved: example_2 and example_3. + ASSERT_EQ(entries.value().GetList().size(), 2u); + + scoped_refptr<const ReadingListEntry> e2 = + reading_list_model->GetEntryByURL(GURL("https://www.example2.com")); + scoped_refptr<const ReadingListEntry> e3 = + reading_list_model->GetEntryByURL(GURL("https://www.example3.com")); + + // Expect that the first entry is equivalent to `e2`. + absl::optional<api::reading_list::ReadingListEntry> entry1_actual = + api::reading_list::ReadingListEntry::FromValue(entries->GetList()[0]); + int64_t e2_update_in_milliseconds = + base::Microseconds(e2->UpdateTime()).InMilliseconds(); + int64_t e2_creation_in_milliseconds = + base::Microseconds(e2->CreationTime()).InMilliseconds(); + EXPECT_EQ(entry1_actual->url, e2->URL().spec()); + EXPECT_EQ(entry1_actual->title, e2->Title()); + EXPECT_EQ(entry1_actual->has_been_read, e2->IsRead()); + EXPECT_EQ(entry1_actual->last_update_time, e2_update_in_milliseconds); + EXPECT_EQ(entry1_actual->creation_time, e2_creation_in_milliseconds); + + // Expect that the second entry is equivalent to `e3`. + absl::optional<api::reading_list::ReadingListEntry> entry2_actual = + api::reading_list::ReadingListEntry::FromValue(entries->GetList()[1]); + int64_t e3_update_in_milliseconds = + base::Microseconds(e3->UpdateTime()).InMilliseconds(); + int64_t e3_creation_in_milliseconds = + base::Microseconds(e3->CreationTime()).InMilliseconds(); + EXPECT_EQ(entry2_actual->url, e3->URL().spec()); + EXPECT_EQ(entry2_actual->title, e3->Title()); + EXPECT_EQ(entry2_actual->has_been_read, e3->IsRead()); + EXPECT_EQ(entry2_actual->last_update_time, e3_update_in_milliseconds); + EXPECT_EQ(entry2_actual->creation_time, e3_creation_in_milliseconds); + + EXPECT_EQ(reading_list_model->size(), 3u); +} + +// Test that a query can return no matching entries. +TEST_F(ReadingListApiUnitTest, NoEntriesRetrieved) { + scoped_refptr<const Extension> extension = CreateReadingListExtension(); + + ReadingListModel* reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + AddReadingListEntry(reading_list_model, GURL("https://www.example.com"), + "example of title", /*has_been_read=*/false); + + // Query for an entry. + auto update_function = base::MakeRefCounted<ReadingListQueryFunction>(); + update_function->set_extension(extension); + static constexpr char kArgs[] = + R"([{ + "url": "https://www.example.com", + "title": "Title", + "hasBeenRead": false + }])"; + auto entries = api_test_utils::RunFunctionAndReturnSingleResult( + update_function.get(), kArgs, profile(), + api_test_utils::FunctionMode::kNone); + + EXPECT_EQ(entries.value().GetList().size(), 0u); +} + +// Test that adding an entry generates an event. +TEST_F(ReadingListApiUnitTest, ReadingListOnEntryAdded) { + TestEventRouterObserver event_observer(EventRouter::Get(browser_context())); + + ReadingListModel* const reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + AddReadingListEntry(reading_list_model, GURL("https://www.example.com"), + "example of title", /*has_been_read=*/false); + + EXPECT_EQ(reading_list_model->size(), 1u); + + EXPECT_TRUE(base::Contains(event_observer.events(), + api::reading_list::OnEntryAdded::kEventName)); +} + +// Test that removing an entry generates an event. +TEST_F(ReadingListApiUnitTest, ReadingListOnEntryRemoved) { + ReadingListModel* const reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + const GURL url = GURL("https://www.example.com"); + + AddReadingListEntry(reading_list_model, url, "example of title", + /*has_been_read=*/false); + EXPECT_EQ(reading_list_model->size(), 1u); + + TestEventRouterObserver event_observer(EventRouter::Get(browser_context())); + + reading_list_model->RemoveEntryByURL(url); + EXPECT_EQ(reading_list_model->size(), 0u); + + EXPECT_EQ(event_observer.events().size(), 1u); + EXPECT_TRUE(base::Contains(event_observer.events(), + api::reading_list::OnEntryRemoved::kEventName)); +} + +// Test that updating an entry generates an event. +TEST_F(ReadingListApiUnitTest, ReadingListOnEntryUpdated) { + ReadingListModel* const reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + + ReadingListLoadObserver(reading_list_model).Wait(); + + const GURL url = GURL("https://www.example.com"); + + AddReadingListEntry(reading_list_model, url, "example of title", + /*has_been_read=*/false); + EXPECT_EQ(reading_list_model->size(), 1u); + EXPECT_EQ(reading_list_model->GetEntryByURL(url)->Title(), + "example of title"); + + TestEventRouterObserver event_observer(EventRouter::Get(browser_context())); + + reading_list_model->SetEntryTitleIfExists(url, "New title"); + EXPECT_EQ(reading_list_model->GetEntryByURL(url)->Title(), "New title"); + + EXPECT_EQ(event_observer.events().size(), 1u); + EXPECT_TRUE(base::Contains(event_observer.events(), + api::reading_list::OnEntryUpdated::kEventName)); +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_apitest.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_apitest.cc new file mode 100644 index 00000000000..4bc8e8034bb --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_apitest.cc @@ -0,0 +1,67 @@ +// Copyright 2023 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/extensions/extension_apitest.h" +#include "chrome/browser/reading_list/reading_list_model_factory.h" +#include "chrome/common/extensions/api/reading_list.h" +#include "components/reading_list/core/reading_list_model.h" +#include "content/public/test/browser_test.h" +#include "extensions/browser/test_event_router_observer.h" + +namespace extensions { + +namespace { + +using ReadingListApiTest = ExtensionApiTest; + +IN_PROC_BROWSER_TEST_F(ReadingListApiTest, TestReadingListWorks) { + ASSERT_TRUE(RunExtensionTest("reading_list")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ReadingListApiTest, + TestReadingListEventsAcrossProfiles) { + // The EventRouter is shared between on- and off-the-record profiles, so + // this observer will catch events for each. + TestEventRouterObserver event_observer(EventRouter::Get(profile())); + + // Add a Reading List entry in a normal browser. + ReadingListModel* const reading_list_model = + ReadingListModelFactory::GetForBrowserContext(profile()); + reading_list_model->AddOrReplaceEntry( + GURL("https://www.example.com"), "example of title", + reading_list::EntrySource::ADDED_VIA_CURRENT_APP, base::TimeDelta()); + + ASSERT_TRUE(base::Contains(event_observer.events(), + api::reading_list::OnEntryAdded::kEventName)); + Event* normal_event = event_observer.events() + .at(api::reading_list::OnEntryAdded::kEventName) + .get(); + EXPECT_EQ(normal_event->restrict_to_browser_context, profile()); + + event_observer.ClearEvents(); + ASSERT_FALSE(base::Contains(event_observer.events(), + api::reading_list::OnEntryAdded::kEventName)); + + const Browser* const incognito_browser = CreateIncognitoBrowser(profile()); + + // Add a Reading List entry in an incognito browser. + ReadingListModel* const incognito_reading_list_model = + ReadingListModelFactory::GetForBrowserContext( + incognito_browser->profile()); + incognito_reading_list_model->AddOrReplaceEntry( + GURL("https://www.example.com"), "example of title", + reading_list::EntrySource::ADDED_VIA_CURRENT_APP, base::TimeDelta()); + + ASSERT_TRUE(base::Contains(event_observer.events(), + api::reading_list::OnEntryAdded::kEventName)); + Event* incognito_event = event_observer.events() + .at(api::reading_list::OnEntryAdded::kEventName) + .get(); + EXPECT_EQ(incognito_event->restrict_to_browser_context, + incognito_browser->profile()); +} + +} // namespace + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.cc new file mode 100644 index 00000000000..32d1fa3935d --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.cc @@ -0,0 +1,143 @@ +// Copyright 2023 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/extensions/api/reading_list/reading_list_event_router.h" + +#include "base/no_destructor.h" +#include "chrome/browser/extensions/api/reading_list/reading_list_util.h" +#include "chrome/browser/profiles/profile_keyed_service_factory.h" +#include "chrome/browser/reading_list/reading_list_model_factory.h" +#include "chrome/common/extensions/api/reading_list.h" +#include "components/reading_list/core/reading_list_entry.h" +#include "components/reading_list/core/reading_list_model.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/event_router_factory.h" + +namespace extensions { + +namespace { + +// The factory responsible for creating the per-profile event router for the +// ReadingList API. +class ReadingListEventRouterFactory : public ProfileKeyedServiceFactory { + public: + ReadingListEventRouterFactory(); + ReadingListEventRouterFactory(const ReadingListEventRouterFactory&) = delete; + ReadingListEventRouterFactory& operator=( + const ReadingListEventRouterFactory&) = delete; + ~ReadingListEventRouterFactory() override = default; + + // Given a browser context, returns the corresponding ReadingListEventRouter. + ReadingListEventRouter* GetForProfile(content::BrowserContext* context); + + private: + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + bool ServiceIsNULLWhileTesting() const override; +}; + +ReadingListEventRouterFactory::ReadingListEventRouterFactory() + : ProfileKeyedServiceFactory( + "ReadingListEventRouter", + ProfileSelections::BuildForRegularAndIncognito()) { + DependsOn(EventRouterFactory::GetInstance()); + DependsOn(ReadingListModelFactory::GetInstance()); +} + +ReadingListEventRouter* ReadingListEventRouterFactory::GetForProfile( + content::BrowserContext* context) { + return static_cast<ReadingListEventRouter*>( + GetServiceForBrowserContext(context, /*create=*/true)); +} + +KeyedService* ReadingListEventRouterFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new ReadingListEventRouter(context); +} + +bool ReadingListEventRouterFactory::ServiceIsCreatedWithBrowserContext() const { + return true; +} + +// Since there is a dependency on `EventRouter` that is null by default in unit +// tests, this service needs to be null as well. If we want to enable it in a +// specific test we need to override the factories for both `EventRouter` and +// this factory to enforce the service creation. +bool ReadingListEventRouterFactory::ServiceIsNULLWhileTesting() const { + return true; +} + +} // namespace + +ReadingListEventRouter::ReadingListEventRouter( + content::BrowserContext* browser_context) + : reading_list_model_( + ReadingListModelFactory::GetForBrowserContext(browser_context)), + profile_(Profile::FromBrowserContext(browser_context)), + event_router_(EventRouter::Get(browser_context)) { + reading_list_observation_.Observe(reading_list_model_); +} + +ReadingListEventRouter::~ReadingListEventRouter() = default; + +// static +ReadingListEventRouter* ReadingListEventRouter::Get( + content::BrowserContext* browser_context) { + return static_cast<ReadingListEventRouterFactory*>(GetFactoryInstance()) + ->GetForProfile(browser_context); +} + +// static +ProfileKeyedServiceFactory* ReadingListEventRouter::GetFactoryInstance() { + static base::NoDestructor<ReadingListEventRouterFactory> factory; + return factory.get(); +} + +void ReadingListEventRouter::ReadingListDidAddEntry( + const ReadingListModel* model, + const GURL& url, + reading_list::EntrySource source) { + auto args(api::reading_list::OnEntryAdded::Create( + reading_list_util::ParseEntry(*model->GetEntryByURL(url)))); + + DispatchEvent(events::READING_LIST_ON_ENTRY_ADDED, + api::reading_list::OnEntryAdded::kEventName, std::move(args)); +} + +void ReadingListEventRouter::ReadingListWillRemoveEntry( + const ReadingListModel* model, + const GURL& url) { + auto args(api::reading_list::OnEntryRemoved::Create( + reading_list_util::ParseEntry(*model->GetEntryByURL(url)))); + + // Even though we dispatch the event in ReadingListWillRemoveEntry() (i.e., + // the entry is still in the model at this point), we can safely dispatch it + // as `onEntryRemoved` (past tense) to the extension. The entry is removed + // synchronously after this, so there's no way the extension could still + // see the entry in the list. + DispatchEvent(events::READING_LIST_ON_ENTRY_REMOVED, + api::reading_list::OnEntryRemoved::kEventName, std::move(args)); +} + +void ReadingListEventRouter::ReadingListDidUpdateEntry( + const ReadingListModel* model, + const GURL& url) { + auto args(api::reading_list::OnEntryUpdated::Create( + reading_list_util::ParseEntry(*model->GetEntryByURL(url)))); + + DispatchEvent(events::READING_LIST_ON_ENTRY_UPDATED, + api::reading_list::OnEntryUpdated::kEventName, std::move(args)); +} + +void ReadingListEventRouter::DispatchEvent( + events::HistogramValue histogram_value, + const std::string& event_name, + base::Value::List args) { + event_router_->BroadcastEvent(std::make_unique<Event>( + histogram_value, event_name, std::move(args), profile_)); +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.h b/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.h new file mode 100644 index 00000000000..20e8e8427f8 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_event_router.h @@ -0,0 +1,67 @@ +// Copyright 2023 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_EXTENSIONS_API_READING_LIST_READING_LIST_EVENT_ROUTER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_EVENT_ROUTER_H_ + +#include "chrome/browser/profiles/profile.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/reading_list/core/reading_list_entry.h" +#include "components/reading_list/core/reading_list_model_observer.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_event_histogram_value.h" + +class ProfileKeyedServiceFactory; + +namespace extensions { + +// The ReadingListEventRouter listens for reading list events and notifies +// observers of the changes. +class ReadingListEventRouter : public KeyedService, + public ReadingListModelObserver { + public: + explicit ReadingListEventRouter(content::BrowserContext* browser_context); + ReadingListEventRouter(const ReadingListEventRouter&) = delete; + ReadingListEventRouter& operator=(const ReadingListEventRouter&) = delete; + ~ReadingListEventRouter() override; + + static ReadingListEventRouter* Get(content::BrowserContext* browser_context); + + static ProfileKeyedServiceFactory* GetFactoryInstance(); + + private: + // ReadingListModelObserver: + void ReadingListModelLoaded(const ReadingListModel* model) override{}; + void ReadingListDidAddEntry(const ReadingListModel* model, + const GURL& url, + reading_list::EntrySource source) override; + void ReadingListWillRemoveEntry(const ReadingListModel* model, + const GURL& url) override; + void ReadingListDidUpdateEntry(const ReadingListModel* model, + const GURL& url) override; + + void DispatchEvent(events::HistogramValue histogram_value, + const std::string& event_name, + base::Value::List args); + + base::ScopedObservation<ReadingListModel, ReadingListModelObserver> + reading_list_observation_{this}; + + // Guaranteed to outlive this object since it is declared as a KeyedService + // dependency. + raw_ptr<ReadingListModel> reading_list_model_; + + // Guaranteed to outlive this object since it is declared as a KeyedService + // dependency. + raw_ptr<Profile> const profile_; + + // Notifies observers of events associated with the profile. Guaranteed to + // outlive this object since it is declared as a KeyedService dependency. + raw_ptr<EventRouter> const event_router_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_EVENT_ROUTER_H_ diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.cc b/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.cc new file mode 100644 index 00000000000..7c4db3edbf9 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.cc @@ -0,0 +1,24 @@ +// Copyright 2023 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/extensions/api/reading_list/reading_list_util.h" +#include "base/time/time.h" + +namespace extensions::reading_list_util { + +api::reading_list::ReadingListEntry ParseEntry(const ReadingListEntry& entry) { + api::reading_list::ReadingListEntry reading_list_entry; + + reading_list_entry.url = entry.URL().spec(); + reading_list_entry.title = entry.Title(); + reading_list_entry.has_been_read = entry.IsRead(); + reading_list_entry.last_update_time = + base::Microseconds(entry.UpdateTime()).InMilliseconds(); + reading_list_entry.creation_time = + base::Microseconds(entry.CreationTime()).InMilliseconds(); + + return reading_list_entry; +} + +} // namespace extensions::reading_list_util diff --git a/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.h b/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.h new file mode 100644 index 00000000000..ad50c345379 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/reading_list/reading_list_util.h @@ -0,0 +1,18 @@ +// Copyright 2023 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_EXTENSIONS_API_READING_LIST_READING_LIST_UTIL_H_ +#define CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_UTIL_H_ + +#include "chrome/common/extensions/api/reading_list.h" +#include "components/reading_list/core/reading_list_entry.h" + +namespace extensions::reading_list_util { + +// Converts from ReadingListEntry to api::reading_list::ReadingListEntry. +api::reading_list::ReadingListEntry ParseEntry(const ReadingListEntry& entry); + +} // namespace extensions::reading_list_util + +#endif // CHROME_BROWSER_EXTENSIONS_API_READING_LIST_READING_LIST_UTIL_H_ diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc index 4c807188e08..6b2aaa005f3 100644 --- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc +++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.cc @@ -48,9 +48,10 @@ SafeBrowsingPrivateEventRouterFactory::SafeBrowsingPrivateEventRouterFactory() SafeBrowsingPrivateEventRouterFactory:: ~SafeBrowsingPrivateEventRouterFactory() = default; -KeyedService* SafeBrowsingPrivateEventRouterFactory::BuildServiceInstanceFor( +std::unique_ptr<KeyedService> +SafeBrowsingPrivateEventRouterFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const { - return new SafeBrowsingPrivateEventRouter(context); + return std::make_unique<SafeBrowsingPrivateEventRouter>(context); } bool SafeBrowsingPrivateEventRouterFactory::ServiceIsCreatedWithBrowserContext() diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h index 4c85cb71b32..f8cfef70def 100644 --- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h +++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h @@ -43,7 +43,7 @@ class SafeBrowsingPrivateEventRouterFactory ~SafeBrowsingPrivateEventRouterFactory() override; // BrowserContextKeyedServiceFactory: - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const override; }; diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc index ecd466ce7ec..2756fbec578 100644 --- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc +++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_util.cc @@ -101,6 +101,11 @@ safe_browsing_private::ReferrerChainEntry ReferrerToReferrerChainEntry( entry.navigation_initiation = safe_browsing_private:: NAVIGATION_INITIATION_COPY_PASTE_USER_INITIATED; break; + case safe_browsing:: + ReferrerChainEntry_NavigationInitiation_NOTIFICATION_INITIATED: + entry.navigation_initiation = + safe_browsing_private::NAVIGATION_INITIATION_NOTIFICATION_INITIATED; + break; case safe_browsing::ReferrerChainEntry_NavigationInitiation_UNDEFINED: NOTREACHED(); } diff --git a/chromium/chrome/browser/extensions/api/scripting/scripting_api.cc b/chromium/chrome/browser/extensions/api/scripting/scripting_api.cc index 77d0b1ccb6d..9459dec59b0 100644 --- a/chromium/chrome/browser/extensions/api/scripting/scripting_api.cc +++ b/chromium/chrome/browser/extensions/api/scripting/scripting_api.cc @@ -16,11 +16,10 @@ #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/common/extensions/api/scripting.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "extensions/browser/api/scripting/scripting_constants.h" +#include "extensions/browser/api/scripting/scripting_utils.h" #include "extensions/browser/extension_api_frame_id_map.h" #include "extensions/browser/extension_file_task_runner.h" #include "extensions/browser/extension_system.h" @@ -37,8 +36,8 @@ #include "extensions/common/mojom/execution_world.mojom-shared.h" #include "extensions/common/mojom/host_id.mojom.h" #include "extensions/common/mojom/run_location.mojom-shared.h" -#include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/user_script.h" #include "extensions/common/utils/content_script_utils.h" #include "extensions/common/utils/extension_types_utils.h" @@ -51,9 +50,6 @@ constexpr char kDuplicateFileSpecifiedError[] = "Duplicate file specified: '*'."; constexpr char kExactlyOneOfCssAndFilesError[] = "Exactly one of 'css' and 'files' must be specified."; -constexpr char kFilesExceededSizeLimitError[] = - "Scripts could not be loaded because '*' exceeds the maximum script size " - "or the extension's maximum total script size."; // Note: CSS always injects as soon as possible, so we default to // document_start. Because of tab loading, there's no guarantee this will @@ -500,37 +496,12 @@ std::unique_ptr<UserScript> ParseUserScript( return result; } -ValidateContentScriptsResult ValidateParsedScriptsOnFileThread( - ExtensionResource::SymlinkPolicy symlink_policy, - std::unique_ptr<UserScriptList> scripts) { - DCHECK(GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence()); - - // Validate that claimed script resources actually exist, and are UTF-8 - // encoded. - std::string error; - std::vector<InstallWarning> warnings; - bool are_script_files_valid = script_parsing::ValidateFileSources( - *scripts, symlink_policy, &error, &warnings); - - // Script files over the per script/extension limit are recorded as warnings. - // However, for the scripting API we should treat "install warnings" as - // errors by turning this call into a no-op and returning an error. - if (!warnings.empty() && error.empty()) { - error = ErrorUtils::FormatErrorMessage(kFilesExceededSizeLimitError, - warnings[0].specific); - are_script_files_valid = false; - } - - return std::make_pair(std::move(scripts), are_script_files_valid - ? absl::nullopt - : absl::make_optional(error)); -} - // Converts a UserScript object to a api::scripting::RegisteredContentScript // object, used for getRegisteredContentScripts. api::scripting::RegisteredContentScript CreateRegisteredContentScriptInfo( const UserScript& script) { api::scripting::RegisteredContentScript script_info; + CHECK_EQ(UserScript::Source::kDynamicContentScript, script.GetSource()); script_info.id = script.id(); script_info.matches.emplace(); @@ -936,32 +907,25 @@ ScriptingRegisterContentScriptsFunction::Run() { std::vector<api::scripting::RegisteredContentScript>& scripts = params->scripts; - ExtensionUserScriptLoader* loader = ExtensionSystem::Get(browser_context()) ->user_script_manager() ->GetUserScriptLoaderForExtension(extension()->id()); - std::set<std::string> existing_script_ids = loader->GetDynamicScriptIDs(); - std::set<std::string> new_script_ids; - for (const auto& script : scripts) { - if (script.id.empty()) - return RespondNow(Error("Content script's ID must not be empty")); - - if (script.id[0] == UserScript::kGeneratedIDPrefix) { - return RespondNow(Error(base::StringPrintf( - "Content script's ID '%s' must not start with '%c'", - script.id.c_str(), UserScript::kGeneratedIDPrefix))); - } - if (base::Contains(existing_script_ids, script.id) || - base::Contains(new_script_ids, script.id)) { - return RespondNow(Error( - base::StringPrintf("Duplicate script ID '%s'", script.id.c_str()))); - } - - new_script_ids.insert(script.id); + // Create script ids for dynamic content scripts. + std::string error; + std::set<std::string> existing_script_ids = + loader->GetDynamicScriptIDs(UserScript::Source::kDynamicContentScript); + std::set<std::string> new_script_ids = scripting::CreateDynamicScriptIds( + scripts, UserScript::Source::kDynamicContentScript, existing_script_ids, + &error); + + if (!error.empty()) { + CHECK(new_script_ids.empty()); + return RespondNow(Error(std::move(error))); } + // Parse content scripts. std::u16string parse_error; auto parsed_scripts = std::make_unique<UserScriptList>(); std::set<std::string> persistent_script_ids; @@ -971,12 +935,13 @@ ScriptingRegisterContentScriptsFunction::Run() { parsed_scripts->reserve(scripts.size()); for (size_t i = 0; i < scripts.size(); ++i) { if (!scripts[i].matches) { + std::string error_script_id = + UserScript::TrimPrefixFromScriptID(scripts[i].id); return RespondNow( Error(base::StringPrintf("Script with ID '%s' must specify 'matches'", - scripts[i].id.c_str()))); + error_script_id.c_str()))); } - // Parse/Create user script. std::unique_ptr<UserScript> user_script = ParseUserScript(browser_context(), *extension(), scripts[i], i, valid_schemes, &parse_error); @@ -997,7 +962,7 @@ ScriptingRegisterContentScriptsFunction::Run() { GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult( FROM_HERE, - base::BindOnce(&ValidateParsedScriptsOnFileThread, + base::BindOnce(&scripting::ValidateParsedScriptsOnFileThread, script_parsing::GetSymlinkPolicy(extension()), std::move(parsed_scripts)), base::BindOnce(&ScriptingRegisterContentScriptsFunction:: @@ -1012,10 +977,11 @@ ScriptingRegisterContentScriptsFunction::Run() { void ScriptingRegisterContentScriptsFunction::OnContentScriptFilesValidated( std::set<std::string> persistent_script_ids, - ValidateContentScriptsResult result) { + scripting::ValidateScriptsResult result) { // We cannot proceed if the `browser_context` is not valid as the // `ExtensionSystem` will not exist. if (!browser_context()) { + Release(); // Matches the `AddRef()` in `Run()`. return; } @@ -1028,8 +994,9 @@ void ScriptingRegisterContentScriptsFunction::OnContentScriptFilesValidated( if (error.has_value()) { std::set<std::string> ids_to_remove; - for (const auto& script : *scripts) + for (const auto& script : *scripts) { ids_to_remove.insert(script->id()); + } loader->RemovePendingDynamicScriptIDs(std::move(ids_to_remove)); Respond(Error(std::move(*error))); @@ -1068,8 +1035,10 @@ ScriptingGetRegisteredContentScriptsFunction::Run() { params->filter; std::set<std::string> id_filter; if (filter && filter->ids) { - id_filter.insert(std::make_move_iterator(filter->ids->begin()), - std::make_move_iterator(filter->ids->end())); + for (const std::string& id : *(filter->ids)) { + id_filter.insert(scripting::AddPrefixToDynamicScriptId( + id, UserScript::Source::kDynamicContentScript)); + } } ExtensionUserScriptLoader* loader = @@ -1082,12 +1051,22 @@ ScriptingGetRegisteredContentScriptsFunction::Run() { std::set<std::string> persistent_script_ids = loader->GetPersistentDynamicScriptIDs(); for (const std::unique_ptr<UserScript>& script : dynamic_scripts) { - if (id_filter.empty() || base::Contains(id_filter, script->id())) { - auto registered_script = CreateRegisteredContentScriptInfo(*script); - registered_script.persist_across_sessions = - base::Contains(persistent_script_ids, script->id()); - script_infos.push_back(std::move(registered_script)); + if (script->GetSource() != UserScript::Source::kDynamicContentScript) { + continue; } + + if (!id_filter.empty() && !base::Contains(id_filter, script->id())) { + continue; + } + + auto registered_script = CreateRegisteredContentScriptInfo(*script); + registered_script.persist_across_sessions = + base::Contains(persistent_script_ids, script->id()); + + // Remove the internally used prefix from the `script`'s ID before + // returning. + registered_script.id = script->GetIDWithoutPrefix(); + script_infos.push_back(std::move(registered_script)); } return RespondNow( @@ -1107,41 +1086,26 @@ ScriptingUnregisterContentScriptsFunction::Run() { EXTENSION_FUNCTION_VALIDATE(params); absl::optional<api::scripting::ContentScriptFilter>& filter = params->filter; - std::set<std::string> ids_to_remove; - - ExtensionUserScriptLoader* loader = - ExtensionSystem::Get(browser_context()) - ->user_script_manager() - ->GetUserScriptLoaderForExtension(extension()->id()); - std::set<std::string> existing_script_ids = loader->GetDynamicScriptIDs(); - if (filter && filter->ids) { - for (const auto& id : *filter->ids) { - if (UserScript::IsIDGenerated(id)) { - return RespondNow(Error(base::StringPrintf( - "Content script's ID '%s' must not start with '%c'", id.c_str(), - UserScript::kGeneratedIDPrefix))); - } - - if (!base::Contains(existing_script_ids, id)) { - return RespondNow(Error( - base::StringPrintf("Nonexistent script ID '%s'", id.c_str()))); - } - - ids_to_remove.insert(id); - } + absl::optional<std::vector<std::string>> ids = absl::nullopt; + // TODO(crbug.com/1300657): `ids` should have an empty list when filter ids is + // empty, instead of a nullopt. Otherwise, we are incorrectly removing all + // content scripts when ids is empty. + if (filter && filter->ids && !filter->ids->empty()) { + ids = std::move(filter->ids); } - if (ids_to_remove.empty()) { - loader->ClearDynamicScripts( - base::BindOnce(&ScriptingUnregisterContentScriptsFunction:: - OnContentScriptsUnregistered, - this)); - } else { - loader->RemoveDynamicScripts( - std::move(ids_to_remove), - base::BindOnce(&ScriptingUnregisterContentScriptsFunction:: - OnContentScriptsUnregistered, - this)); + std::string error; + bool removal_triggered = scripting::RemoveScripts( + ids, UserScript::Source::kDynamicContentScript, browser_context(), + extension()->id(), + base::BindOnce(&ScriptingUnregisterContentScriptsFunction:: + OnContentScriptsUnregistered, + this), + &error); + + if (!removal_triggered) { + CHECK(!error.empty()); + return RespondNow(Error(std::move(error))); } return RespondLater(); @@ -1167,6 +1131,18 @@ ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() { std::vector<api::scripting::RegisteredContentScript>& scripts = params->scripts; + std::string error; + + // Add the prefix for dynamic content scripts onto the IDs of all scripts in + // `scripts` before continuing. + std::set<std::string> ids_to_update = scripting::CreateDynamicScriptIds( + scripts, UserScript::Source::kDynamicContentScript, + std::set<std::string>(), &error); + + if (!error.empty()) { + CHECK(ids_to_update.empty()); + return RespondNow(Error(std::move(error))); + } ExtensionUserScriptLoader* loader = ExtensionSystem::Get(browser_context()) @@ -1177,29 +1153,21 @@ ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() { loaded_scripts_metadata; const UserScriptList& dynamic_scripts = loader->GetLoadedDynamicScripts(); for (const std::unique_ptr<UserScript>& script : dynamic_scripts) { - loaded_scripts_metadata.emplace(script->id(), - CreateRegisteredContentScriptInfo(*script)); + if (script->GetSource() == UserScript::Source::kDynamicContentScript) { + loaded_scripts_metadata.emplace( + script->id(), CreateRegisteredContentScriptInfo(*script)); + } } - std::set<std::string> ids_to_update; for (const auto& script : scripts) { + std::string error_script_id = UserScript::TrimPrefixFromScriptID(script.id); if (loaded_scripts_metadata.find(script.id) == loaded_scripts_metadata.end()) { return RespondNow( - Error(base::StringPrintf("Content script with ID '%s' does not exist " + Error(base::StringPrintf("Script with ID '%s' does not exist " "or is not fully registered", - script.id.c_str()))); - } - - DCHECK(!script.id.empty()); - DCHECK(!UserScript::IsIDGenerated(script.id)); - - if (base::Contains(ids_to_update, script.id)) { - return RespondNow(Error( - base::StringPrintf("Duplicate script ID '%s'", script.id.c_str()))); + error_script_id.c_str()))); } - - ids_to_update.insert(script.id); } std::u16string parse_error; @@ -1268,7 +1236,7 @@ ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() { GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult( FROM_HERE, - base::BindOnce(&ValidateParsedScriptsOnFileThread, + base::BindOnce(&scripting::ValidateParsedScriptsOnFileThread, script_parsing::GetSymlinkPolicy(extension()), std::move(parsed_scripts)), base::BindOnce( @@ -1283,10 +1251,11 @@ ExtensionFunction::ResponseAction ScriptingUpdateContentScriptsFunction::Run() { void ScriptingUpdateContentScriptsFunction::OnContentScriptFilesValidated( std::set<std::string> persistent_script_ids, - ValidateContentScriptsResult result) { + scripting::ValidateScriptsResult result) { // We cannot proceed if the `browser_context` is not valid as the // `ExtensionSystem` will not exist. if (!browser_context()) { + Release(); // Matches the `AddRef()` in `Run()`. return; } diff --git a/chromium/chrome/browser/extensions/api/scripting/scripting_api.h b/chromium/chrome/browser/extensions/api/scripting/scripting_api.h index 811da641457..a9db6bc8959 100644 --- a/chromium/chrome/browser/extensions/api/scripting/scripting_api.h +++ b/chromium/chrome/browser/extensions/api/scripting/scripting_api.h @@ -11,6 +11,7 @@ #include <vector> #include "chrome/common/extensions/api/scripting.h" +#include "extensions/browser/api/scripting/scripting_utils.h" #include "extensions/browser/extension_function.h" #include "extensions/browser/script_executor.h" #include "extensions/common/mojom/code_injection.mojom.h" @@ -108,9 +109,6 @@ class ScriptingRemoveCSSFunction : public ExtensionFunction { void OnCSSRemoved(std::vector<ScriptExecutor::FrameResult> results); }; -using ValidateContentScriptsResult = - std::pair<std::unique_ptr<UserScriptList>, absl::optional<std::string>>; - class ScriptingRegisterContentScriptsFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("scripting.registerContentScripts", @@ -131,7 +129,7 @@ class ScriptingRegisterContentScriptsFunction : public ExtensionFunction { // Called when script files have been checked. void OnContentScriptFilesValidated( std::set<std::string> persistent_script_ids, - ValidateContentScriptsResult result); + scripting::ValidateScriptsResult result); // Called when content scripts have been registered. void OnContentScriptsRegistered(const absl::optional<std::string>& error); @@ -196,7 +194,7 @@ class ScriptingUpdateContentScriptsFunction : public ExtensionFunction { // Called when script files have been checked. void OnContentScriptFilesValidated( std::set<std::string> persistent_script_ids, - ValidateContentScriptsResult result); + scripting::ValidateScriptsResult result); // Called when content scripts have been updated. void OnContentScriptsUpdated(const absl::optional<std::string>& error); diff --git a/chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc b/chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc index abe0c434f07..2f86e914ae1 100644 --- a/chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc +++ b/chromium/chrome/browser/extensions/api/scripting/scripting_apitest.cc @@ -21,6 +21,7 @@ #include "extensions/browser/api_test_utils.h" #include "extensions/browser/background_script_executor.h" #include "extensions/browser/disable_reason.h" +#include "extensions/common/extension_features.h" #include "extensions/common/features/feature_channel.h" #include "extensions/common/utils/content_script_utils.h" #include "extensions/test/extension_test_message_listener.h" @@ -550,4 +551,30 @@ IN_PROC_BROWSER_TEST_F(ScriptingAPIPrerenderingTest, MAYBE_Basic) { ASSERT_TRUE(RunExtensionTest("scripting/prerendering")) << message_; } +class ScriptingAndUserScriptsAPITest : public ScriptingAPITest { + public: + ScriptingAndUserScriptsAPITest() { + scoped_feature_list_.InitAndEnableFeature( + extensions_features::kApiUserScripts); + } + ScriptingAndUserScriptsAPITest(const ScriptingAndUserScriptsAPITest&) = + delete; + ScriptingAndUserScriptsAPITest& operator=( + const ScriptingAndUserScriptsAPITest&) = delete; + ~ScriptingAndUserScriptsAPITest() override = default; + + private: + // The userScripts API is currently behind a channel and feature restriction. + // TODO(crbug.com/1472902): Remove channel override when user scripts API goes + // to stable. + ScopedCurrentChannel current_channel_override_{ + version_info::Channel::UNKNOWN}; + base::test::ScopedFeatureList scoped_feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(ScriptingAndUserScriptsAPITest, + ScriptingAPIDoesNotAffectUserScripts) { + ASSERT_TRUE(RunExtensionTest("scripting/dynamic_user_scripts")) << message_; +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc b/chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc index e94dc4dfd5a..e897598888f 100644 --- a/chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc +++ b/chromium/chrome/browser/extensions/api/settings_overrides/settings_overrides_api.cc @@ -23,9 +23,12 @@ #include "extensions/browser/extension_prefs_factory.h" #include "extensions/browser/extension_prefs_helper.h" #include "extensions/browser/extension_prefs_helper_factory.h" +#include "extensions/common/api/types.h" #include "extensions/common/error_utils.h" #include "extensions/common/manifest_constants.h" +using extensions::api::types::ChromeSettingScope; + namespace extensions { namespace { @@ -147,7 +150,7 @@ void SettingsOverridesAPI::SetPref(const std::string& extension_id, return; prefs_helper->SetExtensionControlledPref( - extension_id, pref_key, kExtensionPrefsScopeRegular, std::move(value)); + extension_id, pref_key, ChromeSettingScope::kRegular, std::move(value)); } void SettingsOverridesAPI::UnsetPref(const std::string& extension_id, @@ -157,7 +160,7 @@ void SettingsOverridesAPI::UnsetPref(const std::string& extension_id, if (!prefs_helper) return; prefs_helper->RemoveExtensionControlledPref(extension_id, pref_key, - kExtensionPrefsScopeRegular); + ChromeSettingScope::kRegular); } void SettingsOverridesAPI::OnExtensionLoaded( diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h index ec5992b76b3..bd31f1492c3 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.h @@ -7,8 +7,8 @@ #include <memory> #include <string> -#include <unordered_map> +#include "base/containers/flat_map.h" #include "base/memory/raw_ptr.h" #include "chrome/browser/extensions/api/settings_private/generated_pref.h" #include "chrome/browser/extensions/api/settings_private/prefs_util_enums.h" @@ -22,11 +22,10 @@ class Value; } namespace extensions { -namespace api { -namespace settings_private { + +namespace api::settings_private { struct PrefObject; -} // namespace settings_private -} // namespace api +} // namespace api::settings_private namespace settings_private { @@ -36,8 +35,7 @@ namespace settings_private { class GeneratedPrefs : public KeyedService { public: // Preference name to implementation map. - using PrefsMap = - std::unordered_map<std::string, std::unique_ptr<GeneratedPref>>; + using PrefsMap = base::flat_map<std::string, std::unique_ptr<GeneratedPref>>; explicit GeneratedPrefs(Profile* profile); diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc index af641e055d1..4ad1f0f943e 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.cc @@ -41,9 +41,10 @@ bool GeneratedPrefsFactory::ServiceIsNULLWhileTesting() const { return true; } -KeyedService* GeneratedPrefsFactory::BuildServiceInstanceFor( +std::unique_ptr<KeyedService> +GeneratedPrefsFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const { - return new GeneratedPrefs(static_cast<Profile*>(profile)); + return std::make_unique<GeneratedPrefs>(static_cast<Profile*>(profile)); } } // namespace settings_private diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h index 2aaea1496d8..69d829feec5 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs_factory.h @@ -32,7 +32,7 @@ class GeneratedPrefsFactory : public ProfileKeyedServiceFactory { // BrowserContextKeyedServiceFactory implementation. bool ServiceIsNULLWhileTesting() const override; - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const override; }; diff --git a/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc index 0e684543447..40fb00a537e 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc @@ -42,8 +42,10 @@ #include "components/password_manager/core/common/password_manager_pref_names.h" #include "components/payments/core/payment_prefs.h" #include "components/performance_manager/public/user_tuning/prefs.h" +#include "components/permissions/pref_names.h" #include "components/prefs/pref_service.h" #include "components/privacy_sandbox/privacy_sandbox_prefs.h" +#include "components/privacy_sandbox/tracking_protection_prefs.h" #include "components/proxy_config/proxy_config_pref_names.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/search_engines/default_search_manager.h" @@ -188,6 +190,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { (*s_allowlist)[autofill::prefs::kAutofillPaymentMethodsMandatoryReauth] = settings_api::PrefType::PREF_TYPE_BOOLEAN; #endif + (*s_allowlist)[autofill::prefs::kAutofillPaymentCvcStorage] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[payments::kCanMakePaymentEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[bookmarks::prefs::kShowBookmarkBar] = @@ -208,8 +212,6 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { (*s_allowlist)[::prefs::kPolicyThemeColor] = settings_api::PrefType::PREF_TYPE_NUMBER; #if BUILDFLAG(IS_LINUX) - (*s_allowlist)[::prefs::kUsesSystemThemeDeprecated] = - settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[::prefs::kSystemTheme] = settings_api::PrefType::PREF_TYPE_NUMBER; #endif @@ -265,6 +267,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::PREF_TYPE_STRING; (*s_allowlist)[drive::prefs::kDriveFsBulkPinningEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_allowlist)[drive::prefs::kDisableDriveOverCellular] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; #endif (*s_allowlist)[::prefs::kDownloadBubblePartialViewEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; @@ -292,6 +296,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { [password_manager::prefs::kBiometricAuthenticationBeforeFilling] = settings_api::PrefType::PREF_TYPE_BOOLEAN; #endif +#if BUILDFLAG(IS_MAC) + (*s_allowlist)[::prefs::kCreatePasskeysInICloudKeychain] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; +#endif // Privacy page (*s_allowlist)[::prefs::kSigninAllowedOnNextStartup] = @@ -352,6 +360,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[::prefs::kPrivacySandboxFirstPartySetsEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_allowlist)[::prefs::kBlockAll3pcToggleEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_allowlist)[::prefs::kTrackingProtectionLevel] = + settings_api::PrefType::PREF_TYPE_NUMBER; // Sync and personalization page. (*s_allowlist)[::prefs::kSearchSuggestEnabled] = @@ -398,6 +410,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[ash::prefs::kAssistPredictiveWritingEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_allowlist)[ash::prefs::kOrcaEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[ash::prefs::kEmojiSuggestionEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[ash::prefs::kLacrosProxyControllingExtension] = @@ -450,6 +464,9 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::PREF_TYPE_NUMBER; (*s_allowlist)[::prefs::kEnableQuietNotificationPermissionUi] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_allowlist) + [::permissions::prefs::kUnusedSitePermissionsRevocationEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; // Clear browsing data settings. (*s_allowlist)[browsing_data::prefs::kDeleteBrowsingHistory] = @@ -503,6 +520,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[::prefs::kLiveCaptionLanguageCode] = settings_api::PrefType::PREF_TYPE_STRING; + (*s_allowlist)[::prefs::kLiveCaptionMaskOffensiveWords] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[::prefs::kLiveTranslateEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[::prefs::kLiveTranslateTargetLanguageCode] = @@ -512,6 +531,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { (*s_allowlist)[::prefs::kAccessibilityPdfOcrAlwaysActive] = settings_api::PrefType::PREF_TYPE_BOOLEAN; #endif +#if defined(USE_AURA) + (*s_allowlist)[::prefs::kOverscrollHistoryNavigationEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; +#endif (*s_allowlist)[::prefs::kCaretBrowsingEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; @@ -549,11 +572,11 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[ash::prefs::kAccessibilityAutoclickMovementThreshold] = settings_api::PrefType::PREF_TYPE_NUMBER; - (*s_allowlist)[ash::prefs::kAccessibilityColorFiltering] = + (*s_allowlist)[ash::prefs::kAccessibilityColorCorrectionEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_allowlist)[ash::prefs::kAccessibilityColorVisionCorrectionAmount] = settings_api::PrefType::PREF_TYPE_NUMBER; - (*s_allowlist)[ash::prefs::kAccessibilityColorVisionDeficiencyType] = + (*s_allowlist)[ash::prefs::kAccessibilityColorVisionCorrectionType] = settings_api::PrefType::PREF_TYPE_NUMBER; (*s_allowlist)[ash::prefs::kShouldAlwaysShowAccessibilityMenu] = settings_api::PrefType::PREF_TYPE_BOOLEAN; diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc index f28e6c77245..f139561b41a 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.cc @@ -38,9 +38,11 @@ SettingsPrivateDelegateFactory::SettingsPrivateDelegateFactory() SettingsPrivateDelegateFactory::~SettingsPrivateDelegateFactory() = default; -KeyedService* SettingsPrivateDelegateFactory::BuildServiceInstanceFor( +std::unique_ptr<KeyedService> +SettingsPrivateDelegateFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const { - return new SettingsPrivateDelegate(static_cast<Profile*>(profile)); + return std::make_unique<SettingsPrivateDelegate>( + static_cast<Profile*>(profile)); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h b/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h index d756c4c2c9b..eef59cde0f7 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h +++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h @@ -35,7 +35,7 @@ class SettingsPrivateDelegateFactory : public ProfileKeyedServiceFactory { ~SettingsPrivateDelegateFactory() override; // BrowserContextKeyedServiceFactory implementation. - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const override; }; diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc index 418137145ae..532c08d59bf 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.cc @@ -161,15 +161,16 @@ void SettingsPrivateEventRouter::SendPrefChange(const std::string& pref_name) { auto args(api::settings_private::OnPrefsChanged::Create(prefs)); - std::unique_ptr<Event> extension_event(new Event( - events::SETTINGS_PRIVATE_ON_PREFS_CHANGED, - api::settings_private::OnPrefsChanged::kEventName, std::move(args))); + std::unique_ptr<Event> extension_event = + std::make_unique<Event>(events::SETTINGS_PRIVATE_ON_PREFS_CHANGED, + api::settings_private::OnPrefsChanged::kEventName, + std::move(args), context_); event_router->BroadcastEvent(std::move(extension_event)); } -SettingsPrivateEventRouter* SettingsPrivateEventRouter::Create( +std::unique_ptr<SettingsPrivateEventRouter> SettingsPrivateEventRouter::Create( content::BrowserContext* context) { - return new SettingsPrivateEventRouter(context); + return std::make_unique<SettingsPrivateEventRouter>(context); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h index 7d2c5543799..a16221f9ec5 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h +++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router.h @@ -35,9 +35,10 @@ class SettingsPrivateEventRouter public EventRouter::Observer, public settings_private::GeneratedPref::Observer { public: - static SettingsPrivateEventRouter* Create( + static std::unique_ptr<SettingsPrivateEventRouter> Create( content::BrowserContext* browser_context); + explicit SettingsPrivateEventRouter(content::BrowserContext* context); SettingsPrivateEventRouter(const SettingsPrivateEventRouter&) = delete; SettingsPrivateEventRouter& operator=(const SettingsPrivateEventRouter&) = delete; @@ -50,8 +51,6 @@ class SettingsPrivateEventRouter content::BrowserContext* context_for_test() { return context_; } protected: - explicit SettingsPrivateEventRouter(content::BrowserContext* context); - // KeyedService overrides: void Shutdown() override; diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc index bf49f8d1803..24c742fa38e 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.cc @@ -46,7 +46,8 @@ SettingsPrivateEventRouterFactory::SettingsPrivateEventRouterFactory() SettingsPrivateEventRouterFactory::~SettingsPrivateEventRouterFactory() = default; -KeyedService* SettingsPrivateEventRouterFactory::BuildServiceInstanceFor( +std::unique_ptr<KeyedService> +SettingsPrivateEventRouterFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const { return SettingsPrivateEventRouter::Create(context); } diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h index a16216aca44..d831a361e81 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h +++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h @@ -42,7 +42,7 @@ class SettingsPrivateEventRouterFactory : public ProfileKeyedServiceFactory { ~SettingsPrivateEventRouterFactory() override; // BrowserContextKeyedServiceFactory: - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const override; }; diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_unittest.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_unittest.cc new file mode 100644 index 00000000000..f1ff2ea6e74 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_event_router_unittest.cc @@ -0,0 +1,160 @@ +// Copyright 2023 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/extensions/api/settings_private/settings_private_event_router.h" + +#include "base/test/run_until.h" +#include "chrome/browser/extensions/api/settings_private/settings_private_event_router_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/mock_render_process_host.h" +#include "extensions/browser/event_router_factory.h" +#include "extensions/browser/process_map.h" +#include "extensions/browser/process_map_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { +namespace { + +// A fake that pretends that all contexts are WebUI. +class ProcessMapFake : public ProcessMap { + public: + Feature::Context GetMostLikelyContextType(const Extension* extension, + int process_id, + const GURL* url) const override { + return Feature::WEBUI_CONTEXT; + } +}; + +std::unique_ptr<KeyedService> BuildEventRouter( + content::BrowserContext* profile) { + return std::make_unique<extensions::EventRouter>(profile, nullptr); +} + +std::unique_ptr<KeyedService> BuildSettingsPrivateEventRouter( + content::BrowserContext* profile) { + return std::unique_ptr<KeyedService>( + SettingsPrivateEventRouter::Create(profile)); +} + +std::unique_ptr<KeyedService> BuildProcessMap( + content::BrowserContext* profile) { + return std::make_unique<ProcessMapFake>(); +} + +// Tracks event dispatches to a specific process. +class EventRouterObserver : public EventRouter::TestObserver { + public: + // Only counts events that match |process_id|. + explicit EventRouterObserver(int process_id) : process_id_(process_id) {} + + void OnWillDispatchEvent(const Event& event) override { + // Do nothing. + } + + void OnDidDispatchEventToProcess(const Event& event, + int process_id) override { + if (process_id == process_id_) { + ++dispatch_count; + } + } + + int dispatch_count = 0; + const int process_id_; +}; + +class SettingsPrivateEventRouterTest : public testing::Test { + public: + SettingsPrivateEventRouterTest() + : manager_(TestingBrowserProcess::GetGlobal()) {} + void SetUp() override { ASSERT_TRUE(manager_.SetUp()); } + + protected: + content::BrowserTaskEnvironment task_environment_; + TestingProfileManager manager_; +}; + +// Tests that events from incognito profiles do not get routed to regular +// profiles. Regression test for https://crbug.com/1381219. +TEST_F(SettingsPrivateEventRouterTest, IncognitoEventRouting) { + // Create a testing profile. Override relevant factories. + TestingProfile* profile = manager_.CreateTestingProfile("test"); + EventRouterFactory::GetInstance()->SetTestingFactory( + profile, base::BindRepeating(&BuildEventRouter)); + SettingsPrivateEventRouterFactory::GetInstance()->SetTestingFactory( + profile, base::BindRepeating(&BuildSettingsPrivateEventRouter)); + ProcessMapFactory::GetInstance()->SetTestingFactory( + profile, base::BindRepeating(&BuildProcessMap)); + + // Create an otr profile. Override relevant factories. + Profile::OTRProfileID otr_id = Profile::OTRProfileID::PrimaryID(); + Profile* otr_profile = + profile->GetOffTheRecordProfile(otr_id, /*create_if_needed=*/true); + EventRouterFactory::GetInstance()->SetTestingFactory( + otr_profile, base::BindRepeating(&BuildEventRouter)); + SettingsPrivateEventRouterFactory::GetInstance()->SetTestingFactory( + otr_profile, base::BindRepeating(&BuildSettingsPrivateEventRouter)); + ProcessMapFactory::GetInstance()->SetTestingFactory( + otr_profile, base::BindRepeating(&BuildProcessMap)); + + // Create the event routers. + EventRouter* regular_event_router = + EventRouterFactory::GetInstance()->GetForBrowserContext(profile); + EventRouter* otr_event_router = + EventRouterFactory::GetInstance()->GetForBrowserContext(otr_profile); + + // Today, EventRouter instances are shared between on- and off-the-record + // profile instances. We separate them into variables here, since the + // SettingsPrivateEventRouter shouldn't necessarily know about that or + // care. + EXPECT_EQ(regular_event_router, otr_event_router); + + // Create the special routers for settingsPrivate. + ASSERT_TRUE(SettingsPrivateEventRouterFactory::GetForProfile(profile)); + ASSERT_TRUE(SettingsPrivateEventRouterFactory::GetForProfile(otr_profile)); + + // Create some mock rphs. + content::MockRenderProcessHost regular_rph(profile); + content::MockRenderProcessHost otr_rph(otr_profile); + + // Add event listeners, as if we had created two real WebUIs, one in a regular + // profile and one in an otr profile. Note that the string chrome://settings + // is hardcoded into the api permissions of settingsPrivate. + GURL kDummyURL("chrome://settings"); + regular_event_router->AddEventListenerForURL( + api::settings_private::OnPrefsChanged::kEventName, ®ular_rph, + kDummyURL); + otr_event_router->AddEventListenerForURL( + api::settings_private::OnPrefsChanged::kEventName, &otr_rph, kDummyURL); + + // Hook up some test observers + EventRouterObserver regular_counter(regular_rph.GetID()); + regular_event_router->AddObserverForTesting(®ular_counter); + EventRouterObserver otr_counter(otr_rph.GetID()); + otr_event_router->AddObserverForTesting(&otr_counter); + + EXPECT_EQ(0, regular_counter.dispatch_count); + EXPECT_EQ(0, otr_counter.dispatch_count); + + // Setting an otr pref should not trigger the normal observer. + otr_profile->GetPrefs()->SetBoolean(prefs::kPromptForDownload, true); + ASSERT_TRUE( + base::test::RunUntil([&]() { return otr_counter.dispatch_count == 1; })); + EXPECT_EQ(0, regular_counter.dispatch_count); + EXPECT_EQ(1, otr_counter.dispatch_count); + + // Setting a regular pref should not trigger the otr observer. + profile->GetPrefs()->SetBoolean(prefs::kPromptForDownload, true); + ASSERT_TRUE(base::test::RunUntil( + [&]() { return regular_counter.dispatch_count == 1; })); + EXPECT_EQ(1, regular_counter.dispatch_count); + EXPECT_EQ(1, otr_counter.dispatch_count); +} + +} // namespace +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h b/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h index 4e672e62e66..571f4e1b0aa 100644 --- a/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h +++ b/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.h @@ -93,7 +93,7 @@ class ManagedValueStoreCache : public ValueStoreCache, VALID_CONTEXT_REQUIRED(backend_sequence_checker_); // The profile that owns the extension system being used. - const raw_ref<Profile, DanglingUntriaged> profile_ + const raw_ref<Profile, AcrossTasksDanglingUntriaged> profile_ GUARDED_BY_CONTEXT(ui_sequence_checker_); // The policy domain. This is used for observing the policy updates. @@ -101,8 +101,8 @@ class ManagedValueStoreCache : public ValueStoreCache, GUARDED_BY_CONTEXT(ui_sequence_checker_); // The `profile_`'s `PolicyService`. - const raw_ref<policy::PolicyService, DanglingUntriaged> policy_service_ - GUARDED_BY_CONTEXT(ui_sequence_checker_); + const raw_ref<policy::PolicyService, AcrossTasksDanglingUntriaged> + policy_service_ GUARDED_BY_CONTEXT(ui_sequence_checker_); // Observes extension loading and unloading, and keeps the `Profile`'s // `PolicyService` aware of the current list of extensions. diff --git a/chromium/chrome/browser/extensions/api/storage/policy_value_store.h b/chromium/chrome/browser/extensions/api/storage/policy_value_store.h index fc39df27d06..872f953838e 100644 --- a/chromium/chrome/browser/extensions/api/storage/policy_value_store.h +++ b/chromium/chrome/browser/extensions/api/storage/policy_value_store.h @@ -13,6 +13,7 @@ #include "components/value_store/value_store.h" #include "extensions/browser/api/storage/settings_observer.h" +#include "extensions/common/extension_id.h" namespace policy { class PolicyMap; @@ -63,7 +64,7 @@ class PolicyValueStore : public value_store::ValueStore { value_store::ValueStore* delegate() { return delegate_.get(); } private: - std::string extension_id_; + ExtensionId extension_id_; SequenceBoundSettingsChangedCallback observer_; std::unique_ptr<value_store::ValueStore> delegate_; }; diff --git a/chromium/chrome/browser/extensions/api/storage/setting_sync_data.h b/chromium/chrome/browser/extensions/api/storage/setting_sync_data.h index 913bc2c76a8..76dce4a4b14 100644 --- a/chromium/chrome/browser/extensions/api/storage/setting_sync_data.h +++ b/chromium/chrome/browser/extensions/api/storage/setting_sync_data.h @@ -10,6 +10,7 @@ #include "base/values.h" #include "components/sync/model/sync_change.h" +#include "extensions/common/extension_id.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace syncer { @@ -45,7 +46,7 @@ class SettingSyncData { const { return change_type_; } - const std::string& extension_id() const { return extension_id_; } + const ExtensionId& extension_id() const { return extension_id_; } const std::string& key() const { return key_; } // value() cannot be called if ExtractValue() has been called. const base::Value& value() const { return *value_; } @@ -60,7 +61,7 @@ class SettingSyncData { void ExtractSyncData(const syncer::SyncData& sync_data); absl::optional<syncer::SyncChange::SyncChangeType> change_type_; - std::string extension_id_; + ExtensionId extension_id_; std::string key_; absl::optional<base::Value> value_; }; diff --git a/chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h b/chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h index e8702bc637c..e1e8f119a2a 100644 --- a/chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h +++ b/chromium/chrome/browser/extensions/api/storage/settings_sync_processor.h @@ -12,6 +12,7 @@ #include "base/values.h" #include "components/sync/base/model_type.h" #include "components/value_store/value_store_change.h" +#include "extensions/common/extension_id.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace syncer { @@ -53,7 +54,7 @@ class SettingsSyncProcessor { private: // ID of the extension the changes are for. - const std::string extension_id_; + const ExtensionId extension_id_; // Sync model type. Either EXTENSION_SETTING or APP_SETTING. const syncer::ModelType type_; diff --git a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc index e36654a1039..75ca60daa3d 100644 --- a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc +++ b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.cc @@ -39,14 +39,15 @@ SystemIndicatorManagerFactory::SystemIndicatorManagerFactory() SystemIndicatorManagerFactory::~SystemIndicatorManagerFactory() = default; -KeyedService* SystemIndicatorManagerFactory::BuildServiceInstanceFor( +std::unique_ptr<KeyedService> +SystemIndicatorManagerFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* profile) const { StatusTray* status_tray = g_browser_process->status_tray(); if (status_tray == NULL) return NULL; - return new SystemIndicatorManager(static_cast<Profile*>(profile), - status_tray); + return std::make_unique<SystemIndicatorManager>( + static_cast<Profile*>(profile), status_tray); } bool SystemIndicatorManagerFactory::ServiceIsCreatedWithBrowserContext() const { diff --git a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h index 2168833ed93..cb24fad396c 100644 --- a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h +++ b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h @@ -30,7 +30,7 @@ class SystemIndicatorManagerFactory : public ProfileKeyedServiceFactory { ~SystemIndicatorManagerFactory() override; // BrowserContextKeyedServiceFactory implementation. - KeyedService* BuildServiceInstanceFor( + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const override; bool ServiceIsCreatedWithBrowserContext() const override; }; diff --git a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc index 64a8ace89c1..e324c999ad3 100644 --- a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc +++ b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc @@ -3,9 +3,9 @@ // found in the LICENSE file. #include <cmath> -#include <unordered_map> #include "base/command_line.h" +#include "base/containers/flat_map.h" #include "base/files/file_util.h" #include "base/strings/stringprintf.h" #include "base/test/trace_event_analyzer.h" @@ -54,7 +54,7 @@ constexpr char kEventSuffixFailRate[] = "FailRate"; constexpr char kEventSuffixLatency[] = "Latency"; constexpr char kEventCommitAndDrawCompositorFrame[] = "WidgetBase::DidCommitAndDrawCompositorFrame"; -const std::unordered_map<std::string, std::string> kEventToMetricMap( +const base::flat_map<std::string, std::string> kEventToMetricMap( {{kEventCapture, kMetricCaptureMs}, {std::string(kEventCapture) + kEventSuffixFailRate, kMetricCaptureFailRatePercent}, diff --git a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc index 05ea0170bfa..170aa77db94 100644 --- a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc +++ b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc @@ -20,6 +20,7 @@ #include "content/public/browser/web_contents_observer.h" #include "extensions/browser/event_router.h" #include "extensions/common/extension.h" +#include "extensions/common/extension_id.h" #include "url/origin.h" using content::BrowserThread; @@ -35,7 +36,7 @@ namespace tab_capture = api::tab_capture; class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver { public: LiveRequest(content::WebContents* target_contents, - const std::string& extension_id, + const ExtensionId& extension_id, bool is_anonymous, TabCaptureRegistry* registry) : content::WebContentsObserver(target_contents), @@ -56,7 +57,7 @@ class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver { ~LiveRequest() override {} // Accessors. - const std::string& extension_id() const { return extension_id_; } + const ExtensionId& extension_id() const { return extension_id_; } bool is_anonymous() const { return is_anonymous_; } TabCaptureState capture_state() const { return capture_state_; } bool is_verified() const { return is_verified_; } @@ -102,7 +103,7 @@ class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver { } private: - const std::string extension_id_; + const ExtensionId extension_id_; const bool is_anonymous_; const raw_ptr<TabCaptureRegistry> registry_; TabCaptureState capture_state_ = tab_capture::TAB_CAPTURE_STATE_NONE; diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc index 5025286fe96..93b7498b7c5 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc @@ -51,6 +51,8 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h" #include "chrome/browser/resource_coordinator/tab_manager.h" +#include "chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h" +#include "chrome/browser/safe_browsing/extension_telemetry/tabs_api_signal.h" #include "chrome/browser/translate/chrome_translate_client.h" #include "chrome/browser/ui/apps/chrome_app_delegate.h" #include "chrome/browser/ui/browser.h" @@ -76,6 +78,7 @@ #include "chrome/common/url_constants.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" +#include "components/safe_browsing/core/common/features.h" #include "components/sessions/content/session_tab_helper.h" #include "components/tab_groups/tab_group_color.h" #include "components/tab_groups/tab_group_id.h" @@ -437,6 +440,30 @@ bool WindowBoundsIntersectDisplays(const gfx::Rect& bounds) { return intersect_area >= (bounds.size().GetArea() / 2); } +void NotifyExtensionTelemetry(Profile* profile, + const Extension* extension, + safe_browsing::TabsApiInfo::ApiMethod api_method, + const std::string& current_url, + const std::string& new_url) { + // Ignore API calls that are not invoked by extensions. + if (!extension) { + return; + } + + auto* extension_telemetry_service = + safe_browsing::ExtensionTelemetryService::Get(profile); + + if (!extension_telemetry_service || !extension_telemetry_service->enabled() || + !base::FeatureList::IsEnabled( + safe_browsing::kExtensionTelemetryTabsApiSignal)) { + return; + } + + auto tabs_api_signal = std::make_unique<safe_browsing::TabsApiSignal>( + extension->id(), api_method, current_url, new_url); + extension_telemetry_service->AddSignal(std::move(tabs_api_signal)); +} + } // namespace void ZoomModeToZoomSettings(ZoomController::ZoomMode zoom_mode, @@ -1298,6 +1325,11 @@ ExtensionFunction::ResponseAction TabsCreateFunction::Run() { return Error(result.error()); } + NotifyExtensionTelemetry(Profile::FromBrowserContext(browser_context()), + extension(), safe_browsing::TabsApiInfo::CREATE, + /*current_url=*/std::string(), + options.url.value_or(std::string())); + // Return data about the newly created tab. return has_callback() ? WithArguments(std::move(*result)) : NoArguments(); }()); @@ -1509,8 +1541,18 @@ ExtensionFunction::ResponseAction TabsUpdateFunction::Run() { return RespondNow(Error(ErrorUtils::FormatErrorMessage( tabs_constants::kURLsNotAllowedInIncognitoError, updated_url))); } + + // Get last committed or pending URL. + std::string current_url = contents->GetVisibleURL().is_valid() + ? contents->GetVisibleURL().spec() + : std::string(); + if (!UpdateURL(updated_url, tab_id, &error)) return RespondNow(Error(std::move(error))); + + NotifyExtensionTelemetry(Profile::FromBrowserContext(browser_context()), + extension(), safe_browsing::TabsApiInfo::UPDATE, + current_url, updated_url); } bool active = false; @@ -1613,12 +1655,6 @@ bool TabsUpdateFunction::UpdateURL(const std::string& url_string, return false; } - // JavaScript URLs are forbidden in chrome.tabs.update(). - if (url->SchemeIs(url::kJavaScriptScheme)) { - *error = tabs_constants::kJavaScriptUrlsNotAllowedInTabsUpdate; - return false; - } - NavigationController::LoadURLParams load_params(*url); // Treat extension-initiated navigations as renderer-initiated so that the URL @@ -1864,6 +1900,15 @@ bool TabsRemoveFunction::RemoveTab(int tab_id, std::string* error) { *error = tabs_constants::kTabStripNotEditableError; return false; } + + // Get last committed or pending URL. + std::string current_url = contents->GetVisibleURL().is_valid() + ? contents->GetVisibleURL().spec() + : std::string(); + NotifyExtensionTelemetry(Profile::FromBrowserContext(browser_context()), + extension(), safe_browsing::TabsApiInfo::REMOVE, + current_url, /*new_url=*/std::string()); + // The tab might not immediately close after calling Close() below, so we // should wait until WebContentsDestroyed is called before responding. web_contents_destroyed_observers_.push_back( @@ -2420,9 +2465,9 @@ bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) { int frame_id = details_->frame_id ? *details_->frame_id : ExtensionApiFrameIdMap::kTopFrameId; - content::RenderFrameHost* rfh = + content::RenderFrameHost* render_frame_host = ExtensionApiFrameIdMap::GetRenderFrameHostById(contents, frame_id); - if (!rfh) { + if (!render_frame_host) { *error = ErrorUtils::FormatErrorMessage( tabs_constants::kFrameNotFoundError, base::NumberToString(frame_id), base::NumberToString(execute_tab_id_)); @@ -2432,11 +2477,12 @@ bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) { // Content scripts declared in manifest.json can access frames at about:-URLs // if the extension has permission to access the frame's origin, so also allow // programmatic content scripts at about:-URLs for allowed origins. - GURL effective_document_url(rfh->GetLastCommittedURL()); + GURL effective_document_url(render_frame_host->GetLastCommittedURL()); bool is_about_url = effective_document_url.SchemeIs(url::kAboutScheme); if (is_about_url && details_->match_about_blank && *details_->match_about_blank) { - effective_document_url = GURL(rfh->GetLastCommittedOrigin().Serialize()); + effective_document_url = + GURL(render_frame_host->GetLastCommittedOrigin().Serialize()); } if (!effective_document_url.is_valid()) { @@ -2454,8 +2500,8 @@ bool ExecuteCodeInTabFunction::CanExecuteScriptOnPage(std::string* error) { mojom::APIPermissionID::kTab)) { *error = ErrorUtils::FormatErrorMessage( manifest_errors::kCannotAccessAboutUrl, - rfh->GetLastCommittedURL().spec(), - rfh->GetLastCommittedOrigin().Serialize()); + render_frame_host->GetLastCommittedURL().spec(), + render_frame_host->GetLastCommittedOrigin().Serialize()); } return false; } diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc index 42c4e323d51..1e164b48f21 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc @@ -42,7 +42,7 @@ #endif #if BUILDFLAG(IS_CHROMEOS) -#include "chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h" +#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_content_manager.h" #endif namespace extensions { @@ -537,7 +537,8 @@ TEST_F(TabsApiUnitTest, TabsUpdateJavaScriptUrlNotAllowed) { kFormatArgs, tab_id, "javascript:void(document.title = 'Won't work')"); std::string error = api_test_utils::RunFunctionAndReturnError( function.get(), args, profile(), api_test_utils::FunctionMode::kNone); - EXPECT_EQ(tabs_constants::kJavaScriptUrlsNotAllowedInTabsUpdate, error); + EXPECT_EQ(tabs_constants::kJavaScriptUrlsNotAllowedInExtensionNavigations, + error); // Clean up. browser()->tab_strip_model()->CloseAllTabs(); diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc index fbac58ab101..1aa7cb74978 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_apitest.cc @@ -11,7 +11,6 @@ #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/scoped_disable_client_side_decorations_for_test.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/common/chrome_switches.h" @@ -132,7 +131,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTabTest, TabAudible) { << message_; } -IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, Muted) { +// TODO(crbug.com/1470083): Re-enable this test +#if BUILDFLAG(IS_MAC) +#define MAYBE_Muted DISABLED_Muted +#else +#define MAYBE_Muted Muted +#endif +IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, MAYBE_Muted) { ASSERT_TRUE(RunExtensionTest("tabs/basics/muted")) << message_; } @@ -146,11 +151,6 @@ IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, Duplicate) { } IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, Size) { - // TODO(crbug.com/1240482): the test expectations fail if the window gets CSD - // and becomes smaller because of that. Investigate this and remove the line - // below if possible. - ui::ScopedDisableClientSideDecorationsForTest scoped_disabled_csd; - ASSERT_TRUE(RunExtensionTest("tabs/basics/tab_size")) << message_; } @@ -206,7 +206,8 @@ IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, GetCurrent) { ASSERT_TRUE(RunExtensionTest("tabs/get_current")) << message_; } -IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, Connect) { +// Disabled for being flaky. See crbug.com/1472144 +IN_PROC_BROWSER_TEST_P(ExtensionApiTabTestWithContextType, DISABLED_Connect) { ASSERT_TRUE(RunExtensionTest("tabs/connect")) << message_; } diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc index 3c8378d7f00..ed6d1a80111 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_constants.cc @@ -124,9 +124,9 @@ const char kCannotDetermineLanguageOfUnloadedTab[] = const char kMissingLockWindowFullscreenPrivatePermission[] = "Cannot lock window to fullscreen or close a locked fullscreen window " "without lockWindowFullscreenPrivate manifest permission"; -const char kJavaScriptUrlsNotAllowedInTabsUpdate[] = - "JavaScript URLs are not allowed in chrome.tabs.update. Use " - "chrome.tabs.executeScript instead."; +const char kJavaScriptUrlsNotAllowedInExtensionNavigations[] = + "JavaScript URLs are not allowed in API based extension navigations. Use " + "chrome.scripting.executeScript instead."; const char kBrowserWindowNotAllowed[] = "Browser windows not allowed."; const char kLockedFullscreenModeNewTabError[] = "You cannot create new tabs while in locked fullscreen mode."; @@ -142,6 +142,8 @@ const char kCannotHighlightTabs[] = "progress."; const char kNotAllowedForDevToolsError[] = "Operation not allowed for DevTools windows"; +const char kFileUrlsNotAllowedInExtensionNavigations[] = + "Cannot navigate to a file URL without local file access."; } // namespace tabs_constants } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_constants.h b/chromium/chrome/browser/extensions/api/tabs/tabs_constants.h index fc1d59b4317..c68ccde74e3 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_constants.h +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_constants.h @@ -78,6 +78,7 @@ extern const char kWindowTypeValueDevTools[]; // Error messages. extern const char kCannotZoomDisabledTabError[]; +extern const char kFileUrlsNotAllowedInExtensionNavigations[]; extern const char kFrameNotFoundError[]; extern const char kNoCrashBrowserError[]; extern const char kNoCurrentWindowError[]; @@ -110,7 +111,7 @@ extern const char kScreenshotsDisabledByDlp[]; extern const char kCannotUpdateMuteCaptured[]; extern const char kCannotDetermineLanguageOfUnloadedTab[]; extern const char kMissingLockWindowFullscreenPrivatePermission[]; -extern const char kJavaScriptUrlsNotAllowedInTabsUpdate[]; +extern const char kJavaScriptUrlsNotAllowedInExtensionNavigations[]; extern const char kBrowserWindowNotAllowed[]; extern const char kLockedFullscreenModeNewTabError[]; extern const char kGroupParamsError[]; diff --git a/chromium/chrome/browser/extensions/api/tabs/windows_event_router.h b/chromium/chrome/browser/extensions/api/tabs/windows_event_router.h index 9db3a0d58c4..f5cadaf2e34 100644 --- a/chromium/chrome/browser/extensions/api/tabs/windows_event_router.h +++ b/chromium/chrome/browser/extensions/api/tabs/windows_event_router.h @@ -88,7 +88,7 @@ class WindowsEventRouter : public AppWindowRegistry::Observer, // The profile the currently focused window belongs to; either the main or // incognito profile or NULL (none of the above). We remember this in order // to correctly handle focus changes between non-OTR and OTR windows. - raw_ptr<Profile, DanglingUntriaged> focused_profile_; + raw_ptr<Profile, AcrossTasksDanglingUntriaged> focused_profile_; // The currently focused window. We keep this so as to avoid sending multiple // windows.onFocusChanged events with the same windowId. diff --git a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc index 2872ac2f667..b5156852191 100644 --- a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc +++ b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc @@ -12,6 +12,7 @@ #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" +#include "ash/webui/settings/public/constants/routes.mojom.h" #include "base/command_line.h" #include "base/containers/contains.h" #include "base/containers/flat_set.h" @@ -46,7 +47,6 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/settings_window_manager_chromeos.h" -#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/api/terminal_private.h" #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h" diff --git a/chromium/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc b/chromium/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc new file mode 100644 index 00000000000..c641309fa82 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc @@ -0,0 +1,52 @@ +// Copyright 2023 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/extensions/extension_apitest.h" +#include "components/version_info/channel.h" +#include "content/public/test/browser_test.h" +#include "extensions/common/extension_features.h" +#include "net/dns/mock_host_resolver.h" + +namespace extensions { + +class UserScriptsAPITest : public ExtensionApiTest { + public: + UserScriptsAPITest(); + UserScriptsAPITest(const UserScriptsAPITest&) = delete; + const UserScriptsAPITest& operator=(const UserScriptsAPITest&) = delete; + ~UserScriptsAPITest() override = default; + + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(StartEmbeddedTestServer()); + } + + private: + // The userScripts API is currently behind a channel and feature restriction. + // TODO(crbug.com/1472902): Remove channel override when user scripts API goes + // to stable. + ScopedCurrentChannel current_channel_override_{ + version_info::Channel::UNKNOWN}; + base::test::ScopedFeatureList scoped_feature_list_; +}; + +UserScriptsAPITest::UserScriptsAPITest() { + scoped_feature_list_.InitAndEnableFeature( + extensions_features::kApiUserScripts); +} + +IN_PROC_BROWSER_TEST_F(UserScriptsAPITest, RegisterUserScripts) { + ASSERT_TRUE(RunExtensionTest("user_scripts/register")) << message_; +} + +IN_PROC_BROWSER_TEST_F(UserScriptsAPITest, GetUserScripts) { + ASSERT_TRUE(RunExtensionTest("user_scripts/get_scripts")) << message_; +} + +IN_PROC_BROWSER_TEST_F(UserScriptsAPITest, UnregisterUserScripts) { + ASSERT_TRUE(RunExtensionTest("user_scripts/unregister")) << message_; +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc index 9984c0e75b2..15bb28c4fa6 100644 --- a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc +++ b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc @@ -13,6 +13,7 @@ #include "ash/public/cpp/clipboard_history_controller.h" #include "ash/public/cpp/clipboard_image_model_factory.h" #include "ash/public/cpp/keyboard/keyboard_types.h" +#include "ash/webui/settings/public/constants/routes.mojom-forward.h" #include "base/check.h" #include "base/feature_list.h" #include "base/functional/bind.h" @@ -24,7 +25,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h" #include "chrome/browser/ui/settings_window_manager_chromeos.h" -#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-forward.h" #include "chromeos/constants/chromeos_features.h" #include "chromeos/crosapi/mojom/clipboard_history.mojom.h" #include "components/user_manager/user_manager.h" @@ -127,7 +127,8 @@ bool SendKeyEventImpl(const std::string& type, SendProcessKeyEvent(ui::ET_KEY_PRESSED, host); - ui::KeyEvent char_event(key_value, code, ui::DomCode::NONE, ui::EF_NONE); + ui::KeyEvent char_event = ui::KeyEvent::FromCharacter( + key_value, code, ui::DomCode::NONE, ui::EF_NONE); if (tic) tic->InsertChar(char_event); SendProcessKeyEvent(ui::ET_KEY_RELEASED, host); @@ -517,9 +518,6 @@ void ChromeVirtualKeyboardDelegate::OnHasInputDevices( "newheader", base::FeatureList::IsEnabled(ash::features::kVirtualKeyboardNewHeader))); features.Append(GenerateFeatureFlag( - "multitouch", - base::FeatureList::IsEnabled(ash::features::kVirtualKeyboardMultitouch))); - features.Append(GenerateFeatureFlag( "roundCorners", base::FeatureList::IsEnabled( ash::features::kVirtualKeyboardRoundCorners))); features.Append( @@ -540,6 +538,9 @@ void ChromeVirtualKeyboardDelegate::OnHasInputDevices( features.Append(GenerateFeatureFlag( "japanesefunctionrow", base::FeatureList::IsEnabled(ash::features::kJapaneseFunctionRow))); + features.Append(GenerateFeatureFlag( + "virtualkeyboardremovenacl", + base::FeatureList::IsEnabled(ash::features::kVirtualKeyboardRemoveNacl))); results.Set("features", std::move(features)); diff --git a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc index a1c970a77cf..92d43cdd507 100644 --- a/chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/virtual_keyboard_private/virtual_keyboard_private_apitest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "ash/public/cpp/clipboard_history_controller.h" +#include "base/containers/flat_map.h" #include "base/path_service.h" #include "chrome/browser/ash/login/lock/screen_locker_tester.h" #include "chrome/browser/extensions/extension_apitest.h" @@ -31,7 +32,7 @@ void CopyBitmapItem() { } void CopyFileItem() { - const std::unordered_map<std::u16string, std::u16string> input_data = { + const base::flat_map<std::u16string, std::u16string> input_data = { {u"fs/sources", u"/path/to/My%20File.txt"}}; base::Pickle input_data_pickle; ui::WriteCustomDataToPickle(input_data, &input_data_pickle); diff --git a/chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc b/chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc index b3ab5fbff15..85c934eaf4e 100644 --- a/chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc +++ b/chromium/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc @@ -16,7 +16,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/common/extensions/api/vpn_provider.h" #include "chromeos/ash/components/network/shill_property_handler.h" -#include "chromeos/crosapi/mojom/vpn_service.mojom-test-utils.h" +#include "chromeos/crosapi/mojom/vpn_service.mojom.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/pepper_vpn_provider_resource_host_proxy.h" #include "content/public/browser/vpn_service_proxy.h" @@ -41,7 +41,6 @@ #endif #if BUILDFLAG(IS_CHROMEOS_LACROS) -#include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h" #include "chromeos/crosapi/mojom/test_controller.mojom.h" #include "chromeos/lacros/lacros_service.h" #endif @@ -208,9 +207,11 @@ class VpnProviderApiTestLacros : public VpnProviderApiTestBase { LOG(ERROR) << "Unsupported ash version."; return false; } - crosapi::mojom::TestControllerAsyncWaiter waiter{ - service->GetRemote<crosapi::mojom::TestController>().get()}; - waiter.BindTestShillController(controller_.BindNewPipeAndPassReceiver()); + base::test::TestFuture<void> future; + service->GetRemote<crosapi::mojom::TestController>() + ->BindTestShillController(controller_.BindNewPipeAndPassReceiver(), + future.GetCallback()); + EXPECT_TRUE(future.Wait()); return true; } @@ -650,9 +651,9 @@ IN_PROC_BROWSER_TEST_F(VpnProviderApiTest, PlatformMessage) { extension_id(), remote.BindNewPipeAndPassReceiver(), receiver.BindNewPipeAndPassRemote()); - crosapi::mojom::VpnServiceForExtensionAsyncWaiter waiter{remote.get()}; - crosapi::mojom::VpnErrorResponsePtr error; - waiter.CreateConfiguration(kTestConfig, &error); + base::test::TestFuture<crosapi::mojom::VpnErrorResponsePtr> future; + remote->CreateConfiguration(kTestConfig, future.GetCallback()); + auto error = future.Take(); ASSERT_FALSE(error) << "CreateConfiguration failed with |message| = " << error->message.value_or(std::string{}); diff --git a/chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc b/chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc index 143cbd58c2d..275a78505da 100644 --- a/chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc +++ b/chromium/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc @@ -33,8 +33,9 @@ bool FrameNavigationState::allow_extension_scheme_ = false; DOCUMENT_USER_DATA_KEY_IMPL(FrameNavigationState); -FrameNavigationState::FrameNavigationState(content::RenderFrameHost* rfh) - : content::DocumentUserData<FrameNavigationState>(rfh) {} +FrameNavigationState::FrameNavigationState( + content::RenderFrameHost* render_frame_host) + : content::DocumentUserData<FrameNavigationState>(render_frame_host) {} FrameNavigationState::~FrameNavigationState() = default; // static diff --git a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc index 1639909dca1..e72083bb18a 100644 --- a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc +++ b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc @@ -430,11 +430,11 @@ bool WebNavigationTabObserver::IsReferenceFragmentNavigation( } void WebNavigationTabObserver::RenderFrameHostPendingDeletion( - content::RenderFrameHost* pending_delete_rfh) { - // The |pending_delete_rfh| and its children are now pending deletion. - // Stop tracking them. + content::RenderFrameHost* pending_delete_render_frame_host) { + // The |pending_delete_render_frame_host| and its children are now pending + // deletion. Stop tracking them. - pending_delete_rfh->ForEachRenderFrameHost( + pending_delete_render_frame_host->ForEachRenderFrameHost( [this](content::RenderFrameHost* render_frame_host) { auto* navigation_state = FrameNavigationState::GetForCurrentDocument(render_frame_host); diff --git a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc index e6308c358c0..3d5386ddb06 100644 --- a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc +++ b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc @@ -111,8 +111,9 @@ class DelayLoadStartAndExecuteJavascript : public TabStripModelObserver, // WebContentsObserver: void DidStartNavigation( content::NavigationHandle* navigation_handle) override { - if (navigation_handle->GetURL() != delay_url_ || !rfh_) + if (navigation_handle->GetURL() != delay_url_ || !render_frame_host_) { return; + } auto throttle = std::make_unique<WillStartRequestObserverThrottle>(navigation_handle); @@ -120,11 +121,11 @@ class DelayLoadStartAndExecuteJavascript : public TabStripModelObserver, navigation_handle->RegisterThrottleForTesting(std::move(throttle)); if (has_user_gesture_) { - rfh_->ExecuteJavaScriptWithUserGestureForTests(base::UTF8ToUTF16(script_), - base::NullCallback()); + render_frame_host_->ExecuteJavaScriptWithUserGestureForTests( + base::UTF8ToUTF16(script_), base::NullCallback()); } else { - rfh_->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script_), - base::NullCallback()); + render_frame_host_->ExecuteJavaScriptForTests(base::UTF8ToUTF16(script_), + base::NullCallback()); } script_was_executed_ = true; } @@ -143,7 +144,7 @@ class DelayLoadStartAndExecuteJavascript : public TabStripModelObserver, } if (navigation_handle->IsInMainFrame()) - rfh_ = navigation_handle->GetRenderFrameHost(); + render_frame_host_ = navigation_handle->GetRenderFrameHost(); } void set_has_user_gesture(bool has_user_gesture) { @@ -184,7 +185,8 @@ class DelayLoadStartAndExecuteJavascript : public TabStripModelObserver, std::string script_; bool has_user_gesture_ = false; bool script_was_executed_ = false; - raw_ptr<content::RenderFrameHost, DanglingUntriaged> rfh_ = nullptr; + raw_ptr<content::RenderFrameHost, AcrossTasksDanglingUntriaged> + render_frame_host_ = nullptr; }; // Handles requests for URLs with paths of "/test*" sent to the test server, so @@ -479,6 +481,7 @@ IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, UserAction) { params.is_editable = false; params.media_type = blink::mojom::ContextMenuDataMediaType::kNone; params.page_url = url; + params.frame_url = url; params.link_url = extension->GetResourceURL("b.html"); // Get the child frame, which will be the one associated with the context diff --git a/chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc index 9955d59dad3..95f82df0033 100644 --- a/chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc @@ -39,6 +39,7 @@ #include "content/public/common/url_constants.h" #include "content/public/test/browser_task_environment.h" #include "extensions/browser/api/declarative_net_request/test_utils.h" +#include "extensions/browser/api/web_request/extension_web_request_event_router.h" #include "extensions/browser/api/web_request/upload_data_presenter.h" #include "extensions/browser/api/web_request/web_request_api.h" #include "extensions/browser/api/web_request/web_request_api_constants.h" @@ -151,12 +152,12 @@ TEST_F(ExtensionWebRequestTest, AddAndRemoveListeners) { // Add two listeners. ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( - &profile_, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName1, + &profile_, ext_id, ext_id, kEventName, kSubEventName1, ExtensionWebRequestEventRouter::RequestFilter(), 0, 1 /* render_process_id */, 0, extensions::kMainThreadId, blink::mojom::kInvalidServiceWorkerVersionId); ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( - &profile_, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName2, + &profile_, ext_id, ext_id, kEventName, kSubEventName2, ExtensionWebRequestEventRouter::RequestFilter(), 0, 1 /* render_process_id */, 0, extensions::kMainThreadId, blink::mojom::kInvalidServiceWorkerVersionId); @@ -167,20 +168,18 @@ TEST_F(ExtensionWebRequestTest, AddAndRemoveListeners) { // Now remove the listeners one at a time, verifying the counts after each // removal. - ExtensionWebRequestEventRouter::GetInstance()->UpdateActiveListener( - ExtensionWebRequestEventRouter::ListenerUpdateType::kRemove, - ExtensionWebRequestEventRouter::GetBrowserContextID(&profile_), ext_id, - kSubEventName1, extensions::kMainThreadId, + ExtensionWebRequestEventRouter::GetInstance()->UpdateActiveListenerForTesting( + &profile_, ExtensionWebRequestEventRouter::ListenerUpdateType::kRemove, + ext_id, kSubEventName1, extensions::kMainThreadId, blink::mojom::kInvalidServiceWorkerVersionId); EXPECT_EQ( 1u, ExtensionWebRequestEventRouter::GetInstance()->GetListenerCountForTesting( &profile_, kEventName)); - ExtensionWebRequestEventRouter::GetInstance()->UpdateActiveListener( - ExtensionWebRequestEventRouter::ListenerUpdateType::kRemove, - ExtensionWebRequestEventRouter::GetBrowserContextID(&profile_), ext_id, - kSubEventName2, extensions::kMainThreadId, + ExtensionWebRequestEventRouter::GetInstance()->UpdateActiveListenerForTesting( + &profile_, ExtensionWebRequestEventRouter::ListenerUpdateType::kRemove, + ext_id, kSubEventName2, extensions::kMainThreadId, blink::mojom::kInvalidServiceWorkerVersionId); EXPECT_EQ( 0u, @@ -200,23 +199,23 @@ TEST_F(ExtensionWebRequestTest, BrowserContextShutdown) { const std::string kSubEventName = kEventName + "/1"; EXPECT_EQ(0u, event_router->GetListenerCountForTesting(&profile_, kEventName)); - EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerImpl(&profile_)); + EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerForTesting(&profile_)); // Add two listeners for the main profile. event_router->AddEventListener( - &profile_, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName, + &profile_, ext_id, ext_id, kEventName, kSubEventName, ExtensionWebRequestEventRouter::RequestFilter(), 0, 1 /* render_process_id */, 0, extensions::kMainThreadId, blink::mojom::kInvalidServiceWorkerVersionId); event_router->AddEventListener( - &profile_, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName, + &profile_, ext_id, ext_id, kEventName, kSubEventName, ExtensionWebRequestEventRouter::RequestFilter(), 0, 2 /* render_process_id */, 0, extensions::kMainThreadId, blink::mojom::kInvalidServiceWorkerVersionId); event_router->IncrementExtraHeadersListenerCount(&profile_); EXPECT_EQ(2u, event_router->GetListenerCountForTesting(&profile_, kEventName)); - EXPECT_TRUE(event_router->HasAnyExtraHeadersListenerImpl(&profile_)); + EXPECT_TRUE(event_router->HasAnyExtraHeadersListenerForTesting(&profile_)); // Create an off-the-record profile. auto otr_profile_id = Profile::OTRProfileID::CreateUniqueForTesting(); @@ -232,29 +231,29 @@ TEST_F(ExtensionWebRequestTest, BrowserContextShutdown) { event_router->OnOTRBrowserContextCreated(&profile_, otr_profile); EXPECT_EQ(0u, event_router->GetListenerCountForTesting(otr_profile, kEventName)); - EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerImpl(otr_profile)); + EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerForTesting(otr_profile)); // Add two listeners for the otr profile. event_router->AddEventListener( - otr_profile, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName, + otr_profile, ext_id, ext_id, kEventName, kSubEventName, ExtensionWebRequestEventRouter::RequestFilter(), 0, 1 /* render_process_id */, 0, extensions::kMainThreadId, blink::mojom::kInvalidServiceWorkerVersionId); event_router->AddEventListener( - otr_profile, ext_id, ext_id, events::FOR_TEST, kEventName, kSubEventName, + otr_profile, ext_id, ext_id, kEventName, kSubEventName, ExtensionWebRequestEventRouter::RequestFilter(), 0, 2 /* render_process_id */, 0, extensions::kMainThreadId, blink::mojom::kInvalidServiceWorkerVersionId); event_router->IncrementExtraHeadersListenerCount(otr_profile); EXPECT_EQ(2u, event_router->GetListenerCountForTesting(otr_profile, kEventName)); - EXPECT_TRUE(event_router->HasAnyExtraHeadersListenerImpl(otr_profile)); + EXPECT_TRUE(event_router->HasAnyExtraHeadersListenerForTesting(otr_profile)); // Simulate the OTR being destroyed. event_router->OnOTRBrowserContextDestroyed(&profile_, otr_profile); EXPECT_EQ(0u, event_router->GetListenerCountForTesting(otr_profile, kEventName)); - EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerImpl(otr_profile)); + EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerForTesting(otr_profile)); // We can't just delete the profile, because the call comes through the // WebRequestAPI instance for that profile, and creating that requires @@ -263,7 +262,7 @@ TEST_F(ExtensionWebRequestTest, BrowserContextShutdown) { event_router->OnBrowserContextShutdown(&profile_); EXPECT_EQ(0u, event_router->GetListenerCountForTesting(&profile_, kEventName)); - EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerImpl(&profile_)); + EXPECT_FALSE(event_router->HasAnyExtraHeadersListenerForTesting(&profile_)); } namespace { diff --git a/chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc index 6b76ea8fcb3..5c19a78e7dc 100644 --- a/chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc +++ b/chromium/chrome/browser/extensions/api/web_request/web_request_apitest.cc @@ -98,6 +98,7 @@ #include "content/public/test/url_loader_interceptor.h" #include "content/public/test/url_loader_monitor.h" #include "content/public/test/web_transport_simple_test_server.h" +#include "extensions/browser/api/web_request/extension_web_request_event_router.h" #include "extensions/browser/api/web_request/web_request_api.h" #include "extensions/browser/background_script_executor.h" #include "extensions/browser/blocked_action_type.h" @@ -1290,8 +1291,8 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiTestWithContextType, // Check that reloading an extension that runs in incognito split mode and // has two active background pages with registered events does not crash the // browser. Regression test for http://crbug.com/224094 -// Flaky on linux-lacros. See http://crbug.com/1423252 -#if BUILDFLAG(IS_CHROMEOS_LACROS) +// Flaky on linux-lacros and Linux. See http://crbug.com/1423252 +#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_LINUX) #define MAYBE_IncognitoSplitModeReload DISABLED_IncognitoSplitModeReload #else #define MAYBE_IncognitoSplitModeReload IncognitoSplitModeReload @@ -2837,7 +2838,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestMockedClockTest, LoadExtension(test_dir.AppendASCII("extension_1")); ASSERT_TRUE(extension_1); ASSERT_TRUE(ready_1_listener.WaitUntilSatisfied()); - const std::string extension_id_1 = extension_1->id(); + const ExtensionId extension_id_1 = extension_1->id(); // Load the second extension. ExtensionTestMessageListener ready_2_listener("ready_2"); @@ -2845,7 +2846,7 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestMockedClockTest, LoadExtension(test_dir.AppendASCII("extension_2")); ASSERT_TRUE(extension_2); ASSERT_TRUE(ready_2_listener.WaitUntilSatisfied()); - const std::string extension_id_2 = extension_2->id(); + const ExtensionId extension_id_2 = extension_2->id(); const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); EXPECT_LT(prefs->GetLastUpdateTime(extension_id_1), @@ -3667,8 +3668,14 @@ IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiTestWithContextType, // Ensure we don't strip off initiator incorrectly in web request events when // both the normal and incognito contexts are active. Regression test for // crbug.com/934398. +// Flaky on Linux. See http://crbug.com/1423252 +#if BUILDFLAG(IS_LINUX) +#define MAYBE_Initiator_SplitIncognito DISABLED_Initiator_SplitIncognito +#else +#define MAYBE_Initiator_SplitIncognito Initiator_SplitIncognito +#endif IN_PROC_BROWSER_TEST_P(ExtensionWebRequestApiTestWithContextType, - Initiator_SplitIncognito) { + MAYBE_Initiator_SplitIncognito) { embedded_test_server()->ServeFilesFromSourceDirectory("chrome/test/data"); ASSERT_TRUE(embedded_test_server()->Start()); @@ -5602,6 +5609,7 @@ IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, WebRequestBlocking) { EXPECT_EQ(net::OK, nav_observer.last_net_error_code()); } + base::HistogramTester histogram_tester; // Now, navigate to block.example. This navigation should be blocked. { content::TestNavigationObserver nav_observer(web_contents); @@ -5610,6 +5618,16 @@ IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, WebRequestBlocking) { embedded_test_server()->GetURL("block.example", "/simple.html"))); EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, nav_observer.last_net_error_code()); } + + // TODO(crbug.com/1441221): Create more targeted tests to confirm when metrics + // should be firing or not. + // Web request API events should not have metrics emitted for SW/MV3. + histogram_tester.ExpectTotalCount( + "Extensions.Events.DispatchToAckTime.ExtensionServiceWorker2", + /*expected_count=*/0); + histogram_tester.ExpectTotalCount( + "Extensions.Events.DispatchToAckLongTime.ExtensionServiceWorker2", + /*expected_count=*/0); } // Tests a service worker-based extension registering multiple webRequest events @@ -6420,6 +6438,61 @@ IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, << errors[0]->message(); } +// Tests that an extension that doesn't have the `webView` permission cannot +// manually create and add a WebRequestEvent that specifies a webViewInstanceId. +// TODO(tjudkins): It would be good to also stop this on the JS layer by not +// allowing extensions to manually create and add WebRequestEvents. +// Regression test for crbug.com/1472830 +IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, + TestWebviewIdSpecifiedOnEvent_NoPermission) { + ASSERT_TRUE(StartEmbeddedTestServer()); + + static constexpr char kManifest[] = + R"({ + "name": "MV3 WebRequest", + "version": "0.1", + "manifest_version": 3, + "permissions": ["webRequest"], + "host_permissions": [ "http://example.com/*" ], + "background": {"service_worker": "background.js"} + })"; + // The extension tries to add a listener; this will fail asynchronously + // as a part of the webRequestInternal API trying to add the listener. + // This results in runtime.lastError being set, but since it's an + // internal API, there's no way for the extension to catch the error. + static constexpr char kBackgroundJs[] = + R"(let event = new chrome.webRequest.onBeforeRequest.constructor( + 'webRequest.onBeforeRequest', + undefined, + undefined, + undefined, + 1); // webViewInstanceId + event.addListener(() => {}, + {urls: ['*://*.example.com/*']});)"; + + // Since we can't catch the error in the extension's JS, we instead listen to + // the error come into the error console. + ErrorConsoleTestObserver error_observer(1u, profile()); + error_observer.EnableErrorCollection(); + + // Load the extension and wait for the error to come. + TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs); + const Extension* extension = LoadExtension(test_dir.UnpackedPath()); + + ASSERT_TRUE(extension); + error_observer.WaitForErrors(); + + const ErrorList& errors = + ErrorConsole::Get(profile())->GetErrorsForExtension(extension->id()); + ASSERT_EQ(1u, errors.size()); + EXPECT_EQ(u"Unchecked runtime.lastError: Missing webview permission.", + errors[0]->message()); + EXPECT_EQ(0u, web_request_router()->GetListenerCountForTesting( + profile(), "webRequest.onBeforeRequest")); +} + IN_PROC_BROWSER_TEST_F(ManifestV3WebRequestApiTest, RecordUkmOnNavigation) { ASSERT_TRUE(StartEmbeddedTestServer()); TestExtensionDir test_dir1; diff --git a/chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc b/chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc index 020d47ebc04..b4f4a634e88 100644 --- a/chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc +++ b/chromium/chrome/browser/extensions/api/web_view/chrome_web_view_internal_api.cc @@ -27,10 +27,9 @@ ChromeWebViewInternalContextMenusCreateFunction::Run() { MenuItem::Id id( Profile::FromBrowserContext(browser_context())->IsOffTheRecord(), - MenuItem::ExtensionKey( - extension_id(), - GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(), - params->instance_id)); + MenuItem::ExtensionKey(extension_id(), + render_frame_host()->GetProcess()->GetID(), + params->instance_id)); if (params->create_properties.id) { id.string_uid = *params->create_properties.id; @@ -62,10 +61,9 @@ ChromeWebViewInternalContextMenusUpdateFunction::Run() { Profile* profile = Profile::FromBrowserContext(browser_context()); MenuItem::Id item_id( profile->IsOffTheRecord(), - MenuItem::ExtensionKey( - extension_id(), - GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(), - params->instance_id)); + MenuItem::ExtensionKey(extension_id(), + render_frame_host()->GetProcess()->GetID(), + params->instance_id)); if (params->id.as_string) item_id.string_uid = *params->id.as_string; @@ -92,10 +90,9 @@ ChromeWebViewInternalContextMenusRemoveFunction::Run() { MenuItem::Id id( Profile::FromBrowserContext(browser_context())->IsOffTheRecord(), - MenuItem::ExtensionKey( - extension_id(), - GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(), - params->instance_id)); + MenuItem::ExtensionKey(extension_id(), + render_frame_host()->GetProcess()->GetID(), + params->instance_id)); if (params->menu_item_id.as_string) { id.string_uid = *params->menu_item_id.as_string; @@ -127,8 +124,7 @@ ChromeWebViewInternalContextMenusRemoveAllFunction::Run() { MenuManager* menu_manager = MenuManager::Get(Profile::FromBrowserContext(browser_context())); menu_manager->RemoveAllContextItems(MenuItem::ExtensionKey( - extension_id(), - GetSenderWebContents()->GetPrimaryMainFrame()->GetProcess()->GetID(), + extension_id(), render_frame_host()->GetProcess()->GetID(), params->instance_id)); return RespondNow(NoArguments()); diff --git a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc index ed1df85124e..dd65d77b192 100644 --- a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc +++ b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h" #include <memory> +#include <string> #include <utility> #include <vector> @@ -12,6 +13,8 @@ #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/media/webrtc/media_device_salt_service_factory.h" +#include "components/media_device_salt/media_device_salt_service.h" #include "content/public/browser/audio_service.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" @@ -23,6 +26,7 @@ #include "extensions/common/error_utils.h" #include "extensions/common/permissions/permissions_data.h" #include "media/audio/audio_system.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" #include "url/gurl.h" #include "url/origin.h" @@ -102,7 +106,12 @@ WebrtcAudioPrivateFunction::WebrtcAudioPrivateFunction() {} WebrtcAudioPrivateFunction::~WebrtcAudioPrivateFunction() {} +url::Origin WebrtcAudioPrivateFunction::GetExtensionOrigin() const { + return url::Origin::Create(source_url()); +} + std::string WebrtcAudioPrivateFunction::CalculateHMAC( + const std::string& extension_salt, const std::string& raw_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -114,18 +123,40 @@ std::string WebrtcAudioPrivateFunction::CalculateHMAC( if (media::AudioDeviceDescription::IsDefaultDevice(raw_id)) return media::AudioDeviceDescription::kDefaultDeviceId; - url::Origin security_origin = - url::Origin::Create(source_url().DeprecatedGetOriginAsURL()); - return content::GetHMACForMediaDeviceID(device_id_salt(), security_origin, + return content::GetHMACForMediaDeviceID(extension_salt, GetExtensionOrigin(), raw_id); } -void WebrtcAudioPrivateFunction::InitDeviceIDSalt() { - device_id_salt_ = browser_context()->GetMediaDeviceIDSalt(); +void WebrtcAudioPrivateFunction::GetSalt( + const url::Origin& origin, + base::OnceCallback<void(const std::string&)> salt_callback) { + media_device_salt::MediaDeviceSaltService* salt_service = + MediaDeviceSaltServiceFactory::GetInstance()->GetForBrowserContext( + browser_context()); + if (!salt_service) { + std::move(salt_callback).Run(browser_context()->UniqueId()); + return; + } + + salt_service->GetSalt(blink::StorageKey::CreateFirstParty(origin), + std::move(salt_callback)); +} + +void WebrtcAudioPrivateFunction::GetSaltAndDeviceDescriptions( + const url::Origin& origin, + bool is_input_devices, + SaltAndDeviceDescriptionsCallback callback) { + GetSalt(origin, base::BindOnce( + &WebrtcAudioPrivateFunction::GotSaltForDeviceDescriptions, + this, is_input_devices, std::move(callback))); } -std::string WebrtcAudioPrivateFunction::device_id_salt() const { - return device_id_salt_; +void WebrtcAudioPrivateFunction::GotSaltForDeviceDescriptions( + bool is_input_devices, + SaltAndDeviceDescriptionsCallback callback, + const std::string& device_id_salt) { + GetAudioSystem()->GetDeviceDescriptions( + is_input_devices, base::BindOnce(std::move(callback), device_id_salt)); } media::AudioSystem* WebrtcAudioPrivateFunction::GetAudioSystem() { @@ -137,9 +168,9 @@ media::AudioSystem* WebrtcAudioPrivateFunction::GetAudioSystem() { ExtensionFunction::ResponseAction WebrtcAudioPrivateGetSinksFunction::Run() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - InitDeviceIDSalt(); - GetAudioSystem()->GetDeviceDescriptions( - false, + GetSaltAndDeviceDescriptions( + GetExtensionOrigin(), + /*is_input_devices=*/false, base::BindOnce( &WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions, this)); @@ -147,12 +178,13 @@ ExtensionFunction::ResponseAction WebrtcAudioPrivateGetSinksFunction::Run() { } void WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions( + const std::string& extension_salt, media::AudioDeviceDescriptions sink_devices) { DCHECK_CURRENTLY_ON(BrowserThread::UI); auto results = std::make_unique<SinkInfoVector>(); for (const media::AudioDeviceDescription& description : sink_devices) { wap::SinkInfo info; - info.sink_id = CalculateHMAC(description.unique_id); + info.sink_id = CalculateHMAC(extension_salt, description.unique_id); info.sink_label = description.device_name; // TODO(joi): Add other parameters. results->push_back(std::move(info)); @@ -161,38 +193,37 @@ void WebrtcAudioPrivateGetSinksFunction::ReceiveOutputDeviceDescriptions( } WebrtcAudioPrivateGetAssociatedSinkFunction:: - WebrtcAudioPrivateGetAssociatedSinkFunction() {} + WebrtcAudioPrivateGetAssociatedSinkFunction() = default; WebrtcAudioPrivateGetAssociatedSinkFunction:: - ~WebrtcAudioPrivateGetAssociatedSinkFunction() {} + ~WebrtcAudioPrivateGetAssociatedSinkFunction() = default; ExtensionFunction::ResponseAction WebrtcAudioPrivateGetAssociatedSinkFunction::Run() { params_ = wap::GetAssociatedSink::Params::Create(args()); DCHECK_CURRENTLY_ON(BrowserThread::UI); EXTENSION_FUNCTION_VALIDATE(params_); - InitDeviceIDSalt(); - - GetAudioSystem()->GetDeviceDescriptions( - true, base::BindOnce(&WebrtcAudioPrivateGetAssociatedSinkFunction:: - ReceiveInputDeviceDescriptions, - this)); + url::Origin origin = url::Origin::Create(GURL(params_->security_origin)); + GetSaltAndDeviceDescriptions( + origin, /*is_input_devices=*/true, + base::BindOnce(&WebrtcAudioPrivateGetAssociatedSinkFunction:: + ReceiveInputDeviceDescriptions, + this, origin)); return RespondLater(); } void WebrtcAudioPrivateGetAssociatedSinkFunction:: ReceiveInputDeviceDescriptions( + const url::Origin& origin, + const std::string& salt, media::AudioDeviceDescriptions source_devices) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - url::Origin security_origin = - url::Origin::Create(GURL(params_->security_origin)); std::string source_id_in_origin(params_->source_id_in_origin); // Find the raw source ID for source_id_in_origin. std::string raw_source_id; for (const auto& device : source_devices) { - if (content::DoesMediaDeviceIDMatchHMAC(device_id_salt(), security_origin, - source_id_in_origin, + if (content::DoesMediaDeviceIDMatchHMAC(salt, origin, source_id_in_origin, device.unique_id)) { raw_source_id = device.unique_id; DVLOG(2) << "Found raw ID " << raw_source_id @@ -201,22 +232,32 @@ void WebrtcAudioPrivateGetAssociatedSinkFunction:: } } if (raw_source_id.empty()) { - CalculateHMACAndReply(absl::nullopt); + Reply(media::AudioDeviceDescription::kDefaultDeviceId); return; } + GetSalt(GetExtensionOrigin(), + base::BindOnce( + &WebrtcAudioPrivateGetAssociatedSinkFunction::GotExtensionSalt, + this, raw_source_id)); +} + +void WebrtcAudioPrivateGetAssociatedSinkFunction::GotExtensionSalt( + const std::string& raw_source_id, + const std::string& extension_salt) { GetAudioSystem()->GetAssociatedOutputDeviceID( raw_source_id, base::BindOnce( &WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACAndReply, - this)); + this, extension_salt)); } void WebrtcAudioPrivateGetAssociatedSinkFunction::CalculateHMACAndReply( + const std::string& extension_salt, const absl::optional<std::string>& raw_sink_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!raw_sink_id || !raw_sink_id->empty()); // If no |raw_sink_id| is provided, the default device is used. - Reply(CalculateHMAC(raw_sink_id.value_or(std::string()))); + Reply(CalculateHMAC(extension_salt, raw_sink_id.value_or(std::string()))); } void WebrtcAudioPrivateGetAssociatedSinkFunction::Reply( diff --git a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h index 708e02161d3..8d5d6c7c83e 100644 --- a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h +++ b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h @@ -52,31 +52,40 @@ class WebrtcAudioPrivateEventService // Common base for WebrtcAudioPrivate functions, that provides a // couple of optionally-used common implementations. class WebrtcAudioPrivateFunction : public ExtensionFunction { - protected: - WebrtcAudioPrivateFunction(); - + public: WebrtcAudioPrivateFunction(const WebrtcAudioPrivateFunction&) = delete; WebrtcAudioPrivateFunction& operator=(const WebrtcAudioPrivateFunction&) = delete; + protected: + WebrtcAudioPrivateFunction(); ~WebrtcAudioPrivateFunction() override; - protected: - // Calculates a single HMAC, using the extension ID as the security origin. - std::string CalculateHMAC(const std::string& raw_id); + using SaltAndDeviceDescriptionsCallback = + base::OnceCallback<void(const std::string&, + media::AudioDeviceDescriptions)>; + // Calculates the HMAC for `raw_id` using extension ID as the security origin. + // `extension_salt` must be the salt for the extension ID. + std::string CalculateHMAC(const std::string& extension_salt, + const std::string& raw_id); - // Initializes |device_id_salt_|. Must be called on the UI thread, - // before any calls to |device_id_salt()|. - void InitDeviceIDSalt(); + // Returns the extension ID as an origin. + url::Origin GetExtensionOrigin() const; - // Callable from any thread. Must previously have called - // |InitDeviceIDSalt()|. - std::string device_id_salt() const; + void GetSalt(const url::Origin&, + base::OnceCallback<void(const std::string&)> salt_callback); + + // Returns the device ID salt and device descriptions. + void GetSaltAndDeviceDescriptions(const url::Origin& security_origin, + bool is_input_devices, + SaltAndDeviceDescriptionsCallback callback); media::AudioSystem* GetAudioSystem(); private: - std::string device_id_salt_; + void GotSaltForDeviceDescriptions(bool is_input_devices, + SaltAndDeviceDescriptionsCallback callback, + const std::string& device_id_salt); std::unique_ptr<media::AudioSystem> audio_system_; }; @@ -95,6 +104,7 @@ class WebrtcAudioPrivateGetSinksFunction : public WebrtcAudioPrivateFunction { // Receives output device descriptions, calculates HMACs for them and sends // the response. void ReceiveOutputDeviceDescriptions( + const std::string& extension_salt, media::AudioDeviceDescriptions sink_devices); }; @@ -116,10 +126,16 @@ class WebrtcAudioPrivateGetAssociatedSinkFunction // Receives the input device descriptions, looks up the raw source device ID // basing on |params|, and requests the associated raw sink ID for it. void ReceiveInputDeviceDescriptions( + const url::Origin& origin, + const std::string& salt, media::AudioDeviceDescriptions source_devices); - // Receives the raw sink ID, calculates HMAC and calls Reply(). - void CalculateHMACAndReply(const absl::optional<std::string>& raw_sink_id); + void GotExtensionSalt(const std::string& raw_source_id, + const std::string& extension_salt); + + // Calculates HMAC and calls Reply(). + void CalculateHMACAndReply(const std::string& extension_salt, + const absl::optional<std::string>& raw_sink_id); // Receives the associated sink ID as HMAC and sends the response. void Reply(const std::string& hmac); diff --git a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc index f6c71b8346d..f3a5ab79336 100644 --- a/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc @@ -23,6 +23,7 @@ #include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/media/webrtc/media_device_salt_service_factory.h" #include "chrome/browser/media/webrtc/webrtc_log_uploader.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/recently_audible_helper.h" @@ -30,6 +31,7 @@ #include "chrome/common/buildflags.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/media_device_salt/media_device_salt_service.h" #include "components/network_session_configurator/common/network_switches.h" #include "content/public/browser/audio_service.h" #include "content/public/browser/browser_thread.h" @@ -49,6 +51,7 @@ #include "net/test/embedded_test_server/http_response.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" #if BUILDFLAG(IS_WIN) #include "base/win/windows_version.h" @@ -123,6 +126,16 @@ class WebrtcAudioPrivateTest : public AudioWaitingExtensionTest { return RunFunctionAndReturnSingleResult(function.get(), "[]", profile()); } + std::string GetMediaDeviceIDSalt(const url::Origin& origin) { + media_device_salt::MediaDeviceSaltService* salt_service = + MediaDeviceSaltServiceFactory::GetInstance()->GetForBrowserContext( + profile()); + base::test::TestFuture<const std::string&> future; + salt_service->GetSalt(blink::StorageKey::CreateFirstParty(origin), + future.GetCallback()); + return future.Get(); + } + GURL source_url_; }; @@ -152,13 +165,12 @@ IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, GetSinks) { const std::string* sink_id = dict.FindString("sinkId"); EXPECT_TRUE(sink_id); + url::Origin origin = url::Origin::Create(source_url_); std::string expected_id = media::AudioDeviceDescription::IsDefaultDevice(it->unique_id) ? media::AudioDeviceDescription::kDefaultDeviceId - : content::GetHMACForMediaDeviceID( - profile()->GetMediaDeviceIDSalt(), - url::Origin::Create(source_url_.DeprecatedGetOriginAsURL()), - it->unique_id); + : content::GetHMACForMediaDeviceID(GetMediaDeviceIDSalt(origin), + origin, it->unique_id); EXPECT_EQ(expected_id, *sink_id); const std::string* sink_label = dict.FindString("sinkLabel"); @@ -189,13 +201,13 @@ IN_PROC_BROWSER_TEST_F(WebrtcAudioPrivateTest, GetAssociatedSink) { std::string raw_device_id = device.unique_id; VLOG(2) << "Trying to find associated sink for device " << raw_device_id; - GURL origin(GURL("http://www.google.com/").DeprecatedGetOriginAsURL()); + GURL gurl("http://www.google.com/"); + url::Origin origin = url::Origin::Create(gurl); std::string source_id_in_origin = content::GetHMACForMediaDeviceID( - profile()->GetMediaDeviceIDSalt(), url::Origin::Create(origin), - raw_device_id); + GetMediaDeviceIDSalt(origin), origin, raw_device_id); base::Value::List parameters; - parameters.Append(origin.spec()); + parameters.Append(gurl.spec()); parameters.Append(source_id_in_origin); std::string parameter_string; JSONWriter::Write(parameters, ¶meter_string); diff --git a/chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc b/chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc index db80d6505a6..7db82d71476 100644 --- a/chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc +++ b/chromium/chrome/browser/extensions/api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc @@ -48,15 +48,16 @@ WebrtcDesktopCapturePrivateChooseDesktopMediaFunction::Run() { absl::optional<Params> params = Params::Create(args()); EXTENSION_FUNCTION_VALIDATE(params); - content::RenderFrameHost* rfh = content::RenderFrameHost::FromID( - params->request.guest_process_id, - params->request.guest_render_frame_id); + content::RenderFrameHost* render_frame_host = + content::RenderFrameHost::FromID(params->request.guest_process_id, + params->request.guest_render_frame_id); - if (!rfh) { + if (!render_frame_host) { return RespondNow(Error(kTargetNotFoundError)); } - GURL origin = rfh->GetLastCommittedURL().DeprecatedGetOriginAsURL(); + GURL origin = + render_frame_host->GetLastCommittedURL().DeprecatedGetOriginAsURL(); if (!base::CommandLine::ForCurrentProcess()->HasSwitch( ::switches::kAllowHttpScreenCapture) && !network::IsUrlPotentiallyTrustworthy(origin)) { @@ -74,8 +75,8 @@ WebrtcDesktopCapturePrivateChooseDesktopMediaFunction::Run() { // suppressLocalAudioPlaybackIntended here. return Execute(*sources, /*exclude_system_audio=*/false, /*exclude_self_browser_surface=*/false, - /*suppress_local_audio_playback_intended=*/false, rfh, origin, - target_name); + /*suppress_local_audio_playback_intended=*/false, + render_frame_host, origin, target_name); } WebrtcDesktopCapturePrivateCancelChooseDesktopMediaFunction:: diff --git a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc index 34fbd329f70..05b58430520 100644 --- a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc @@ -995,7 +995,7 @@ class WebrtcLoggingPrivateApiStartEventLoggingTestInIncognitoMode bool WebRtcEventLogCollectionPolicy() const override { return true; } private: - raw_ptr<Browser, DanglingUntriaged> browser_{ + raw_ptr<Browser, AcrossTasksDanglingUntriaged> browser_{ nullptr}; // Does not own the object. }; diff --git a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc index 4da2a1cf861..6431da933a5 100644 --- a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc +++ b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc @@ -69,8 +69,6 @@ #include "url/gurl.h" #if BUILDFLAG(ENABLE_SUPERVISED_USERS) -// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build -// flag to #if BUILDFLAG(IS_CHROMEOS) #include "chrome/browser/supervised_user/supervised_user_browser_utils.h" #include "chrome/browser/supervised_user/supervised_user_service_factory.h" #include "components/supervised_user/core/browser/supervised_user_service.h" @@ -223,17 +221,9 @@ const char kEphemeralAppLaunchingNotSupported[] = "Ephemeral launching of apps is no longer supported."; #if BUILDFLAG(ENABLE_SUPERVISED_USERS) -// Note that the following error doesn't mean an incorrect password was entered, -// nor that the parent permisison request was canceled by the user, but rather -// that the Parent permission request after credential entry and acceptance -// failed due to either a network connection error or some unsatisfied invariant -// that prevented the request from completing. -const char kWebstoreParentPermissionFailedError[] = - "Parent permission request failed"; - const char kParentBlockedExtensionInstallError[] = "Parent has blocked extension/app installation"; -#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) +#endif // The number of user gestures to trace back for the referrer chain. const int kExtensionReferrerUserGestureLimit = 2; @@ -658,7 +648,9 @@ void WebstorePrivateBeginInstallWithManifest3Function:: OnExtensionApprovalCanceled() { if (test_delegate) { test_delegate->OnExtensionInstallFailure( - dummy_extension_->id(), kWebstoreParentPermissionFailedError, + dummy_extension_->id(), + l10n_util::GetStringUTF8( + IDS_EXTENSIONS_SUPERVISED_USER_PARENTAL_PERMISSION_FAILURE), WebstoreInstaller::FailureReason::FAILURE_REASON_CANCELLED); } @@ -669,12 +661,16 @@ void WebstorePrivateBeginInstallWithManifest3Function:: OnExtensionApprovalFailed() { if (test_delegate) { test_delegate->OnExtensionInstallFailure( - dummy_extension_->id(), kWebstoreParentPermissionFailedError, + dummy_extension_->id(), + l10n_util::GetStringUTF8( + IDS_EXTENSIONS_SUPERVISED_USER_PARENTAL_PERMISSION_FAILURE), WebstoreInstaller::FailureReason::FAILURE_REASON_OTHER); } - Respond(BuildResponse(api::webstore_private::RESULT_UNKNOWN_ERROR, - kWebstoreParentPermissionFailedError)); + Respond(BuildResponse( + api::webstore_private::RESULT_UNKNOWN_ERROR, + l10n_util::GetStringUTF8( + IDS_EXTENSIONS_SUPERVISED_USER_PARENTAL_PERMISSION_FAILURE))); } void WebstorePrivateBeginInstallWithManifest3Function:: @@ -856,12 +852,23 @@ WebstorePrivateBeginInstallWithManifest3Function::BuildResponse( api::webstore_private::Result result, const std::string& error) { if (result != api::webstore_private::RESULT_SUCCESS) { + // TODO(tjudkins): We should not be using ErrorWithArguments here as it + // doesn't play well with promise based API calls (only emitting the error + // and dropping the arguments). In almost every case the error directly + // responds with the result enum value returned, so instead we should drop + // the error and have the caller just base logic on the enum value alone. + // In the cases where they do not correspond we should add a new enum value. + // We will need to ensure that the Webstore is entirely basing its logic on + // the result alone before removing the error. return ErrorWithArguments( BeginInstallWithManifest3::Results::Create(result), error); } - // The web store expects an empty string on success, so don't use + // The old Webstore expects an empty string on success, so don't use // RESULT_SUCCESS here. + // TODO(crbug.com/709120): The new Webstore accepts either the empty string or + // RESULT_SUCCESS on success now, so once the old Webstore is turned down this + // can be changed over. return ArgumentList(BeginInstallWithManifest3::Results::Create( api::webstore_private::RESULT_EMPTY_STRING)); } @@ -1264,11 +1271,11 @@ WebstorePrivateGetReferrerChainFunction::Run() { return RespondNow(ArgumentList( api::webstore_private::GetReferrerChain::Results::Create(""))); - content::RenderFrameHost* rfh = render_frame_host(); - content::RenderFrameHost* outermost_rfh = - rfh ? rfh->GetOutermostMainFrame() : nullptr; + content::RenderFrameHost* outermost_render_frame_host = + render_frame_host() ? render_frame_host()->GetOutermostMainFrame() + : nullptr; - if (!outermost_rfh) { + if (!outermost_render_frame_host) { return RespondNow(ErrorWithArguments( api::webstore_private::GetReferrerChain::Results::Create(""), kWebstoreUserCancelledError)); @@ -1281,7 +1288,8 @@ WebstorePrivateGetReferrerChainFunction::Run() { safe_browsing::ReferrerChain referrer_chain; SafeBrowsingNavigationObserverManager::AttributionResult result = navigation_observer_manager->IdentifyReferrerChainByRenderFrameHost( - outermost_rfh, kExtensionReferrerUserGestureLimit, &referrer_chain); + outermost_render_frame_host, kExtensionReferrerUserGestureLimit, + &referrer_chain); // If the referrer chain is incomplete we'll append the most recent // navigations to referrer chain for diagnostic purposes. This only happens if diff --git a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h index 957ab11765a..3626267125e 100644 --- a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h +++ b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_api.h @@ -26,8 +26,6 @@ #include "third_party/skia/include/core/SkBitmap.h" #if BUILDFLAG(ENABLE_SUPERVISED_USERS) -// TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build -// flag to #if BUILDFLAG(IS_CHROMEOS) #include "chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h" #include "extensions/browser/supervised_user_extensions_delegate.h" #endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) |